mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 20:22:53 -06:00
Merge pull request #9630 from netbox-community/9092-ObjectChildrenView
Closes #9092: Introduce support for use of ObjectChildrenView by plugins
This commit is contained in:
commit
2245f1bf41
@ -51,15 +51,16 @@ This makes our view accessible at the URL `/plugins/animal-sounds/random/`. (Rem
|
|||||||
|
|
||||||
NetBox provides several generic view classes (documented below) to facilitate common operations, such as creating, viewing, modifying, and deleting objects. Plugins can subclass these views for their own use.
|
NetBox provides several generic view classes (documented below) to facilitate common operations, such as creating, viewing, modifying, and deleting objects. Plugins can subclass these views for their own use.
|
||||||
|
|
||||||
| View Class | Description |
|
| View Class | Description |
|
||||||
|--------------------|--------------------------------|
|
|----------------------|--------------------------------------------------------|
|
||||||
| `ObjectView` | View a single object |
|
| `ObjectView` | View a single object |
|
||||||
| `ObjectEditView` | Create or edit a single object |
|
| `ObjectEditView` | Create or edit a single object |
|
||||||
| `ObjectDeleteView` | Delete a single object |
|
| `ObjectDeleteView` | Delete a single object |
|
||||||
| `ObjectListView` | View a list of objects |
|
| `ObjectChildrenView` | A list of child objects within the context of a parent |
|
||||||
| `BulkImportView` | Import a set of new objects |
|
| `ObjectListView` | View a list of objects |
|
||||||
| `BulkEditView` | Edit multiple objects |
|
| `BulkImportView` | Import a set of new objects |
|
||||||
| `BulkDeleteView` | Delete multiple objects |
|
| `BulkEditView` | Edit multiple objects |
|
||||||
|
| `BulkDeleteView` | Delete multiple objects |
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
Please note that only the classes which appear in this documentation are currently supported. Although other classes may be present within the `views.generic` module, they are not yet supported for use by plugins.
|
Please note that only the classes which appear in this documentation are currently supported. Although other classes may be present within the `views.generic` module, they are not yet supported for use by plugins.
|
||||||
@ -99,6 +100,12 @@ Below are the class definitions for NetBox's object views. These views handle CR
|
|||||||
members:
|
members:
|
||||||
- get_object
|
- get_object
|
||||||
|
|
||||||
|
::: netbox.views.generic.ObjectChildrenView
|
||||||
|
selection:
|
||||||
|
members:
|
||||||
|
- get_children
|
||||||
|
- prep_table_data
|
||||||
|
|
||||||
## Multi-Object Views
|
## Multi-Object Views
|
||||||
|
|
||||||
Below are the class definitions for NetBox's multi-object views. These views handle simultaneous actions for sets objects. The list, import, edit, and delete views each inherit from `BaseMultiObjectView`, which is not intended to be used directly.
|
Below are the class definitions for NetBox's multi-object views. These views handle simultaneous actions for sets objects. The list, import, edit, and delete views each inherit from `BaseMultiObjectView`, which is not intended to be used directly.
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
### Plugins API
|
### Plugins API
|
||||||
|
|
||||||
|
* [#9092](https://github.com/netbox-community/netbox/issues/9092) - Add support for `ObjectChildrenView` generic view
|
||||||
* [#9414](https://github.com/netbox-community/netbox/issues/9414) - Add `clone()` method to NetBoxModel for copying instance attributes
|
* [#9414](https://github.com/netbox-community/netbox/issues/9414) - Add `clone()` method to NetBoxModel for copying instance attributes
|
||||||
|
|
||||||
### Other Changes
|
### Other Changes
|
||||||
|
@ -24,6 +24,7 @@ from utilities.htmx import is_htmx
|
|||||||
from utilities.permissions import get_permission_for_model
|
from utilities.permissions import get_permission_for_model
|
||||||
from utilities.views import GetReturnURLMixin
|
from utilities.views import GetReturnURLMixin
|
||||||
from .base import BaseMultiObjectView
|
from .base import BaseMultiObjectView
|
||||||
|
from .mixins import ActionsMixin, TableMixin
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'BulkComponentCreateView',
|
'BulkComponentCreateView',
|
||||||
@ -36,9 +37,9 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ObjectListView(BaseMultiObjectView):
|
class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
||||||
"""
|
"""
|
||||||
Display multiple objects, all of the same type, as a table.
|
Display multiple objects, all the same type, as a table.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
filterset: A django-filter FilterSet that is applied to the queryset
|
filterset: A django-filter FilterSet that is applied to the queryset
|
||||||
@ -50,31 +51,10 @@ class ObjectListView(BaseMultiObjectView):
|
|||||||
template_name = 'generic/object_list.html'
|
template_name = 'generic/object_list.html'
|
||||||
filterset = None
|
filterset = None
|
||||||
filterset_form = None
|
filterset_form = None
|
||||||
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete')
|
|
||||||
action_perms = defaultdict(set, **{
|
|
||||||
'add': {'add'},
|
|
||||||
'import': {'add'},
|
|
||||||
'bulk_edit': {'change'},
|
|
||||||
'bulk_delete': {'delete'},
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_required_permission(self):
|
def get_required_permission(self):
|
||||||
return get_permission_for_model(self.queryset.model, 'view')
|
return get_permission_for_model(self.queryset.model, 'view')
|
||||||
|
|
||||||
def get_table(self, request, bulk_actions=True):
|
|
||||||
"""
|
|
||||||
Return the django-tables2 Table instance to be used for rendering the objects list.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request: The current request
|
|
||||||
bulk_actions: Show checkboxes for object selection
|
|
||||||
"""
|
|
||||||
table = self.table(self.queryset, user=request.user)
|
|
||||||
if 'pk' in table.base_columns and bulk_actions:
|
|
||||||
table.columns.show('pk')
|
|
||||||
|
|
||||||
return table
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Export methods
|
# Export methods
|
||||||
#
|
#
|
||||||
@ -147,19 +127,14 @@ class ObjectListView(BaseMultiObjectView):
|
|||||||
self.queryset = self.filterset(request.GET, self.queryset).qs
|
self.queryset = self.filterset(request.GET, self.queryset).qs
|
||||||
|
|
||||||
# Determine the available actions
|
# Determine the available actions
|
||||||
actions = []
|
actions = self.get_permitted_actions(request.user)
|
||||||
for action in self.actions:
|
|
||||||
if request.user.has_perms([
|
|
||||||
get_permission_for_model(model, name) for name in self.action_perms[action]
|
|
||||||
]):
|
|
||||||
actions.append(action)
|
|
||||||
has_bulk_actions = any([a.startswith('bulk_') for a in actions])
|
has_bulk_actions = any([a.startswith('bulk_') for a in actions])
|
||||||
|
|
||||||
if 'export' in request.GET:
|
if 'export' in request.GET:
|
||||||
|
|
||||||
# Export the current table view
|
# Export the current table view
|
||||||
if request.GET['export'] == 'table':
|
if request.GET['export'] == 'table':
|
||||||
table = self.get_table(request, has_bulk_actions)
|
table = self.get_table(self.queryset, request, has_bulk_actions)
|
||||||
columns = [name for name, _ in table.selected_columns]
|
columns = [name for name, _ in table.selected_columns]
|
||||||
return self.export_table(table, columns)
|
return self.export_table(table, columns)
|
||||||
|
|
||||||
@ -177,12 +152,11 @@ class ObjectListView(BaseMultiObjectView):
|
|||||||
|
|
||||||
# Fall back to default table/YAML export
|
# Fall back to default table/YAML export
|
||||||
else:
|
else:
|
||||||
table = self.get_table(request, has_bulk_actions)
|
table = self.get_table(self.queryset, request, has_bulk_actions)
|
||||||
return self.export_table(table)
|
return self.export_table(table)
|
||||||
|
|
||||||
# Render the objects table
|
# Render the objects table
|
||||||
table = self.get_table(request, has_bulk_actions)
|
table = self.get_table(self.queryset, request, has_bulk_actions)
|
||||||
table.configure(request)
|
|
||||||
|
|
||||||
# If this is an HTMX request, return only the rendered table HTML
|
# If this is an HTMX request, return only the rendered table HTML
|
||||||
if is_htmx(request):
|
if is_htmx(request):
|
||||||
@ -190,15 +164,13 @@ class ObjectListView(BaseMultiObjectView):
|
|||||||
'table': table,
|
'table': table,
|
||||||
})
|
})
|
||||||
|
|
||||||
context = {
|
return render(request, self.template_name, {
|
||||||
'model': model,
|
'model': model,
|
||||||
'table': table,
|
'table': table,
|
||||||
'actions': actions,
|
'actions': actions,
|
||||||
'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
|
'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
|
||||||
**self.get_extra_context(request),
|
**self.get_extra_context(request),
|
||||||
}
|
})
|
||||||
|
|
||||||
return render(request, self.template_name, context)
|
|
||||||
|
|
||||||
|
|
||||||
class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView):
|
class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView):
|
||||||
|
47
netbox/netbox/views/generic/mixins.py
Normal file
47
netbox/netbox/views/generic/mixins.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from utilities.permissions import get_permission_for_model
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'TableMixin',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ActionsMixin:
|
||||||
|
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete')
|
||||||
|
action_perms = defaultdict(set, **{
|
||||||
|
'add': {'add'},
|
||||||
|
'import': {'add'},
|
||||||
|
'bulk_edit': {'change'},
|
||||||
|
'bulk_delete': {'delete'},
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_permitted_actions(self, user, model=None):
|
||||||
|
"""
|
||||||
|
Return a tuple of actions for which the given user is permitted to do.
|
||||||
|
"""
|
||||||
|
model = model or self.queryset.model
|
||||||
|
return [
|
||||||
|
action for action in self.actions if user.has_perms([
|
||||||
|
get_permission_for_model(model, name) for name in self.action_perms[action]
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TableMixin:
|
||||||
|
|
||||||
|
def get_table(self, data, request, bulk_actions=True):
|
||||||
|
"""
|
||||||
|
Return the django-tables2 Table instance to be used for rendering the objects list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Queryset or iterable containing table data
|
||||||
|
request: The current request
|
||||||
|
bulk_actions: Render checkboxes for object selection
|
||||||
|
"""
|
||||||
|
table = self.table(data, user=request.user)
|
||||||
|
if 'pk' in table.base_columns and bulk_actions:
|
||||||
|
table.columns.show('pk')
|
||||||
|
table.configure(request)
|
||||||
|
|
||||||
|
return table
|
@ -20,6 +20,7 @@ from utilities.permissions import get_permission_for_model
|
|||||||
from utilities.utils import get_viewname, normalize_querydict, prepare_cloned_fields
|
from utilities.utils import get_viewname, normalize_querydict, prepare_cloned_fields
|
||||||
from utilities.views import GetReturnURLMixin
|
from utilities.views import GetReturnURLMixin
|
||||||
from .base import BaseObjectView
|
from .base import BaseObjectView
|
||||||
|
from .mixins import ActionsMixin, TableMixin
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ComponentCreateView',
|
'ComponentCreateView',
|
||||||
@ -69,12 +70,17 @@ class ObjectView(BaseObjectView):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class ObjectChildrenView(ObjectView):
|
class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin):
|
||||||
"""
|
"""
|
||||||
Display a table of child objects associated with the parent object.
|
Display a table of child objects associated with the parent object.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
table: Table class used to render child objects list
|
child_model: The model class which represents the child objects
|
||||||
|
table: The django-tables2 Table class used to render the child objects list
|
||||||
|
filterset: A django-filter FilterSet that is applied to the queryset
|
||||||
|
actions: Supported actions for the model. When adding custom actions, bulk action names must
|
||||||
|
be prefixed with `bulk_`. Default actions: add, import, export, bulk_edit, bulk_delete
|
||||||
|
action_perms: A dictionary mapping supported actions to a set of permissions required for each
|
||||||
"""
|
"""
|
||||||
child_model = None
|
child_model = None
|
||||||
table = None
|
table = None
|
||||||
@ -84,8 +90,9 @@ class ObjectChildrenView(ObjectView):
|
|||||||
"""
|
"""
|
||||||
Return a QuerySet of child objects.
|
Return a QuerySet of child objects.
|
||||||
|
|
||||||
request: The current request
|
Args:
|
||||||
parent: The parent object
|
request: The current request
|
||||||
|
parent: The parent object
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(f'{self.__class__.__name__} must implement get_children()')
|
raise NotImplementedError(f'{self.__class__.__name__} must implement get_children()')
|
||||||
|
|
||||||
@ -114,16 +121,11 @@ class ObjectChildrenView(ObjectView):
|
|||||||
if self.filterset:
|
if self.filterset:
|
||||||
child_objects = self.filterset(request.GET, child_objects).qs
|
child_objects = self.filterset(request.GET, child_objects).qs
|
||||||
|
|
||||||
permissions = {}
|
# Determine the available actions
|
||||||
for action in ('change', 'delete'):
|
actions = self.get_permitted_actions(request.user, model=self.child_model)
|
||||||
perm_name = get_permission_for_model(self.child_model, action)
|
|
||||||
permissions[action] = request.user.has_perm(perm_name)
|
|
||||||
|
|
||||||
table = self.table(self.prep_table_data(request, child_objects, instance), user=request.user)
|
table_data = self.prep_table_data(request, child_objects, instance)
|
||||||
# Determine whether to display bulk action checkboxes
|
table = self.get_table(table_data, request, bool(actions))
|
||||||
if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']):
|
|
||||||
table.columns.show('pk')
|
|
||||||
table.configure(request)
|
|
||||||
|
|
||||||
# If this is an HTMX request, return only the rendered table HTML
|
# If this is an HTMX request, return only the rendered table HTML
|
||||||
if is_htmx(request):
|
if is_htmx(request):
|
||||||
@ -134,8 +136,9 @@ class ObjectChildrenView(ObjectView):
|
|||||||
|
|
||||||
return render(request, self.get_template_name(), {
|
return render(request, self.get_template_name(), {
|
||||||
'object': instance,
|
'object': instance,
|
||||||
|
'child_model': self.child_model,
|
||||||
'table': table,
|
'table': table,
|
||||||
'permissions': permissions,
|
'actions': actions,
|
||||||
**self.get_extra_context(request, instance),
|
**self.get_extra_context(request, instance),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_consoleport %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_consoleport %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_consoleserverport %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_consoleserverport %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_devicebay %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_devicebay %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-outline-danger btn-sm">
|
<button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-outline-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete selected
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete selected
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_frontport %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={% url 'dcim:device_frontports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={% url 'dcim:device_frontports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_frontport %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' %}?return_url={% url 'dcim:device_frontports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' %}?return_url={% url 'dcim:device_frontports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_interface %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={% url 'dcim:device_interfaces' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={% url 'dcim:device_interfaces' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -64,7 +64,7 @@
|
|||||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_interface %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={% url 'dcim:device_interfaces' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={% url 'dcim:device_interfaces' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_inventoryitem %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:inventoryitem_bulk_rename' %}?return_url={% url 'dcim:device_inventory' pk=object.pk %}" class="btn btn-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:inventoryitem_bulk_rename' %}?return_url={% url 'dcim:device_inventory' pk=object.pk %}" class="btn btn-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_inventoryitem %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:inventoryitem_bulk_delete' %}?return_url={% url 'dcim:device_inventory' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'dcim:inventoryitem_bulk_delete' %}?return_url={% url 'dcim:device_inventory' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_modulebay %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:modulebay_bulk_rename' %}?return_url={% url 'dcim:device_modulebays' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:modulebay_bulk_rename' %}?return_url={% url 'dcim:device_modulebays' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_modulebay %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" formaction="{% url 'dcim:modulebay_bulk_delete' %}?return_url={% url 'dcim:device_modulebays' pk=object.pk %}" class="btn btn-outline-danger btn-sm">
|
<button type="submit" formaction="{% url 'dcim:modulebay_bulk_delete' %}?return_url={% url 'dcim:device_modulebays' pk=object.pk %}" class="btn btn-outline-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete selected
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete selected
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_powerport %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_poweroutlet %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_powerport %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:powerport_bulk_rename' %}?return_url={% url 'dcim:device_powerports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:powerport_bulk_rename' %}?return_url={% url 'dcim:device_powerports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_powerport %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:powerport_bulk_delete' %}?return_url={% url 'dcim:device_powerports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'dcim:powerport_bulk_delete' %}?return_url={% url 'dcim:device_powerports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.dcim.change_rearport %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={% url 'dcim:device_rearports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={% url 'dcim:device_rearports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
</button>
|
</button>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.delete_rearport %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' %}?return_url={% url 'dcim:device_rearports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' %}?return_url={% url 'dcim:device_rearports' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -25,12 +25,12 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.ipam.change_prefix %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'ipam:prefix_bulk_edit' %}?return_url={% url 'ipam:aggregate_prefixes' pk=object.pk %}" class="btn btn-warning btn-sm">
|
<button type="submit" name="_edit" formaction="{% url 'ipam:prefix_bulk_edit' %}?return_url={% url 'ipam:aggregate_prefixes' pk=object.pk %}" class="btn btn-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.delete_prefix %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'ipam:prefix_bulk_delete' %}?return_url={% url 'ipam:aggregate_prefixes' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'ipam:prefix_bulk_delete' %}?return_url={% url 'ipam:aggregate_prefixes' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.ipam.change_ipaddress %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'ipam:ipaddress_bulk_edit' %}?return_url={% url 'ipam:iprange_ipaddresses' pk=object.pk %}" class="btn btn-warning btn-sm">
|
<button type="submit" name="_edit" formaction="{% url 'ipam:ipaddress_bulk_edit' %}?return_url={% url 'ipam:iprange_ipaddresses' pk=object.pk %}" class="btn btn-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.delete_ipaddress %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'ipam:ipaddress_bulk_delete' %}?return_url={% url 'ipam:iprange_ipaddresses' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'ipam:ipaddress_bulk_delete' %}?return_url={% url 'ipam:iprange_ipaddresses' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.ipam.change_ipaddress %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'ipam:ipaddress_bulk_edit' %}?return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-warning btn-sm">
|
<button type="submit" name="_edit" formaction="{% url 'ipam:ipaddress_bulk_edit' %}?return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.delete_ipaddress %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'ipam:ipaddress_bulk_delete' %}?return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'ipam:ipaddress_bulk_delete' %}?return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.ipam.change_iprange %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'ipam:iprange_bulk_edit' %}?return_url={% url 'ipam:prefix_ipranges' pk=object.pk %}" class="btn btn-warning btn-sm">
|
<button type="submit" name="_edit" formaction="{% url 'ipam:iprange_bulk_edit' %}?return_url={% url 'ipam:prefix_ipranges' pk=object.pk %}" class="btn btn-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.delete_iprange %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'ipam:iprange_bulk_delete' %}?return_url={% url 'ipam:prefix_ipranges' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'ipam:iprange_bulk_delete' %}?return_url={% url 'ipam:prefix_ipranges' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -25,12 +25,12 @@
|
|||||||
|
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.ipam.change_prefix %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'ipam:prefix_bulk_edit' %}?return_url={% url 'ipam:prefix_prefixes' pk=object.pk %}" class="btn btn-warning btn-sm">
|
<button type="submit" name="_edit" formaction="{% url 'ipam:prefix_bulk_edit' %}?return_url={% url 'ipam:prefix_prefixes' pk=object.pk %}" class="btn btn-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.delete_prefix %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'ipam:prefix_bulk_delete' %}?return_url={% url 'ipam:prefix_prefixes' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'ipam:prefix_bulk_delete' %}?return_url={% url 'ipam:prefix_prefixes' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -14,12 +14,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="noprint bulk-buttons">
|
<div class="noprint bulk-buttons">
|
||||||
<div class="bulk-button-group">
|
<div class="bulk-button-group">
|
||||||
{% if perms.virtualization.change_virtualmachine %}
|
{% if 'bulk_edit' in actions %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'virtualization:virtualmachine_bulk_edit' %}?return_url={% url 'virtualization:cluster_virtualmachines' pk=object.pk %}" class="btn btn-warning btn-sm">
|
<button type="submit" name="_edit" formaction="{% url 'virtualization:virtualmachine_bulk_edit' %}?return_url={% url 'virtualization:cluster_virtualmachines' pk=object.pk %}" class="btn btn-warning btn-sm">
|
||||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.virtualization.delete_virtualmachine %}
|
{% if 'bulk_delete' in actions %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'virtualization:virtualmachine_bulk_delete' %}?return_url={% url 'virtualization:cluster_virtualmachines' pk=object.pk %}" class="btn btn-danger btn-sm">
|
<button type="submit" name="_delete" formaction="{% url 'virtualization:virtualmachine_bulk_delete' %}?return_url={% url 'virtualization:cluster_virtualmachines' pk=object.pk %}" class="btn btn-danger btn-sm">
|
||||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user