mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-13 16:47:34 -06:00
* Initial work on #19735 * Work in progress * Remove ClusterRemoveDevicesView (anti-pattern) * Misc cleanup * Fix has_bulk_actions * Fix has_bulk_actions for ObjectChildrenView * Restore clone button * Misc cleanup * Clean up custom bulk actions * Rename individual object actions * Collapse into a single template tag * Fix support for legacy action dicts * Rename bulk attr to multi * clone_button tag should fail silently if view name is invalid * Clean up action buttons * Fix export button label * Replace clone_button with an ObjectAction * Create object actions for adding device/VM components * Move core_sync.html to core app * Remove extra_bulk_buttons from template doc
This commit is contained in:
parent
71e6ea5785
commit
601a77ac73
18
netbox/core/object_actions.py
Normal file
18
netbox/core/object_actions.py
Normal file
@ -0,0 +1,18 @@
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from netbox.object_actions import ObjectAction
|
||||
|
||||
__all__ = (
|
||||
'BulkSync',
|
||||
)
|
||||
|
||||
|
||||
class BulkSync(ObjectAction):
|
||||
"""
|
||||
Synchronize multiple objects at once.
|
||||
"""
|
||||
name = 'bulk_sync'
|
||||
label = _('Sync Data')
|
||||
multi = True
|
||||
permissions_required = {'sync'}
|
||||
template_name = 'core/buttons/bulk_sync.html'
|
@ -22,6 +22,7 @@ from rq.worker_registration import clean_worker_registry
|
||||
|
||||
from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs_from_status, requeue_rq_job, stop_rq_job
|
||||
from netbox.config import get_config, PARAMS
|
||||
from netbox.object_actions import AddObject, BulkDelete, BulkExport, DeleteObject
|
||||
from netbox.registry import registry
|
||||
from netbox.views import generic
|
||||
from netbox.views.generic.base import BaseObjectView
|
||||
@ -138,14 +139,13 @@ class DataFileListView(generic.ObjectListView):
|
||||
filterset = filtersets.DataFileFilterSet
|
||||
filterset_form = forms.DataFileFilterForm
|
||||
table = tables.DataFileTable
|
||||
actions = {
|
||||
'bulk_delete': {'delete'},
|
||||
}
|
||||
actions = (BulkDelete,)
|
||||
|
||||
|
||||
@register_model_view(DataFile)
|
||||
class DataFileView(generic.ObjectView):
|
||||
queryset = DataFile.objects.all()
|
||||
actions = (DeleteObject,)
|
||||
|
||||
|
||||
@register_model_view(DataFile, 'delete')
|
||||
@ -170,15 +170,13 @@ class JobListView(generic.ObjectListView):
|
||||
filterset = filtersets.JobFilterSet
|
||||
filterset_form = forms.JobFilterForm
|
||||
table = tables.JobTable
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
'bulk_delete': {'delete'},
|
||||
}
|
||||
actions = (BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(Job)
|
||||
class JobView(generic.ObjectView):
|
||||
queryset = Job.objects.all()
|
||||
actions = (DeleteObject,)
|
||||
|
||||
|
||||
@register_model_view(Job, 'delete')
|
||||
@ -204,9 +202,7 @@ class ObjectChangeListView(generic.ObjectListView):
|
||||
filterset_form = forms.ObjectChangeFilterForm
|
||||
table = tables.ObjectChangeTable
|
||||
template_name = 'core/objectchange_list.html'
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
}
|
||||
actions = (BulkExport,)
|
||||
|
||||
|
||||
@register_model_view(ObjectChange)
|
||||
@ -274,6 +270,7 @@ class ConfigRevisionListView(generic.ObjectListView):
|
||||
filterset = filtersets.ConfigRevisionFilterSet
|
||||
filterset_form = forms.ConfigRevisionFilterForm
|
||||
table = tables.ConfigRevisionTable
|
||||
actions = (AddObject, BulkExport)
|
||||
|
||||
|
||||
@register_model_view(ConfigRevision)
|
||||
|
38
netbox/dcim/object_actions.py
Normal file
38
netbox/dcim/object_actions.py
Normal file
@ -0,0 +1,38 @@
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from netbox.object_actions import ObjectAction
|
||||
|
||||
__all__ = (
|
||||
'BulkAddComponents',
|
||||
'BulkDisconnect',
|
||||
)
|
||||
|
||||
|
||||
class BulkAddComponents(ObjectAction):
|
||||
"""
|
||||
Add components to the selected devices.
|
||||
"""
|
||||
label = _('Add Components')
|
||||
multi = True
|
||||
permissions_required = {'change'}
|
||||
template_name = 'dcim/buttons/bulk_add_components.html'
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, context, obj):
|
||||
return {
|
||||
'perms': context.get('perms'),
|
||||
'request': context.get('request'),
|
||||
'formaction': context.get('formaction'),
|
||||
'label': cls.label,
|
||||
}
|
||||
|
||||
|
||||
class BulkDisconnect(ObjectAction):
|
||||
"""
|
||||
Disconnect each of a set of objects to which a cable is connected.
|
||||
"""
|
||||
name = 'bulk_disconnect'
|
||||
label = _('Disconnect Selected')
|
||||
multi = True
|
||||
permissions_required = {'change'}
|
||||
template_name = 'dcim/buttons/bulk_disconnect.html'
|
@ -15,7 +15,7 @@ from circuits.models import Circuit, CircuitTermination
|
||||
from extras.views import ObjectConfigContextView, ObjectRenderConfigView
|
||||
from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN
|
||||
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
||||
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||
from netbox.object_actions import *
|
||||
from netbox.views import generic
|
||||
from utilities.forms import ConfirmationForm
|
||||
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||
@ -34,6 +34,7 @@ from wireless.models import WirelessLAN
|
||||
from . import filtersets, forms, tables
|
||||
from .choices import DeviceFaceChoices, InterfaceModeChoices
|
||||
from .models import *
|
||||
from .object_actions import BulkAddComponents, BulkDisconnect
|
||||
|
||||
CABLE_TERMINATION_TYPES = {
|
||||
'dcim.consoleport': ConsolePort,
|
||||
@ -49,11 +50,6 @@ CABLE_TERMINATION_TYPES = {
|
||||
|
||||
|
||||
class DeviceComponentsView(generic.ObjectChildrenView):
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
'bulk_disconnect': {'change'},
|
||||
}
|
||||
queryset = Device.objects.all()
|
||||
|
||||
def get_children(self, request, parent):
|
||||
@ -61,12 +57,8 @@ class DeviceComponentsView(generic.ObjectChildrenView):
|
||||
|
||||
|
||||
class DeviceTypeComponentsView(generic.ObjectChildrenView):
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
|
||||
queryset = DeviceType.objects.all()
|
||||
template_name = 'dcim/devicetype/component_templates.html'
|
||||
viewname = None # Used for return_url resolution
|
||||
|
||||
def get_children(self, request, parent):
|
||||
@ -78,9 +70,9 @@ class DeviceTypeComponentsView(generic.ObjectChildrenView):
|
||||
}
|
||||
|
||||
|
||||
class ModuleTypeComponentsView(DeviceComponentsView):
|
||||
class ModuleTypeComponentsView(generic.ObjectChildrenView):
|
||||
queryset = ModuleType.objects.all()
|
||||
template_name = 'dcim/moduletype/component_templates.html'
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
|
||||
viewname = None # Used for return_url resolution
|
||||
|
||||
def get_children(self, request, parent):
|
||||
@ -2116,7 +2108,7 @@ class DeviceListView(generic.ObjectListView):
|
||||
filterset = filtersets.DeviceFilterSet
|
||||
filterset_form = forms.DeviceFilterForm
|
||||
table = tables.DeviceTable
|
||||
template_name = 'dcim/device_list.html'
|
||||
actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkRename, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(Device)
|
||||
@ -2157,7 +2149,7 @@ class DeviceConsolePortsView(DeviceComponentsView):
|
||||
table = tables.DeviceConsolePortTable
|
||||
filterset = filtersets.ConsolePortFilterSet
|
||||
filterset_form = forms.ConsolePortFilterForm
|
||||
template_name = 'dcim/device/consoleports.html',
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Console Ports'),
|
||||
badge=lambda obj: obj.console_port_count,
|
||||
@ -2173,7 +2165,7 @@ class DeviceConsoleServerPortsView(DeviceComponentsView):
|
||||
table = tables.DeviceConsoleServerPortTable
|
||||
filterset = filtersets.ConsoleServerPortFilterSet
|
||||
filterset_form = forms.ConsoleServerPortFilterForm
|
||||
template_name = 'dcim/device/consoleserverports.html'
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Console Server Ports'),
|
||||
badge=lambda obj: obj.console_server_port_count,
|
||||
@ -2189,7 +2181,7 @@ class DevicePowerPortsView(DeviceComponentsView):
|
||||
table = tables.DevicePowerPortTable
|
||||
filterset = filtersets.PowerPortFilterSet
|
||||
filterset_form = forms.PowerPortFilterForm
|
||||
template_name = 'dcim/device/powerports.html'
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Power Ports'),
|
||||
badge=lambda obj: obj.power_port_count,
|
||||
@ -2205,7 +2197,7 @@ class DevicePowerOutletsView(DeviceComponentsView):
|
||||
table = tables.DevicePowerOutletTable
|
||||
filterset = filtersets.PowerOutletFilterSet
|
||||
filterset_form = forms.PowerOutletFilterForm
|
||||
template_name = 'dcim/device/poweroutlets.html'
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Power Outlets'),
|
||||
badge=lambda obj: obj.power_outlet_count,
|
||||
@ -2221,6 +2213,7 @@ class DeviceInterfacesView(DeviceComponentsView):
|
||||
table = tables.DeviceInterfaceTable
|
||||
filterset = filtersets.InterfaceFilterSet
|
||||
filterset_form = forms.InterfaceFilterForm
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
|
||||
template_name = 'dcim/device/interfaces.html'
|
||||
tab = ViewTab(
|
||||
label=_('Interfaces'),
|
||||
@ -2243,7 +2236,7 @@ class DeviceFrontPortsView(DeviceComponentsView):
|
||||
table = tables.DeviceFrontPortTable
|
||||
filterset = filtersets.FrontPortFilterSet
|
||||
filterset_form = forms.FrontPortFilterForm
|
||||
template_name = 'dcim/device/frontports.html'
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Front Ports'),
|
||||
badge=lambda obj: obj.front_port_count,
|
||||
@ -2259,7 +2252,7 @@ class DeviceRearPortsView(DeviceComponentsView):
|
||||
table = tables.DeviceRearPortTable
|
||||
filterset = filtersets.RearPortFilterSet
|
||||
filterset_form = forms.RearPortFilterForm
|
||||
template_name = 'dcim/device/rearports.html'
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Rear Ports'),
|
||||
badge=lambda obj: obj.rear_port_count,
|
||||
@ -2275,11 +2268,7 @@ class DeviceModuleBaysView(DeviceComponentsView):
|
||||
table = tables.DeviceModuleBayTable
|
||||
filterset = filtersets.ModuleBayFilterSet
|
||||
filterset_form = forms.ModuleBayFilterForm
|
||||
template_name = 'dcim/device/modulebays.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Module Bays'),
|
||||
badge=lambda obj: obj.module_bay_count,
|
||||
@ -2295,11 +2284,7 @@ class DeviceDeviceBaysView(DeviceComponentsView):
|
||||
table = tables.DeviceDeviceBayTable
|
||||
filterset = filtersets.DeviceBayFilterSet
|
||||
filterset_form = forms.DeviceBayFilterForm
|
||||
template_name = 'dcim/device/devicebays.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Device Bays'),
|
||||
badge=lambda obj: obj.device_bay_count,
|
||||
@ -2315,11 +2300,7 @@ class DeviceInventoryView(DeviceComponentsView):
|
||||
table = tables.DeviceInventoryItemTable
|
||||
filterset = filtersets.InventoryItemFilterSet
|
||||
filterset_form = forms.InventoryItemFilterForm
|
||||
template_name = 'dcim/device/inventory.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Inventory Items'),
|
||||
badge=lambda obj: obj.inventory_item_count,
|
||||
@ -2472,11 +2453,7 @@ class ConsolePortListView(generic.ObjectListView):
|
||||
filterset = filtersets.ConsolePortFilterSet
|
||||
filterset_form = forms.ConsolePortFilterForm
|
||||
table = tables.ConsolePortTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(ConsolePort)
|
||||
@ -2547,11 +2524,7 @@ class ConsoleServerPortListView(generic.ObjectListView):
|
||||
filterset = filtersets.ConsoleServerPortFilterSet
|
||||
filterset_form = forms.ConsoleServerPortFilterForm
|
||||
table = tables.ConsoleServerPortTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(ConsoleServerPort)
|
||||
@ -2622,11 +2595,7 @@ class PowerPortListView(generic.ObjectListView):
|
||||
filterset = filtersets.PowerPortFilterSet
|
||||
filterset_form = forms.PowerPortFilterForm
|
||||
table = tables.PowerPortTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(PowerPort)
|
||||
@ -2697,11 +2666,7 @@ class PowerOutletListView(generic.ObjectListView):
|
||||
filterset = filtersets.PowerOutletFilterSet
|
||||
filterset_form = forms.PowerOutletFilterForm
|
||||
table = tables.PowerOutletTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(PowerOutlet)
|
||||
@ -2772,11 +2737,7 @@ class InterfaceListView(generic.ObjectListView):
|
||||
filterset = filtersets.InterfaceFilterSet
|
||||
filterset_form = forms.InterfaceFilterForm
|
||||
table = tables.InterfaceTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(Interface)
|
||||
@ -2920,11 +2881,7 @@ class FrontPortListView(generic.ObjectListView):
|
||||
filterset = filtersets.FrontPortFilterSet
|
||||
filterset_form = forms.FrontPortFilterForm
|
||||
table = tables.FrontPortTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(FrontPort)
|
||||
@ -2995,11 +2952,7 @@ class RearPortListView(generic.ObjectListView):
|
||||
filterset = filtersets.RearPortFilterSet
|
||||
filterset_form = forms.RearPortFilterForm
|
||||
table = tables.RearPortTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(RearPort)
|
||||
@ -3070,11 +3023,7 @@ class ModuleBayListView(generic.ObjectListView):
|
||||
filterset = filtersets.ModuleBayFilterSet
|
||||
filterset_form = forms.ModuleBayFilterForm
|
||||
table = tables.ModuleBayTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(ModuleBay)
|
||||
@ -3136,11 +3085,7 @@ class DeviceBayListView(generic.ObjectListView):
|
||||
filterset = filtersets.DeviceBayFilterSet
|
||||
filterset_form = forms.DeviceBayFilterForm
|
||||
table = tables.DeviceBayTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(DeviceBay)
|
||||
@ -3283,11 +3228,7 @@ class InventoryItemListView(generic.ObjectListView):
|
||||
filterset = filtersets.InventoryItemFilterSet
|
||||
filterset_form = forms.InventoryItemFilterForm
|
||||
table = tables.InventoryItemTable
|
||||
template_name = 'dcim/component_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(InventoryItem)
|
||||
@ -3627,9 +3568,7 @@ class ConsoleConnectionsListView(generic.ObjectListView):
|
||||
filterset_form = forms.ConsoleConnectionFilterForm
|
||||
table = tables.ConsoleConnectionTable
|
||||
template_name = 'dcim/connections_list.html'
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
}
|
||||
actions = (BulkExport,)
|
||||
|
||||
def get_extra_context(self, request):
|
||||
return {
|
||||
@ -3643,9 +3582,7 @@ class PowerConnectionsListView(generic.ObjectListView):
|
||||
filterset_form = forms.PowerConnectionFilterForm
|
||||
table = tables.PowerConnectionTable
|
||||
template_name = 'dcim/connections_list.html'
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
}
|
||||
actions = (BulkExport,)
|
||||
|
||||
def get_extra_context(self, request):
|
||||
return {
|
||||
@ -3659,9 +3596,7 @@ class InterfaceConnectionsListView(generic.ObjectListView):
|
||||
filterset_form = forms.InterfaceConnectionFilterForm
|
||||
table = tables.InterfaceConnectionTable
|
||||
template_name = 'dcim/connections_list.html'
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
}
|
||||
actions = (BulkExport,)
|
||||
|
||||
def get_extra_context(self, request):
|
||||
return {
|
||||
|
@ -14,12 +14,13 @@ from jinja2.exceptions import TemplateError
|
||||
|
||||
from core.choices import ManagedFileRootPathChoices
|
||||
from core.models import Job
|
||||
from core.object_actions import BulkSync
|
||||
from dcim.models import Device, DeviceRole, Platform
|
||||
from extras.choices import LogLevelChoices
|
||||
from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
|
||||
from extras.dashboard.utils import get_widget_class
|
||||
from extras.utils import SharedObjectViewMixin
|
||||
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||
from netbox.object_actions import *
|
||||
from netbox.views import generic
|
||||
from netbox.views.generic.mixins import TableMixin
|
||||
from utilities.forms import ConfirmationForm, get_field_value
|
||||
@ -232,11 +233,7 @@ class ExportTemplateListView(generic.ObjectListView):
|
||||
filterset = filtersets.ExportTemplateFilterSet
|
||||
filterset_form = forms.ExportTemplateFilterForm
|
||||
table = tables.ExportTemplateTable
|
||||
template_name = 'extras/exporttemplate_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_sync': {'sync'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkSync, BulkEdit, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(ExportTemplate)
|
||||
@ -347,9 +344,7 @@ class TableConfigListView(SharedObjectViewMixin, generic.ObjectListView):
|
||||
filterset = filtersets.TableConfigFilterSet
|
||||
filterset_form = forms.TableConfigFilterForm
|
||||
table = tables.TableConfigTable
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
}
|
||||
actions = (BulkExport,)
|
||||
|
||||
|
||||
@register_model_view(TableConfig)
|
||||
@ -758,13 +753,7 @@ class ConfigContextListView(generic.ObjectListView):
|
||||
filterset = filtersets.ConfigContextFilterSet
|
||||
filterset_form = forms.ConfigContextFilterForm
|
||||
table = tables.ConfigContextTable
|
||||
template_name = 'extras/configcontext_list.html'
|
||||
actions = {
|
||||
'add': {'add'},
|
||||
'bulk_edit': {'change'},
|
||||
'bulk_delete': {'delete'},
|
||||
'bulk_sync': {'sync'},
|
||||
}
|
||||
actions = (AddObject, BulkSync, BulkEdit, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(ConfigContext)
|
||||
@ -877,11 +866,7 @@ class ConfigTemplateListView(generic.ObjectListView):
|
||||
filterset = filtersets.ConfigTemplateFilterSet
|
||||
filterset_form = forms.ConfigTemplateFilterForm
|
||||
table = tables.ConfigTemplateTable
|
||||
template_name = 'extras/configtemplate_list.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_sync': {'sync'},
|
||||
}
|
||||
actions = (AddObject, BulkImport, BulkSync, BulkEdit, BulkExport, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(ConfigTemplate)
|
||||
@ -992,9 +977,7 @@ class ImageAttachmentListView(generic.ObjectListView):
|
||||
filterset = filtersets.ImageAttachmentFilterSet
|
||||
filterset_form = forms.ImageAttachmentFilterForm
|
||||
table = tables.ImageAttachmentTable
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
}
|
||||
actions = (BulkExport,)
|
||||
|
||||
|
||||
@register_model_view(ImageAttachment, 'add', detail=False)
|
||||
@ -1038,12 +1021,7 @@ class JournalEntryListView(generic.ObjectListView):
|
||||
filterset = filtersets.JournalEntryFilterSet
|
||||
filterset_form = forms.JournalEntryFilterForm
|
||||
table = tables.JournalEntryTable
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
'bulk_import': {'add'},
|
||||
'bulk_edit': {'change'},
|
||||
'bulk_delete': {'delete'},
|
||||
}
|
||||
actions = (BulkImport, BulkEdit, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(JournalEntry)
|
||||
|
@ -28,7 +28,8 @@ ADVISORY_LOCK_KEYS = {
|
||||
'job-schedules': 110100,
|
||||
}
|
||||
|
||||
# Default view action permission mapping
|
||||
# TODO: Remove in NetBox v4.6
|
||||
# Legacy default view action permission mapping
|
||||
DEFAULT_ACTION_PERMISSIONS = {
|
||||
'add': {'add'},
|
||||
'export': {'view'},
|
||||
|
176
netbox/netbox/object_actions.py
Normal file
176
netbox/netbox/object_actions.py
Normal file
@ -0,0 +1,176 @@
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from core.models import ObjectType
|
||||
from extras.models import ExportTemplate
|
||||
from utilities.querydict import prepare_cloned_fields
|
||||
|
||||
__all__ = (
|
||||
'AddObject',
|
||||
'BulkDelete',
|
||||
'BulkEdit',
|
||||
'BulkExport',
|
||||
'BulkImport',
|
||||
'BulkRename',
|
||||
'CloneObject',
|
||||
'DeleteObject',
|
||||
'EditObject',
|
||||
'ObjectAction',
|
||||
)
|
||||
|
||||
|
||||
class ObjectAction:
|
||||
"""
|
||||
Base class for single- and multi-object operations.
|
||||
|
||||
Params:
|
||||
name: The action name appended to the module for view resolution
|
||||
label: Human-friendly label for the rendered button
|
||||
multi: Set to True if this action is performed by selecting multiple objects (i.e. using a table)
|
||||
permissions_required: The set of permissions a user must have to perform the action
|
||||
url_kwargs: The set of URL keyword arguments to pass when resolving the view's URL
|
||||
"""
|
||||
name = ''
|
||||
label = None
|
||||
multi = False
|
||||
permissions_required = set()
|
||||
url_kwargs = []
|
||||
|
||||
@classmethod
|
||||
def get_url(cls, obj):
|
||||
viewname = f'{obj._meta.app_label}:{obj._meta.model_name}_{cls.name}'
|
||||
kwargs = {
|
||||
kwarg: getattr(obj, kwarg) for kwarg in cls.url_kwargs
|
||||
}
|
||||
return reverse(viewname, kwargs=kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, context, obj):
|
||||
return {
|
||||
'url': cls.get_url(obj),
|
||||
'label': cls.label,
|
||||
}
|
||||
|
||||
|
||||
class AddObject(ObjectAction):
|
||||
"""
|
||||
Create a new object.
|
||||
"""
|
||||
name = 'add'
|
||||
label = _('Add')
|
||||
permissions_required = {'add'}
|
||||
template_name = 'buttons/add.html'
|
||||
|
||||
|
||||
class CloneObject(ObjectAction):
|
||||
"""
|
||||
Populate the new object form with select details from an existing object.
|
||||
"""
|
||||
name = 'add'
|
||||
label = _('Clone')
|
||||
permissions_required = {'add'}
|
||||
template_name = 'buttons/clone.html'
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, context, obj):
|
||||
param_string = prepare_cloned_fields(obj).urlencode()
|
||||
url = f'{cls.get_url(obj)}?{param_string}' if param_string else None
|
||||
return {
|
||||
'url': url,
|
||||
'label': cls.label,
|
||||
}
|
||||
|
||||
|
||||
class EditObject(ObjectAction):
|
||||
"""
|
||||
Edit a single object.
|
||||
"""
|
||||
name = 'edit'
|
||||
label = _('Edit')
|
||||
permissions_required = {'change'}
|
||||
url_kwargs = ['pk']
|
||||
template_name = 'buttons/edit.html'
|
||||
|
||||
|
||||
class DeleteObject(ObjectAction):
|
||||
"""
|
||||
Delete a single object.
|
||||
"""
|
||||
name = 'delete'
|
||||
label = _('Delete')
|
||||
permissions_required = {'delete'}
|
||||
url_kwargs = ['pk']
|
||||
template_name = 'buttons/delete.html'
|
||||
|
||||
|
||||
class BulkImport(ObjectAction):
|
||||
"""
|
||||
Import multiple objects at once.
|
||||
"""
|
||||
name = 'bulk_import'
|
||||
label = _('Import')
|
||||
permissions_required = {'add'}
|
||||
template_name = 'buttons/import.html'
|
||||
|
||||
|
||||
class BulkExport(ObjectAction):
|
||||
"""
|
||||
Export multiple objects at once.
|
||||
"""
|
||||
name = 'export'
|
||||
label = _('Export')
|
||||
permissions_required = {'view'}
|
||||
template_name = 'buttons/export.html'
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, context, model):
|
||||
object_type = ObjectType.objects.get_for_model(model)
|
||||
user = context['request'].user
|
||||
|
||||
# Determine if the "all data" export returns CSV or YAML
|
||||
data_format = 'YAML' if hasattr(object_type.model_class(), 'to_yaml') else 'CSV'
|
||||
|
||||
# Retrieve all export templates for this model
|
||||
export_templates = ExportTemplate.objects.restrict(user, 'view').filter(object_types=object_type)
|
||||
|
||||
return {
|
||||
'label': cls.label,
|
||||
'perms': context['perms'],
|
||||
'object_type': object_type,
|
||||
'url_params': context['request'].GET.urlencode() if context['request'].GET else '',
|
||||
'export_templates': export_templates,
|
||||
'data_format': data_format,
|
||||
}
|
||||
|
||||
|
||||
class BulkEdit(ObjectAction):
|
||||
"""
|
||||
Change the value of one or more fields on a set of objects.
|
||||
"""
|
||||
name = 'bulk_edit'
|
||||
label = _('Edit Selected')
|
||||
multi = True
|
||||
permissions_required = {'change'}
|
||||
template_name = 'buttons/bulk_edit.html'
|
||||
|
||||
|
||||
class BulkRename(ObjectAction):
|
||||
"""
|
||||
Rename multiple objects at once.
|
||||
"""
|
||||
name = 'bulk_rename'
|
||||
label = _('Rename Selected')
|
||||
multi = True
|
||||
permissions_required = {'change'}
|
||||
template_name = 'buttons/bulk_rename.html'
|
||||
|
||||
|
||||
class BulkDelete(ObjectAction):
|
||||
"""
|
||||
Delete each of a set of objects.
|
||||
"""
|
||||
name = 'bulk_delete'
|
||||
label = _('Delete Selected')
|
||||
multi = True
|
||||
permissions_required = {'delete'}
|
||||
template_name = 'buttons/bulk_delete.html'
|
@ -22,6 +22,7 @@ from core.models import ObjectType
|
||||
from core.signals import clear_events
|
||||
from extras.choices import CustomFieldUIEditableChoices
|
||||
from extras.models import CustomField, ExportTemplate
|
||||
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport
|
||||
from utilities.error_handlers import handle_protectederror
|
||||
from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation
|
||||
from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fields
|
||||
@ -60,6 +61,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
||||
template_name = 'generic/object_list.html'
|
||||
filterset = None
|
||||
filterset_form = None
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkExport, BulkDelete)
|
||||
|
||||
def get_required_permission(self):
|
||||
return get_permission_for_model(self.queryset.model, 'view')
|
||||
@ -150,13 +152,13 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
||||
|
||||
# Determine the available actions
|
||||
actions = self.get_permitted_actions(request.user)
|
||||
has_bulk_actions = any([a.startswith('bulk_') for a in actions])
|
||||
has_table_actions = any(action.multi for action in actions)
|
||||
|
||||
if 'export' in request.GET:
|
||||
|
||||
# Export the current table view
|
||||
if request.GET['export'] == 'table':
|
||||
table = self.get_table(self.queryset, request, has_bulk_actions)
|
||||
table = self.get_table(self.queryset, request, has_table_actions)
|
||||
columns = [name for name, _ in table.selected_columns]
|
||||
return self.export_table(table, columns)
|
||||
|
||||
@ -174,11 +176,11 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
||||
|
||||
# Fall back to default table/YAML export
|
||||
else:
|
||||
table = self.get_table(self.queryset, request, has_bulk_actions)
|
||||
table = self.get_table(self.queryset, request, has_table_actions)
|
||||
return self.export_table(table)
|
||||
|
||||
# Render the objects table
|
||||
table = self.get_table(self.queryset, request, has_bulk_actions)
|
||||
table = self.get_table(self.queryset, request, has_table_actions)
|
||||
|
||||
# If this is an HTMX request, return only the rendered table HTML
|
||||
if htmx_partial(request):
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from extras.models import TableConfig
|
||||
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||
from netbox import object_actions
|
||||
from utilities.permissions import get_permission_for_model
|
||||
|
||||
__all__ = (
|
||||
@ -9,6 +9,18 @@ __all__ = (
|
||||
'TableMixin',
|
||||
)
|
||||
|
||||
# TODO: Remove in NetBox v4.5
|
||||
LEGACY_ACTIONS = {
|
||||
'add': object_actions.AddObject,
|
||||
'edit': object_actions.EditObject,
|
||||
'delete': object_actions.DeleteObject,
|
||||
'export': object_actions.BulkExport,
|
||||
'bulk_import': object_actions.BulkImport,
|
||||
'bulk_edit': object_actions.BulkEdit,
|
||||
'bulk_rename': object_actions.BulkRename,
|
||||
'bulk_delete': object_actions.BulkDelete,
|
||||
}
|
||||
|
||||
|
||||
class ActionsMixin:
|
||||
"""
|
||||
@ -19,7 +31,24 @@ class ActionsMixin:
|
||||
Standard actions include: add, import, export, bulk_edit, and bulk_delete. Some views extend this default map
|
||||
with custom actions, such as bulk_sync.
|
||||
"""
|
||||
actions = DEFAULT_ACTION_PERMISSIONS
|
||||
actions = tuple()
|
||||
|
||||
# TODO: Remove in NetBox v4.5
|
||||
def _convert_legacy_actions(self):
|
||||
"""
|
||||
Convert a legacy dictionary mapping action name to required permissions to a list of ObjectAction subclasses.
|
||||
"""
|
||||
if type(self.actions) is not dict:
|
||||
return
|
||||
|
||||
actions = []
|
||||
for name in self.actions.keys():
|
||||
try:
|
||||
actions.append(LEGACY_ACTIONS[name])
|
||||
except KeyError:
|
||||
raise ValueError(f"Unsupported legacy action: {name}")
|
||||
|
||||
self.actions = actions
|
||||
|
||||
def get_permitted_actions(self, user, model=None):
|
||||
"""
|
||||
@ -27,11 +56,15 @@ class ActionsMixin:
|
||||
"""
|
||||
model = model or self.queryset.model
|
||||
|
||||
# TODO: Remove in NetBox v4.5
|
||||
# Handle legacy action sets
|
||||
self._convert_legacy_actions()
|
||||
|
||||
# Resolve required permissions for each action
|
||||
permitted_actions = []
|
||||
for action in self.actions:
|
||||
required_permissions = [
|
||||
get_permission_for_model(model, name) for name in self.actions.get(action, set())
|
||||
get_permission_for_model(model, perm) for perm in action.permissions_required
|
||||
]
|
||||
if not required_permissions or user.has_perms(required_permissions):
|
||||
permitted_actions.append(action)
|
||||
|
@ -14,6 +14,9 @@ from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from core.signals import clear_events
|
||||
from netbox.object_actions import (
|
||||
AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, CloneObject, DeleteObject, EditObject,
|
||||
)
|
||||
from utilities.error_handlers import handle_protectederror
|
||||
from utilities.exceptions import AbortRequest, PermissionsViolation
|
||||
from utilities.forms import ConfirmationForm, restrict_form_fields
|
||||
@ -36,7 +39,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class ObjectView(BaseObjectView):
|
||||
class ObjectView(ActionsMixin, BaseObjectView):
|
||||
"""
|
||||
Retrieve a single object for display.
|
||||
|
||||
@ -46,6 +49,7 @@ class ObjectView(BaseObjectView):
|
||||
tab: A ViewTab instance for the view
|
||||
"""
|
||||
tab = None
|
||||
actions = (CloneObject, EditObject, DeleteObject)
|
||||
|
||||
def get_required_permission(self):
|
||||
return get_permission_for_model(self.queryset.model, 'view')
|
||||
@ -72,9 +76,11 @@ class ObjectView(BaseObjectView):
|
||||
request: The current request
|
||||
"""
|
||||
instance = self.get_object(**kwargs)
|
||||
actions = self.get_permitted_actions(request.user, model=instance)
|
||||
|
||||
return render(request, self.get_template_name(), {
|
||||
'object': instance,
|
||||
'actions': actions,
|
||||
'tab': self.tab,
|
||||
**self.get_extra_context(request, instance),
|
||||
})
|
||||
@ -97,6 +103,7 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin):
|
||||
table = None
|
||||
filterset = None
|
||||
filterset_form = None
|
||||
actions = (AddObject, BulkImport, BulkEdit, BulkExport, BulkDelete)
|
||||
template_name = 'generic/object_children.html'
|
||||
|
||||
def get_children(self, request, parent):
|
||||
@ -138,10 +145,10 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin):
|
||||
|
||||
# Determine the available actions
|
||||
actions = self.get_permitted_actions(request.user, model=self.child_model)
|
||||
has_bulk_actions = any([a.startswith('bulk_') for a in actions])
|
||||
has_table_actions = any(action.multi for action in actions)
|
||||
|
||||
table_data = self.prep_table_data(request, child_objects, instance)
|
||||
table = self.get_table(table_data, request, has_bulk_actions)
|
||||
table = self.get_table(table_data, request, has_table_actions)
|
||||
|
||||
# If this is an HTMX request, return only the rendered table HTML
|
||||
if htmx_partial(request):
|
||||
|
3
netbox/templates/core/buttons/bulk_sync.html
Normal file
3
netbox/templates/core/buttons/bulk_sync.html
Normal file
@ -0,0 +1,3 @@
|
||||
<button type="submit" name="_sync" {% formaction %}="{{ url }}" class="btn btn-primary">
|
||||
<i class="mdi mdi-sync" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
@ -11,12 +11,6 @@
|
||||
<li class="breadcrumb-item"><a href="{% url 'core:datafile_list' %}?source_id={{ object.source.pk }}">{{ object.source }}</a></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block control-buttons %}
|
||||
{% if request.user|can_delete:object %}
|
||||
{% delete_button object %}
|
||||
{% endif %}
|
||||
{% endblock control-buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
|
@ -22,12 +22,6 @@
|
||||
{% endif %}
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block control-buttons %}
|
||||
{% if request.user|can_delete:object %}
|
||||
{% delete_button object %}
|
||||
{% endif %}
|
||||
{% endblock control-buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col col-12 col-md-6">
|
||||
|
71
netbox/templates/dcim/buttons/bulk_add_components.html
Normal file
71
netbox/templates/dcim/buttons/bulk_add_components.html
Normal file
@ -0,0 +1,71 @@
|
||||
{% load i18n %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% if perms.dcim.add_consoleport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_consoleport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Console Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_consoleserverport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_consoleserverport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item ">
|
||||
{% trans "Console Server Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_powerport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_powerport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Power Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_poweroutlet %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_poweroutlet' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Power Outlets" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_interface %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_interface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Interfaces" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_rearport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_rearport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Rear Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_devicebay %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_devicebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Device Bays" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_modulebay %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_modulebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Module Bays" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_inventoryitem %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_inventoryitem' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Inventory Items" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
3
netbox/templates/dcim/buttons/bulk_disconnect.html
Normal file
3
netbox/templates/dcim/buttons/bulk_disconnect.html
Normal file
@ -0,0 +1,3 @@
|
||||
<button type="submit" name="_disconnect" {% formaction %}="{{ url }}" class="btn btn-red">
|
||||
<i class="mdi mdi-ethernet-cable-off" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
@ -1,22 +0,0 @@
|
||||
{% extends 'generic/object_list.html' %}
|
||||
{% load buttons %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_buttons %}
|
||||
<div class="btn-group" role="group">
|
||||
{% if 'bulk_edit' in actions %}
|
||||
{% bulk_edit_button model query_params=request.GET %}
|
||||
{% endif %}
|
||||
{% if 'bulk_rename' in actions %}
|
||||
{% with bulk_rename_view=model|validated_viewname:"bulk_rename" %}
|
||||
<button type="submit" name="_rename" {% formaction %}="{% url bulk_rename_view %}" class="btn btn-outline-warning btn-float">
|
||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> {% trans "Rename Selected" %}
|
||||
</button>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if 'bulk_delete' in actions %}
|
||||
{% bulk_delete_button model query_params=request.GET %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -1,23 +0,0 @@
|
||||
{% extends 'generic/object_children.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block bulk_edit_controls %}
|
||||
{% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %}
|
||||
{% if 'bulk_edit' in actions and bulk_edit_view %}
|
||||
<button type="submit" name="_edit"
|
||||
{% formaction %}="{% url bulk_edit_view %}?device={{ object.pk }}&return_url={{ return_url }}"
|
||||
class="btn btn-warning">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% with bulk_rename_view=child_model|validated_viewname:"bulk_rename" %}
|
||||
{% if 'bulk_rename' in actions and bulk_rename_view %}
|
||||
<button type="submit" name="_rename"
|
||||
{% formaction %}="{% url bulk_rename_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-warning">
|
||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_edit_controls %}
|
@ -1,28 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
|
||||
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
|
||||
<button type="submit" name="_disconnect"
|
||||
{% formaction %}="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-danger">
|
||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_consoleport %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:consoleport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleports' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Console Ports" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,28 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
|
||||
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
|
||||
<button type="submit" name="_disconnect"
|
||||
{% formaction %}="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-danger">
|
||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_consoleserverport %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Console Server Ports" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,14 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_devicebay %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:devicebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Device Bays" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,28 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
|
||||
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
|
||||
<button type="submit" name="_disconnect"
|
||||
{% formaction %}="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-danger">
|
||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_frontport %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:frontport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_frontports' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Front Ports" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,30 +1,5 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
{% extends 'generic/object_children.html' %}
|
||||
|
||||
{% block table_controls %}
|
||||
{% include 'dcim/device/inc/interface_table_controls.html' with table_modal="DeviceInterfaceTable_config" %}
|
||||
{% include 'dcim/device/inc/interface_table_controls.html' with table_modal="DeviceInterfaceTable_config" %}
|
||||
{% endblock table_controls %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
|
||||
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
|
||||
<button type="submit" name="_disconnect"
|
||||
{% formaction %}="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-danger">
|
||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_interface %}
|
||||
<a href="{% url 'dcim:interface_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Interfaces" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
||||
|
@ -1,14 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_inventoryitem %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:inventoryitem_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_inventory' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Inventory Item" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,14 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_modulebay %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:modulebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_modulebays' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Module Bays" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,28 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
|
||||
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
|
||||
<button type="submit" name="_disconnect"
|
||||
{% formaction %}="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-danger">
|
||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_poweroutlet %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:poweroutlet_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Power Outlets" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,28 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
|
||||
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
|
||||
<button type="submit" name="_disconnect"
|
||||
{% formaction %}="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-danger">
|
||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_powerport %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:powerport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_powerports' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Power Port" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,28 +0,0 @@
|
||||
{% extends 'dcim/device/components_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
|
||||
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
|
||||
<button type="submit" name="_disconnect"
|
||||
{% formaction %}="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-danger">
|
||||
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.dcim.add_rearport %}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'dcim:rearport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_rearports' pk=object.pk %}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Rear Ports" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock bulk_extra_controls %}
|
@ -1,89 +0,0 @@
|
||||
{% extends 'generic/object_list.html' %}
|
||||
{% load buttons %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_buttons %}
|
||||
{% if perms.dcim.change_device %}
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Components" %}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% if perms.dcim.add_consoleport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_consoleport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Console Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_consoleserverport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_consoleserverport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item ">
|
||||
{% trans "Console Server Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_powerport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_powerport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Power Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_poweroutlet %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_poweroutlet' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Power Outlets" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_interface %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_interface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Interfaces" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_rearport %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_rearport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Rear Ports" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_devicebay %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_devicebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Device Bays" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_modulebay %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_modulebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Module Bays" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_inventoryitem %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'dcim:device_bulk_add_inventoryitem' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Inventory Items" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if 'bulk_edit' in actions %}
|
||||
<div class="btn-group" role="group">
|
||||
{% bulk_edit_button model query_params=request.GET %}
|
||||
<button type="submit" name="_rename" {% formaction %}="{% url 'dcim:device_bulk_rename' %}?return_url={% url 'dcim:device_list' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-outline-warning btn-float">
|
||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> {% trans "Rename" %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if 'bulk_delete' in actions %}
|
||||
{% bulk_delete_button model query_params=request.GET %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -1,25 +0,0 @@
|
||||
{% extends 'generic/object_children.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
{% load perms %}
|
||||
|
||||
{% block bulk_edit_controls %}
|
||||
{% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %}
|
||||
{% if 'bulk_edit' in actions and bulk_edit_view %}
|
||||
<button type="submit" name="_edit"
|
||||
{% formaction %}="{% url bulk_edit_view %}?device={{ object.pk }}&return_url={{ return_url }}"
|
||||
class="btn btn-warning">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% with bulk_rename_view=child_model|validated_viewname:"bulk_rename" %}
|
||||
{% if 'bulk_rename' in actions and bulk_rename_view %}
|
||||
<button type="submit" name="_rename"
|
||||
{% formaction %}="{% url bulk_rename_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-warning">
|
||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename Selected
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_edit_controls %}
|
@ -1,30 +0,0 @@
|
||||
{% extends 'generic/object_children.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extra_controls %}
|
||||
{% include 'dcim/inc/moduletype_buttons.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block bulk_edit_controls %}
|
||||
{% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %}
|
||||
{% if 'bulk_edit' in actions and bulk_edit_view %}
|
||||
<button type="submit" name="_edit"
|
||||
{% formaction %}="{% url bulk_edit_view %}?device={{ object.pk }}&return_url={{ return_url }}"
|
||||
class="btn btn-warning">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% with bulk_rename_view=child_model|validated_viewname:"bulk_rename" %}
|
||||
{% if 'bulk_rename' in actions and bulk_rename_view %}
|
||||
<button type="submit" name="_rename"
|
||||
{% formaction %}="{% url bulk_rename_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-warning">
|
||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename Selected
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_edit_controls %}
|
||||
|
@ -1,18 +1,8 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load buttons %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block buttons %}
|
||||
{% if perms.dcim.change_virtualchassis %}
|
||||
{% edit_button object %}
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_virtualchassis %}
|
||||
{% delete_button object %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-12 col-md-4">
|
||||
|
@ -1,11 +0,0 @@
|
||||
{% extends 'generic/object_list.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_buttons %}
|
||||
{% if perms.extras.sync_configcontext %}
|
||||
<button type="submit" name="_sync" {% formaction %}="{% url 'extras:configcontext_bulk_sync' %}" class="btn btn-primary">
|
||||
<i class="mdi mdi-sync" aria-hidden="true"></i> {% trans "Sync Data" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
@ -1,11 +0,0 @@
|
||||
{% extends 'generic/object_list.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_buttons %}
|
||||
{% if perms.extras.sync_configtemplate %}
|
||||
<button type="submit" name="_sync" {% formaction %}="{% url 'extras:configtemplate_bulk_sync' %}" class="btn btn-primary">
|
||||
<i class="mdi mdi-sync" aria-hidden="true"></i> {% trans "Sync Data" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
@ -1,11 +0,0 @@
|
||||
{% extends 'generic/object_list.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_buttons %}
|
||||
{% if perms.extras.sync_configcontext %}
|
||||
<button type="submit" name="_sync" {% formaction %}="{% url 'extras:exporttemplate_bulk_sync' %}" class="btn btn-primary">
|
||||
<i class="mdi mdi-sync" aria-hidden="true"></i> {% trans "Sync Data" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
@ -1,72 +0,0 @@
|
||||
{% extends 'generic/_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% comment %}
|
||||
Blocks:
|
||||
- title: Page title
|
||||
- tabs: Page tabs
|
||||
- content: Primary page content
|
||||
|
||||
Context:
|
||||
- form: The bulk edit form class
|
||||
- parent_obj: The parent object
|
||||
- table: A table of objects being removed
|
||||
- obj_type_plural: The plural form of the object type
|
||||
- return_url: The URL to which the user is redirected after submitting the form
|
||||
{% endcomment %}
|
||||
|
||||
{% block title %}
|
||||
{% trans "Remove" %} {{ table.rows|length }} {{ obj_type_plural|bettertitle }}?
|
||||
{% endblock %}
|
||||
|
||||
{% block tabs %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" type="button" role="tab" aria-controls="edit-form" aria-selected="true">
|
||||
{% trans "Bulk Remove" %}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock tabs %}
|
||||
|
||||
{% block content %}
|
||||
<div class="tab-pane show active" role="tabpanel">
|
||||
<div class="alert alert-danger bg-danger-subtle" role="alert">
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
<i class="mdi mdi-alert-octagon p-2"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="alert-title">{% trans "Confirm Bulk Removal" %}</h4>
|
||||
{% blocktrans trimmed with count=table.rows|length %}
|
||||
The following operation will remove {{ count }} {{ obj_type_plural }} from {{ parent_obj }}. Please
|
||||
carefully review the {{ obj_type_plural }} to be removed and confirm below.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid px-0">
|
||||
<div class="card">
|
||||
<div class="table-responsive">
|
||||
{% render_table table 'inc/table.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<form action="." method="post" class="form">
|
||||
{% csrf_token %}
|
||||
{% for field in form.hidden_fields %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
<div class="text-end">
|
||||
<a href="{{ return_url }}" class="btn btn-outline-secondary">{% trans "Cancel" %}</a>
|
||||
<button type="submit" name="_confirm" class="btn btn-danger">
|
||||
{% blocktrans trimmed with count=table.rows|length %}
|
||||
Remove these {{ count }} {{ obj_type_plural }}
|
||||
{% endblocktrans %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
@ -80,15 +80,7 @@ Context:
|
||||
{% if perms.extras.add_subscription and object.subscriptions %}
|
||||
{% subscribe_button object %}
|
||||
{% endif %}
|
||||
{% if request.user|can_add:object %}
|
||||
{% clone_button object %}
|
||||
{% endif %}
|
||||
{% if request.user|can_change:object %}
|
||||
{% edit_button object %}
|
||||
{% endif %}
|
||||
{% if request.user|can_delete:object %}
|
||||
{% delete_button object %}
|
||||
{% endif %}
|
||||
{% action_buttons actions object %}
|
||||
{% endblock control-buttons %}
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends base_template %}
|
||||
{% load buttons %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
@ -7,8 +8,6 @@ Blocks:
|
||||
- content: Primary page content
|
||||
- table_controls: Control elements for the child objects table
|
||||
- bulk_controls: Bulk action buttons which appear beneath the child objects table
|
||||
- bulk_edit_controls: Bulk edit buttons
|
||||
- bulk_delete_controls: Bulk delete buttons
|
||||
- bulk_extra_controls: Other bulk action buttons
|
||||
- modals: Any pre-loaded modals
|
||||
|
||||
@ -36,36 +35,8 @@ Context:
|
||||
</div>
|
||||
<div class="d-print-none mt-2">
|
||||
{% block bulk_controls %}
|
||||
<div class="btn-group" role="group">
|
||||
{# Bulk edit buttons #}
|
||||
{% block bulk_edit_controls %}
|
||||
{% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %}
|
||||
{% if 'bulk_edit' in actions and bulk_edit_view %}
|
||||
<button type="submit" name="_edit"
|
||||
{% formaction %}="{% url bulk_edit_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-warning">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit Selected" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_edit_controls %}
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
{# Bulk delete buttons #}
|
||||
{% block bulk_delete_controls %}
|
||||
{% with bulk_delete_view=child_model|validated_viewname:"bulk_delete" %}
|
||||
{% if 'bulk_delete' in actions and bulk_delete_view %}
|
||||
<button type="submit"
|
||||
{% formaction %}="{% url bulk_delete_view %}?return_url={{ return_url }}"
|
||||
class="btn btn-danger">
|
||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete Selected" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock bulk_delete_controls %}
|
||||
</div>
|
||||
{# Other bulk action buttons #}
|
||||
{% block bulk_extra_controls %}{% endblock %}
|
||||
{% action_buttons actions model multi=True %}
|
||||
{% block bulk_extra_controls %}{% endblock %}
|
||||
{% endblock bulk_controls %}
|
||||
</div>
|
||||
</form>
|
||||
|
@ -31,15 +31,7 @@ Context:
|
||||
<div class="btn-list">
|
||||
{% plugin_list_buttons model %}
|
||||
{% block extra_controls %}{% endblock %}
|
||||
{% if 'add' in actions %}
|
||||
{% add_button model %}
|
||||
{% endif %}
|
||||
{% if 'bulk_import' in actions %}
|
||||
{% import_button model %}
|
||||
{% endif %}
|
||||
{% if 'export' in actions %}
|
||||
{% export_button model %}
|
||||
{% endif %}
|
||||
{% action_buttons actions model %}
|
||||
</div>
|
||||
{% endblock controls %}
|
||||
|
||||
@ -91,12 +83,7 @@ Context:
|
||||
</label>
|
||||
</div>
|
||||
<div class="bulk-action-buttons">
|
||||
{% if 'bulk_edit' in actions %}
|
||||
{% bulk_edit_button model query_params=request.GET %}
|
||||
{% endif %}
|
||||
{% if 'bulk_delete' in actions %}
|
||||
{% bulk_delete_button model query_params=request.GET %}
|
||||
{% endif %}
|
||||
{% action_buttons actions model multi=True %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -124,12 +111,7 @@ Context:
|
||||
<div class="btn-list d-print-none">
|
||||
{% block bulk_buttons %}
|
||||
<div class="bulk-action-buttons">
|
||||
{% if 'bulk_edit' in actions %}
|
||||
{% bulk_edit_button model query_params=request.GET %}
|
||||
{% endif %}
|
||||
{% if 'bulk_delete' in actions %}
|
||||
{% bulk_delete_button model query_params=request.GET %}
|
||||
{% endif %}
|
||||
{% action_buttons actions model multi=True %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
@ -0,0 +1,22 @@
|
||||
{% load i18n %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Components" %}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% if perms.virtualization.add_vminterface %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'virtualization:virtualmachine_bulk_add_vminterface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Interfaces" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.virtualization.add_virtualdisk %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'virtualization:virtualmachine_bulk_add_virtualdisk' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Virtual Disks" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
@ -1,13 +0,0 @@
|
||||
{% extends 'generic/object_children.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_delete_controls %}
|
||||
{{ block.super }}
|
||||
{% if 'bulk_remove_devices' in actions %}
|
||||
<button type="submit" name="_remove"
|
||||
{% formaction %}="{% url 'virtualization:cluster_remove_devices' pk=object.pk %}?return_url={{ return_url }}"
|
||||
class="btn btn-danger">
|
||||
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> {% trans "Remove Selected" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endblock bulk_delete_controls %}
|
@ -1,14 +0,0 @@
|
||||
{% extends 'generic/object_children.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_edit_controls %}
|
||||
{{ block.super }}
|
||||
{% if 'bulk_rename' in actions %}
|
||||
<button type="submit" name="_rename"
|
||||
{% formaction %}="{% url 'virtualization:vminterface_bulk_rename' %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-warning">
|
||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> {% trans "Rename" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endblock bulk_edit_controls %}
|
@ -1,14 +0,0 @@
|
||||
{% extends 'generic/object_children.html' %}
|
||||
{% load helpers %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_edit_controls %}
|
||||
{{ block.super }}
|
||||
{% if 'bulk_rename' in actions %}
|
||||
<button type="submit" name="_rename"
|
||||
{% formaction %}="{% url 'virtualization:virtualdisk_bulk_rename' %}?return_url={{ return_url }}"
|
||||
class="btn btn-outline-warning">
|
||||
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> {% trans "Rename" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endblock bulk_edit_controls %}
|
@ -1,29 +0,0 @@
|
||||
{% extends 'generic/object_list.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_buttons %}
|
||||
{% if perms.virtualization.change_virtualmachine %}
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Components" %}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% if perms.virtualization.add_vminterface %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'virtualization:virtualmachine_bulk_add_vminterface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Interfaces" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.virtualization.add_virtualdisk %}
|
||||
<li>
|
||||
<button type="submit" {% formaction %}="{% url 'virtualization:virtualmachine_bulk_add_virtualdisk' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||
{% trans "Virtual Disks" %}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
@ -1,6 +1,7 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from netbox.object_actions import BulkDelete, BulkEdit, BulkExport, BulkImport
|
||||
from netbox.views import generic
|
||||
from utilities.query import count_related
|
||||
from utilities.views import GetRelatedModelsMixin, register_model_view
|
||||
@ -349,12 +350,7 @@ class ContactAssignmentListView(generic.ObjectListView):
|
||||
filterset = filtersets.ContactAssignmentFilterSet
|
||||
filterset_form = forms.ContactAssignmentFilterForm
|
||||
table = tables.ContactAssignmentTable
|
||||
actions = {
|
||||
'export': {'view'},
|
||||
'bulk_import': {'add'},
|
||||
'bulk_edit': {'change'},
|
||||
'bulk_delete': {'delete'},
|
||||
}
|
||||
actions = (BulkExport, BulkImport, BulkEdit, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(ContactAssignment, 'add', detail=False)
|
||||
|
@ -1,6 +1,3 @@
|
||||
{% if url %}
|
||||
{% load i18n %}
|
||||
<a href="{{ url }}" type="button" class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick"></i> {% trans "Add" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{{ url }}" class="btn btn-primary" role="button">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {{ label }}
|
||||
</a>
|
||||
|
@ -1,6 +1,3 @@
|
||||
{% load i18n %}
|
||||
{% if url %}
|
||||
<button type="submit" name="_delete" {% formaction %}="{{ url }}" class="btn btn-red">
|
||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete Selected" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="submit" name="_delete" {% formaction %}="{{ url }}" class="btn btn-red">
|
||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
||||
|
@ -1,6 +1,3 @@
|
||||
{% load i18n %}
|
||||
{% if url %}
|
||||
<button type="submit" name="_edit" {% formaction %}="{{ url }}" class="btn btn-yellow">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit Selected" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="submit" name="_edit" {% formaction %}="{{ url }}" class="btn btn-yellow">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
||||
|
3
netbox/utilities/templates/buttons/bulk_rename.html
Normal file
3
netbox/utilities/templates/buttons/bulk_rename.html
Normal file
@ -0,0 +1,3 @@
|
||||
<button type="submit" name="_rename" {% formaction %}="{{ url }}" class="btn btn-yellow">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
@ -1,12 +1,12 @@
|
||||
{% load i18n %}
|
||||
<a href="#"
|
||||
hx-get="{{ url }}"
|
||||
hx-target="#htmx-modal-content"
|
||||
hx-swap="innerHTML"
|
||||
hx-select="form"
|
||||
class="btn btn-red"
|
||||
role="button"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#htmx-modal"
|
||||
>
|
||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete" %}
|
||||
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {{ label }}
|
||||
</a>
|
||||
|
@ -1,4 +1,3 @@
|
||||
{% load i18n %}
|
||||
<a href="{{ url }}" class="btn btn-yellow" role="button">
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit" %}
|
||||
<i class="mdi mdi-pencil" aria-hidden="true"></i> {{ label }}
|
||||
</a>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% load i18n %}
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-purple dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="mdi mdi-download"></i> {% trans "Export" %}
|
||||
<i class="mdi mdi-download" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a id="export_current_view" class="dropdown-item" href="?{% if url_params %}{{ url_params }}&{% endif %}export=table">{% trans "Current View" %}</a></li>
|
||||
|
@ -1,6 +1,3 @@
|
||||
{% load i18n %}
|
||||
{% if url %}
|
||||
<a href="{{ url }}" type="button" class="btn btn-cyan">
|
||||
<i class="mdi mdi-upload"></i> {% trans "Import" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{{ url }}" class="btn btn-cyan" role="button">
|
||||
<i class="mdi mdi-upload" aria-hidden="true"></i> {{ label }}
|
||||
</a>
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% load i18n %}
|
||||
<form action="{{ url }}" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="mdi mdi-sync" aria-hidden="true"></i> {% trans "Sync" %}
|
||||
<i class="mdi mdi-sync" aria-hidden="true"></i> {{ label }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -1,6 +1,9 @@
|
||||
from django import template
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.template import loader
|
||||
from django.urls import NoReverseMatch, reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from core.models import ObjectType
|
||||
from extras.models import Bookmark, ExportTemplate, Subscription
|
||||
@ -9,6 +12,7 @@ from utilities.querydict import prepare_cloned_fields
|
||||
from utilities.views import get_viewname
|
||||
|
||||
__all__ = (
|
||||
'action_buttons',
|
||||
'add_button',
|
||||
'bookmark_button',
|
||||
'bulk_delete_button',
|
||||
@ -25,9 +29,14 @@ __all__ = (
|
||||
register = template.Library()
|
||||
|
||||
|
||||
#
|
||||
# Instance buttons
|
||||
#
|
||||
@register.simple_tag(takes_context=True)
|
||||
def action_buttons(context, actions, obj, multi=False):
|
||||
buttons = [
|
||||
loader.render_to_string(action.template_name, action.get_context(context, obj))
|
||||
for action in actions if action.multi == multi
|
||||
]
|
||||
return mark_safe(''.join(buttons))
|
||||
|
||||
|
||||
@register.inclusion_tag('buttons/bookmark.html', takes_context=True)
|
||||
def bookmark_button(context, instance):
|
||||
@ -60,42 +69,6 @@ def bookmark_button(context, instance):
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('buttons/clone.html')
|
||||
def clone_button(instance):
|
||||
url = reverse(get_viewname(instance, 'add'))
|
||||
|
||||
# Populate cloned field values
|
||||
param_string = prepare_cloned_fields(instance).urlencode()
|
||||
if param_string:
|
||||
url = f'{url}?{param_string}'
|
||||
else:
|
||||
url = None
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('buttons/edit.html')
|
||||
def edit_button(instance):
|
||||
viewname = get_viewname(instance, 'edit')
|
||||
url = reverse(viewname, kwargs={'pk': instance.pk})
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('buttons/delete.html')
|
||||
def delete_button(instance):
|
||||
viewname = get_viewname(instance, 'delete')
|
||||
url = reverse(viewname, kwargs={'pk': instance.pk})
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('buttons/subscribe.html', takes_context=True)
|
||||
def subscribe_button(context, instance):
|
||||
# Skip for objects which don't support notifications
|
||||
@ -131,20 +104,70 @@ def subscribe_button(context, instance):
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Legacy object buttons
|
||||
#
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/clone.html')
|
||||
def clone_button(instance):
|
||||
# Resolve URL path
|
||||
viewname = get_viewname(instance, 'add')
|
||||
try:
|
||||
url = reverse(viewname)
|
||||
except NoReverseMatch:
|
||||
return {
|
||||
'url': None,
|
||||
}
|
||||
|
||||
# Populate cloned field values and return full URL
|
||||
param_string = prepare_cloned_fields(instance).urlencode()
|
||||
return {
|
||||
'url': f'{url}?{param_string}' if param_string else None,
|
||||
}
|
||||
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/edit.html')
|
||||
def edit_button(instance):
|
||||
viewname = get_viewname(instance, 'edit')
|
||||
url = reverse(viewname, kwargs={'pk': instance.pk})
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
'label': _('Edit'),
|
||||
}
|
||||
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/delete.html')
|
||||
def delete_button(instance):
|
||||
viewname = get_viewname(instance, 'delete')
|
||||
url = reverse(viewname, kwargs={'pk': instance.pk})
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
'label': _('Delete'),
|
||||
}
|
||||
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/sync.html')
|
||||
def sync_button(instance):
|
||||
viewname = get_viewname(instance, 'sync')
|
||||
url = reverse(viewname, kwargs={'pk': instance.pk})
|
||||
|
||||
return {
|
||||
'label': _('Sync'),
|
||||
'url': url,
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# List buttons
|
||||
# Legacy list buttons
|
||||
#
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/add.html')
|
||||
def add_button(model, action='add'):
|
||||
try:
|
||||
@ -154,9 +177,11 @@ def add_button(model, action='add'):
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
'label': _('Add'),
|
||||
}
|
||||
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/import.html')
|
||||
def import_button(model, action='bulk_import'):
|
||||
try:
|
||||
@ -166,9 +191,11 @@ def import_button(model, action='bulk_import'):
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
'label': _('Import'),
|
||||
}
|
||||
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/export.html', takes_context=True)
|
||||
def export_button(context, model):
|
||||
object_type = ObjectType.objects.get_for_model(model)
|
||||
@ -181,6 +208,7 @@ def export_button(context, model):
|
||||
export_templates = ExportTemplate.objects.restrict(user, 'view').filter(object_types=object_type)
|
||||
|
||||
return {
|
||||
'label': _('Export'),
|
||||
'perms': context['perms'],
|
||||
'object_type': object_type,
|
||||
'url_params': context['request'].GET.urlencode() if context['request'].GET else '',
|
||||
@ -189,6 +217,7 @@ def export_button(context, model):
|
||||
}
|
||||
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/bulk_edit.html', takes_context=True)
|
||||
def bulk_edit_button(context, model, action='bulk_edit', query_params=None):
|
||||
try:
|
||||
@ -199,11 +228,13 @@ def bulk_edit_button(context, model, action='bulk_edit', query_params=None):
|
||||
url = None
|
||||
|
||||
return {
|
||||
'htmx_navigation': context.get('htmx_navigation'),
|
||||
'label': _('Edit Selected'),
|
||||
'url': url,
|
||||
'htmx_navigation': context.get('htmx_navigation'),
|
||||
}
|
||||
|
||||
|
||||
# TODO: Remove in NetBox v4.6
|
||||
@register.inclusion_tag('buttons/bulk_delete.html', takes_context=True)
|
||||
def bulk_delete_button(context, model, action='bulk_delete', query_params=None):
|
||||
try:
|
||||
@ -214,6 +245,7 @@ def bulk_delete_button(context, model, action='bulk_delete', query_params=None):
|
||||
url = None
|
||||
|
||||
return {
|
||||
'htmx_navigation': context.get('htmx_navigation'),
|
||||
'label': _('Delete Selected'),
|
||||
'url': url,
|
||||
'htmx_navigation': context.get('htmx_navigation'),
|
||||
}
|
||||
|
26
netbox/virtualization/object_actions.py
Normal file
26
netbox/virtualization/object_actions.py
Normal file
@ -0,0 +1,26 @@
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from netbox.object_actions import ObjectAction
|
||||
|
||||
__all__ = (
|
||||
'BulkAddComponents',
|
||||
)
|
||||
|
||||
|
||||
class BulkAddComponents(ObjectAction):
|
||||
"""
|
||||
Add components to the selected virtual machines.
|
||||
"""
|
||||
label = _('Add Components')
|
||||
multi = True
|
||||
permissions_required = {'change'}
|
||||
template_name = 'virtualization/buttons/bulk_add_components.html'
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, context, obj):
|
||||
return {
|
||||
'perms': context.get('perms'),
|
||||
'request': context.get('request'),
|
||||
'formaction': context.get('formaction'),
|
||||
'label': cls.label,
|
||||
}
|
@ -13,13 +13,14 @@ from dcim.tables import DeviceTable
|
||||
from extras.views import ObjectConfigContextView, ObjectRenderConfigView
|
||||
from ipam.models import IPAddress, VLANGroup
|
||||
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
||||
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||
from netbox.object_actions import *
|
||||
from netbox.views import generic
|
||||
from utilities.query import count_related
|
||||
from utilities.query_functions import CollateAsChar
|
||||
from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
|
||||
from . import filtersets, forms, tables
|
||||
from .models import *
|
||||
from .object_actions import BulkAddComponents
|
||||
|
||||
|
||||
#
|
||||
@ -204,6 +205,7 @@ class ClusterVirtualMachinesView(generic.ObjectChildrenView):
|
||||
table = tables.VirtualMachineTable
|
||||
filterset = filtersets.VirtualMachineFilterSet
|
||||
filterset_form = forms.VirtualMachineFilterForm
|
||||
actions = (EditObject, DeleteObject, BulkEdit)
|
||||
tab = ViewTab(
|
||||
label=_('Virtual Machines'),
|
||||
badge=lambda obj: obj.virtual_machines.count(),
|
||||
@ -222,14 +224,7 @@ class ClusterDevicesView(generic.ObjectChildrenView):
|
||||
table = DeviceTable
|
||||
filterset = DeviceFilterSet
|
||||
filterset_form = DeviceFilterForm
|
||||
template_name = 'virtualization/cluster/devices.html'
|
||||
actions = {
|
||||
'add': {'add'},
|
||||
'export': {'view'},
|
||||
'bulk_import': {'add'},
|
||||
'bulk_edit': {'change'},
|
||||
'bulk_remove_devices': {'change'},
|
||||
}
|
||||
actions = (EditObject, DeleteObject, BulkEdit)
|
||||
tab = ViewTab(
|
||||
label=_('Devices'),
|
||||
badge=lambda obj: obj.devices.count(),
|
||||
@ -317,50 +312,6 @@ class ClusterAddDevicesView(generic.ObjectEditView):
|
||||
})
|
||||
|
||||
|
||||
@register_model_view(Cluster, 'remove_devices', path='devices/remove')
|
||||
class ClusterRemoveDevicesView(generic.ObjectEditView):
|
||||
queryset = Cluster.objects.all()
|
||||
form = forms.ClusterRemoveDevicesForm
|
||||
template_name = 'generic/bulk_remove.html'
|
||||
|
||||
def post(self, request, pk):
|
||||
|
||||
cluster = get_object_or_404(self.queryset, pk=pk)
|
||||
|
||||
if '_confirm' in request.POST:
|
||||
form = self.form(request.POST)
|
||||
if form.is_valid():
|
||||
|
||||
device_pks = form.cleaned_data['pk']
|
||||
with transaction.atomic(using=router.db_for_write(Device)):
|
||||
|
||||
# Remove the selected Devices from the Cluster
|
||||
for device in Device.objects.filter(pk__in=device_pks):
|
||||
device.cluster = None
|
||||
device.save()
|
||||
|
||||
messages.success(request, _("Removed {count} devices from cluster {cluster}").format(
|
||||
count=len(device_pks),
|
||||
cluster=cluster
|
||||
))
|
||||
return redirect(cluster.get_absolute_url())
|
||||
|
||||
else:
|
||||
form = self.form(initial={'pk': request.POST.getlist('pk')})
|
||||
|
||||
selected_objects = Device.objects.filter(pk__in=form.initial['pk'])
|
||||
device_table = DeviceTable(list(selected_objects), orderable=False)
|
||||
device_table.configure(request)
|
||||
|
||||
return render(request, self.template_name, {
|
||||
'form': form,
|
||||
'parent_obj': cluster,
|
||||
'table': device_table,
|
||||
'obj_type_plural': 'devices',
|
||||
'return_url': cluster.get_absolute_url(),
|
||||
})
|
||||
|
||||
|
||||
#
|
||||
# Virtual machines
|
||||
#
|
||||
@ -371,7 +322,7 @@ class VirtualMachineListView(generic.ObjectListView):
|
||||
filterset = filtersets.VirtualMachineFilterSet
|
||||
filterset_form = forms.VirtualMachineFilterForm
|
||||
table = tables.VirtualMachineTable
|
||||
template_name = 'virtualization/virtualmachine_list.html'
|
||||
actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkDelete)
|
||||
|
||||
|
||||
@register_model_view(VirtualMachine)
|
||||
@ -386,11 +337,7 @@ class VirtualMachineInterfacesView(generic.ObjectChildrenView):
|
||||
table = tables.VirtualMachineVMInterfaceTable
|
||||
filterset = filtersets.VMInterfaceFilterSet
|
||||
filterset_form = forms.VMInterfaceFilterForm
|
||||
template_name = 'virtualization/virtualmachine/interfaces.html'
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Interfaces'),
|
||||
badge=lambda obj: obj.interface_count,
|
||||
@ -412,17 +359,13 @@ class VirtualMachineVirtualDisksView(generic.ObjectChildrenView):
|
||||
table = tables.VirtualMachineVirtualDiskTable
|
||||
filterset = filtersets.VirtualDiskFilterSet
|
||||
filterset_form = forms.VirtualDiskFilterForm
|
||||
template_name = 'virtualization/virtualmachine/virtual_disks.html'
|
||||
actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
|
||||
tab = ViewTab(
|
||||
label=_('Virtual Disks'),
|
||||
badge=lambda obj: obj.virtual_disk_count,
|
||||
permission='virtualization.view_virtualdisk',
|
||||
weight=500
|
||||
)
|
||||
actions = {
|
||||
**DEFAULT_ACTION_PERMISSIONS,
|
||||
'bulk_rename': {'change'},
|
||||
}
|
||||
|
||||
def get_children(self, request, parent):
|
||||
return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags')
|
||||
|
Loading…
Reference in New Issue
Block a user