Work in progress

This commit is contained in:
Jeremy Stretch 2025-06-26 17:10:05 -04:00
parent c438c13045
commit 3e26ce950c
34 changed files with 211 additions and 533 deletions

View 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')
bulk = True
permissions_required = {'sync'}
template_name = 'buttons/bulk_sync.html'

View File

@ -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 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.config import get_config, PARAMS
from netbox.object_actions import BulkDelete, BulkExport
from netbox.registry import registry from netbox.registry import registry
from netbox.views import generic from netbox.views import generic
from netbox.views.generic.base import BaseObjectView from netbox.views.generic.base import BaseObjectView
@ -138,9 +139,7 @@ class DataFileListView(generic.ObjectListView):
filterset = filtersets.DataFileFilterSet filterset = filtersets.DataFileFilterSet
filterset_form = forms.DataFileFilterForm filterset_form = forms.DataFileFilterForm
table = tables.DataFileTable table = tables.DataFileTable
actions = { actions = (BulkDelete,)
'bulk_delete': {'delete'},
}
@register_model_view(DataFile) @register_model_view(DataFile)
@ -170,10 +169,7 @@ class JobListView(generic.ObjectListView):
filterset = filtersets.JobFilterSet filterset = filtersets.JobFilterSet
filterset_form = forms.JobFilterForm filterset_form = forms.JobFilterForm
table = tables.JobTable table = tables.JobTable
actions = { actions = (BulkExport, BulkDelete)
'export': {'view'},
'bulk_delete': {'delete'},
}
@register_model_view(Job) @register_model_view(Job)
@ -204,9 +200,7 @@ class ObjectChangeListView(generic.ObjectListView):
filterset_form = forms.ObjectChangeFilterForm filterset_form = forms.ObjectChangeFilterForm
table = tables.ObjectChangeTable table = tables.ObjectChangeTable
template_name = 'core/objectchange_list.html' template_name = 'core/objectchange_list.html'
actions = { actions = (BulkExport,)
'export': {'view'},
}
@register_model_view(ObjectChange) @register_model_view(ObjectChange)

View File

@ -0,0 +1,18 @@
from django.utils.translation import gettext as _
from netbox.object_actions import ObjectAction
__all__ = (
'BulkDisconnect',
)
class BulkDisconnect(ObjectAction):
"""
Disconnect each of a set of objects to which a cable is connected.
"""
name = 'bulk_disconnect'
label = _('Disconnect Selected')
bulk = True
permissions_required = {'change'}
template_name = 'buttons/bulk_disconnect.html'

View File

@ -15,7 +15,7 @@ from circuits.models import Circuit, CircuitTermination
from extras.views import ObjectConfigContextView, ObjectRenderConfigView from extras.views import ObjectConfigContextView, ObjectRenderConfigView
from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.object_actions import *
from netbox.views import generic from netbox.views import generic
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.paginator import EnhancedPaginator, get_paginate_count
@ -34,6 +34,7 @@ from wireless.models import WirelessLAN
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .choices import DeviceFaceChoices, InterfaceModeChoices from .choices import DeviceFaceChoices, InterfaceModeChoices
from .models import * from .models import *
from .object_actions import BulkDisconnect
CABLE_TERMINATION_TYPES = { CABLE_TERMINATION_TYPES = {
'dcim.consoleport': ConsolePort, 'dcim.consoleport': ConsolePort,
@ -49,11 +50,6 @@ CABLE_TERMINATION_TYPES = {
class DeviceComponentsView(generic.ObjectChildrenView): class DeviceComponentsView(generic.ObjectChildrenView):
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
'bulk_disconnect': {'change'},
}
queryset = Device.objects.all() queryset = Device.objects.all()
def get_children(self, request, parent): def get_children(self, request, parent):
@ -61,10 +57,7 @@ class DeviceComponentsView(generic.ObjectChildrenView):
class DeviceTypeComponentsView(generic.ObjectChildrenView): class DeviceTypeComponentsView(generic.ObjectChildrenView):
actions = { actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete)
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
queryset = DeviceType.objects.all() queryset = DeviceType.objects.all()
template_name = 'dcim/devicetype/component_templates.html' template_name = 'dcim/devicetype/component_templates.html'
viewname = None # Used for return_url resolution viewname = None # Used for return_url resolution
@ -78,8 +71,9 @@ class DeviceTypeComponentsView(generic.ObjectChildrenView):
} }
class ModuleTypeComponentsView(DeviceComponentsView): class ModuleTypeComponentsView(generic.ObjectChildrenView):
queryset = ModuleType.objects.all() queryset = ModuleType.objects.all()
actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete)
template_name = 'dcim/moduletype/component_templates.html' template_name = 'dcim/moduletype/component_templates.html'
viewname = None # Used for return_url resolution viewname = None # Used for return_url resolution
@ -2157,7 +2151,7 @@ class DeviceConsolePortsView(DeviceComponentsView):
table = tables.DeviceConsolePortTable table = tables.DeviceConsolePortTable
filterset = filtersets.ConsolePortFilterSet filterset = filtersets.ConsolePortFilterSet
filterset_form = forms.ConsolePortFilterForm filterset_form = forms.ConsolePortFilterForm
template_name = 'dcim/device/consoleports.html', actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete, BulkDisconnect)
tab = ViewTab( tab = ViewTab(
label=_('Console Ports'), label=_('Console Ports'),
badge=lambda obj: obj.console_port_count, badge=lambda obj: obj.console_port_count,
@ -2173,7 +2167,7 @@ class DeviceConsoleServerPortsView(DeviceComponentsView):
table = tables.DeviceConsoleServerPortTable table = tables.DeviceConsoleServerPortTable
filterset = filtersets.ConsoleServerPortFilterSet filterset = filtersets.ConsoleServerPortFilterSet
filterset_form = forms.ConsoleServerPortFilterForm filterset_form = forms.ConsoleServerPortFilterForm
template_name = 'dcim/device/consoleserverports.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete, BulkDisconnect)
tab = ViewTab( tab = ViewTab(
label=_('Console Server Ports'), label=_('Console Server Ports'),
badge=lambda obj: obj.console_server_port_count, badge=lambda obj: obj.console_server_port_count,
@ -2189,7 +2183,7 @@ class DevicePowerPortsView(DeviceComponentsView):
table = tables.DevicePowerPortTable table = tables.DevicePowerPortTable
filterset = filtersets.PowerPortFilterSet filterset = filtersets.PowerPortFilterSet
filterset_form = forms.PowerPortFilterForm filterset_form = forms.PowerPortFilterForm
template_name = 'dcim/device/powerports.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete, BulkDisconnect)
tab = ViewTab( tab = ViewTab(
label=_('Power Ports'), label=_('Power Ports'),
badge=lambda obj: obj.power_port_count, badge=lambda obj: obj.power_port_count,
@ -2205,7 +2199,7 @@ class DevicePowerOutletsView(DeviceComponentsView):
table = tables.DevicePowerOutletTable table = tables.DevicePowerOutletTable
filterset = filtersets.PowerOutletFilterSet filterset = filtersets.PowerOutletFilterSet
filterset_form = forms.PowerOutletFilterForm filterset_form = forms.PowerOutletFilterForm
template_name = 'dcim/device/poweroutlets.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete, BulkDisconnect)
tab = ViewTab( tab = ViewTab(
label=_('Power Outlets'), label=_('Power Outlets'),
badge=lambda obj: obj.power_outlet_count, badge=lambda obj: obj.power_outlet_count,
@ -2221,6 +2215,7 @@ class DeviceInterfacesView(DeviceComponentsView):
table = tables.DeviceInterfaceTable table = tables.DeviceInterfaceTable
filterset = filtersets.InterfaceFilterSet filterset = filtersets.InterfaceFilterSet
filterset_form = forms.InterfaceFilterForm filterset_form = forms.InterfaceFilterForm
actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete, BulkDisconnect)
template_name = 'dcim/device/interfaces.html' template_name = 'dcim/device/interfaces.html'
tab = ViewTab( tab = ViewTab(
label=_('Interfaces'), label=_('Interfaces'),
@ -2243,7 +2238,7 @@ class DeviceFrontPortsView(DeviceComponentsView):
table = tables.DeviceFrontPortTable table = tables.DeviceFrontPortTable
filterset = filtersets.FrontPortFilterSet filterset = filtersets.FrontPortFilterSet
filterset_form = forms.FrontPortFilterForm filterset_form = forms.FrontPortFilterForm
template_name = 'dcim/device/frontports.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete, BulkDisconnect)
tab = ViewTab( tab = ViewTab(
label=_('Front Ports'), label=_('Front Ports'),
badge=lambda obj: obj.front_port_count, badge=lambda obj: obj.front_port_count,
@ -2259,7 +2254,7 @@ class DeviceRearPortsView(DeviceComponentsView):
table = tables.DeviceRearPortTable table = tables.DeviceRearPortTable
filterset = filtersets.RearPortFilterSet filterset = filtersets.RearPortFilterSet
filterset_form = forms.RearPortFilterForm filterset_form = forms.RearPortFilterForm
template_name = 'dcim/device/rearports.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete, BulkDisconnect)
tab = ViewTab( tab = ViewTab(
label=_('Rear Ports'), label=_('Rear Ports'),
badge=lambda obj: obj.rear_port_count, badge=lambda obj: obj.rear_port_count,
@ -2275,11 +2270,7 @@ class DeviceModuleBaysView(DeviceComponentsView):
table = tables.DeviceModuleBayTable table = tables.DeviceModuleBayTable
filterset = filtersets.ModuleBayFilterSet filterset = filtersets.ModuleBayFilterSet
filterset_form = forms.ModuleBayFilterForm filterset_form = forms.ModuleBayFilterForm
template_name = 'dcim/device/modulebays.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
tab = ViewTab( tab = ViewTab(
label=_('Module Bays'), label=_('Module Bays'),
badge=lambda obj: obj.module_bay_count, badge=lambda obj: obj.module_bay_count,
@ -2295,11 +2286,7 @@ class DeviceDeviceBaysView(DeviceComponentsView):
table = tables.DeviceDeviceBayTable table = tables.DeviceDeviceBayTable
filterset = filtersets.DeviceBayFilterSet filterset = filtersets.DeviceBayFilterSet
filterset_form = forms.DeviceBayFilterForm filterset_form = forms.DeviceBayFilterForm
template_name = 'dcim/device/devicebays.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
tab = ViewTab( tab = ViewTab(
label=_('Device Bays'), label=_('Device Bays'),
badge=lambda obj: obj.device_bay_count, badge=lambda obj: obj.device_bay_count,
@ -2315,11 +2302,7 @@ class DeviceInventoryView(DeviceComponentsView):
table = tables.DeviceInventoryItemTable table = tables.DeviceInventoryItemTable
filterset = filtersets.InventoryItemFilterSet filterset = filtersets.InventoryItemFilterSet
filterset_form = forms.InventoryItemFilterForm filterset_form = forms.InventoryItemFilterForm
template_name = 'dcim/device/inventory.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
tab = ViewTab( tab = ViewTab(
label=_('Inventory Items'), label=_('Inventory Items'),
badge=lambda obj: obj.inventory_item_count, badge=lambda obj: obj.inventory_item_count,
@ -2472,11 +2455,7 @@ class ConsolePortListView(generic.ObjectListView):
filterset = filtersets.ConsolePortFilterSet filterset = filtersets.ConsolePortFilterSet
filterset_form = forms.ConsolePortFilterForm filterset_form = forms.ConsolePortFilterForm
table = tables.ConsolePortTable table = tables.ConsolePortTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(ConsolePort) @register_model_view(ConsolePort)
@ -2547,11 +2526,7 @@ class ConsoleServerPortListView(generic.ObjectListView):
filterset = filtersets.ConsoleServerPortFilterSet filterset = filtersets.ConsoleServerPortFilterSet
filterset_form = forms.ConsoleServerPortFilterForm filterset_form = forms.ConsoleServerPortFilterForm
table = tables.ConsoleServerPortTable table = tables.ConsoleServerPortTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(ConsoleServerPort) @register_model_view(ConsoleServerPort)
@ -2622,11 +2597,7 @@ class PowerPortListView(generic.ObjectListView):
filterset = filtersets.PowerPortFilterSet filterset = filtersets.PowerPortFilterSet
filterset_form = forms.PowerPortFilterForm filterset_form = forms.PowerPortFilterForm
table = tables.PowerPortTable table = tables.PowerPortTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(PowerPort) @register_model_view(PowerPort)
@ -2697,11 +2668,7 @@ class PowerOutletListView(generic.ObjectListView):
filterset = filtersets.PowerOutletFilterSet filterset = filtersets.PowerOutletFilterSet
filterset_form = forms.PowerOutletFilterForm filterset_form = forms.PowerOutletFilterForm
table = tables.PowerOutletTable table = tables.PowerOutletTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(PowerOutlet) @register_model_view(PowerOutlet)
@ -2772,11 +2739,7 @@ class InterfaceListView(generic.ObjectListView):
filterset = filtersets.InterfaceFilterSet filterset = filtersets.InterfaceFilterSet
filterset_form = forms.InterfaceFilterForm filterset_form = forms.InterfaceFilterForm
table = tables.InterfaceTable table = tables.InterfaceTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(Interface) @register_model_view(Interface)
@ -2920,11 +2883,7 @@ class FrontPortListView(generic.ObjectListView):
filterset = filtersets.FrontPortFilterSet filterset = filtersets.FrontPortFilterSet
filterset_form = forms.FrontPortFilterForm filterset_form = forms.FrontPortFilterForm
table = tables.FrontPortTable table = tables.FrontPortTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(FrontPort) @register_model_view(FrontPort)
@ -2995,11 +2954,7 @@ class RearPortListView(generic.ObjectListView):
filterset = filtersets.RearPortFilterSet filterset = filtersets.RearPortFilterSet
filterset_form = forms.RearPortFilterForm filterset_form = forms.RearPortFilterForm
table = tables.RearPortTable table = tables.RearPortTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(RearPort) @register_model_view(RearPort)
@ -3070,11 +3025,7 @@ class ModuleBayListView(generic.ObjectListView):
filterset = filtersets.ModuleBayFilterSet filterset = filtersets.ModuleBayFilterSet
filterset_form = forms.ModuleBayFilterForm filterset_form = forms.ModuleBayFilterForm
table = tables.ModuleBayTable table = tables.ModuleBayTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(ModuleBay) @register_model_view(ModuleBay)
@ -3136,11 +3087,7 @@ class DeviceBayListView(generic.ObjectListView):
filterset = filtersets.DeviceBayFilterSet filterset = filtersets.DeviceBayFilterSet
filterset_form = forms.DeviceBayFilterForm filterset_form = forms.DeviceBayFilterForm
table = tables.DeviceBayTable table = tables.DeviceBayTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(DeviceBay) @register_model_view(DeviceBay)
@ -3283,11 +3230,7 @@ class InventoryItemListView(generic.ObjectListView):
filterset = filtersets.InventoryItemFilterSet filterset = filtersets.InventoryItemFilterSet
filterset_form = forms.InventoryItemFilterForm filterset_form = forms.InventoryItemFilterForm
table = tables.InventoryItemTable table = tables.InventoryItemTable
template_name = 'dcim/component_list.html' actions = (Add, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
@register_model_view(InventoryItem) @register_model_view(InventoryItem)
@ -3627,9 +3570,7 @@ class ConsoleConnectionsListView(generic.ObjectListView):
filterset_form = forms.ConsoleConnectionFilterForm filterset_form = forms.ConsoleConnectionFilterForm
table = tables.ConsoleConnectionTable table = tables.ConsoleConnectionTable
template_name = 'dcim/connections_list.html' template_name = 'dcim/connections_list.html'
actions = { actions = (BulkExport,)
'export': {'view'},
}
def get_extra_context(self, request): def get_extra_context(self, request):
return { return {
@ -3643,9 +3584,7 @@ class PowerConnectionsListView(generic.ObjectListView):
filterset_form = forms.PowerConnectionFilterForm filterset_form = forms.PowerConnectionFilterForm
table = tables.PowerConnectionTable table = tables.PowerConnectionTable
template_name = 'dcim/connections_list.html' template_name = 'dcim/connections_list.html'
actions = { actions = (BulkExport,)
'export': {'view'},
}
def get_extra_context(self, request): def get_extra_context(self, request):
return { return {
@ -3659,9 +3598,7 @@ class InterfaceConnectionsListView(generic.ObjectListView):
filterset_form = forms.InterfaceConnectionFilterForm filterset_form = forms.InterfaceConnectionFilterForm
table = tables.InterfaceConnectionTable table = tables.InterfaceConnectionTable
template_name = 'dcim/connections_list.html' template_name = 'dcim/connections_list.html'
actions = { actions = (BulkExport,)
'export': {'view'},
}
def get_extra_context(self, request): def get_extra_context(self, request):
return { return {

View File

@ -14,12 +14,13 @@ from jinja2.exceptions import TemplateError
from core.choices import ManagedFileRootPathChoices from core.choices import ManagedFileRootPathChoices
from core.models import Job from core.models import Job
from core.object_actions import BulkSync
from dcim.models import Device, DeviceRole, Platform from dcim.models import Device, DeviceRole, Platform
from extras.choices import LogLevelChoices from extras.choices import LogLevelChoices
from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
from extras.dashboard.utils import get_widget_class from extras.dashboard.utils import get_widget_class
from extras.utils import SharedObjectViewMixin 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 import generic
from netbox.views.generic.mixins import TableMixin from netbox.views.generic.mixins import TableMixin
from utilities.forms import ConfirmationForm, get_field_value from utilities.forms import ConfirmationForm, get_field_value
@ -232,11 +233,7 @@ class ExportTemplateListView(generic.ObjectListView):
filterset = filtersets.ExportTemplateFilterSet filterset = filtersets.ExportTemplateFilterSet
filterset_form = forms.ExportTemplateFilterForm filterset_form = forms.ExportTemplateFilterForm
table = tables.ExportTemplateTable table = tables.ExportTemplateTable
template_name = 'extras/exporttemplate_list.html' actions = (Add, BulkImport, BulkSync, BulkEdit, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_sync': {'sync'},
}
@register_model_view(ExportTemplate) @register_model_view(ExportTemplate)
@ -347,9 +344,7 @@ class TableConfigListView(SharedObjectViewMixin, generic.ObjectListView):
filterset = filtersets.TableConfigFilterSet filterset = filtersets.TableConfigFilterSet
filterset_form = forms.TableConfigFilterForm filterset_form = forms.TableConfigFilterForm
table = tables.TableConfigTable table = tables.TableConfigTable
actions = { actions = (BulkExport,)
'export': {'view'},
}
@register_model_view(TableConfig) @register_model_view(TableConfig)
@ -759,12 +754,7 @@ class ConfigContextListView(generic.ObjectListView):
filterset_form = forms.ConfigContextFilterForm filterset_form = forms.ConfigContextFilterForm
table = tables.ConfigContextTable table = tables.ConfigContextTable
template_name = 'extras/configcontext_list.html' template_name = 'extras/configcontext_list.html'
actions = { actions = (Add, BulkSync, BulkEdit, BulkDelete)
'add': {'add'},
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
'bulk_sync': {'sync'},
}
@register_model_view(ConfigContext) @register_model_view(ConfigContext)
@ -877,11 +867,7 @@ class ConfigTemplateListView(generic.ObjectListView):
filterset = filtersets.ConfigTemplateFilterSet filterset = filtersets.ConfigTemplateFilterSet
filterset_form = forms.ConfigTemplateFilterForm filterset_form = forms.ConfigTemplateFilterForm
table = tables.ConfigTemplateTable table = tables.ConfigTemplateTable
template_name = 'extras/configtemplate_list.html' actions = (Add, BulkImport, BulkSync, BulkEdit, BulkExport, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_sync': {'sync'},
}
@register_model_view(ConfigTemplate) @register_model_view(ConfigTemplate)
@ -992,9 +978,7 @@ class ImageAttachmentListView(generic.ObjectListView):
filterset = filtersets.ImageAttachmentFilterSet filterset = filtersets.ImageAttachmentFilterSet
filterset_form = forms.ImageAttachmentFilterForm filterset_form = forms.ImageAttachmentFilterForm
table = tables.ImageAttachmentTable table = tables.ImageAttachmentTable
actions = { actions = (BulkExport,)
'export': {'view'},
}
@register_model_view(ImageAttachment, 'add', detail=False) @register_model_view(ImageAttachment, 'add', detail=False)
@ -1038,12 +1022,7 @@ class JournalEntryListView(generic.ObjectListView):
filterset = filtersets.JournalEntryFilterSet filterset = filtersets.JournalEntryFilterSet
filterset_form = forms.JournalEntryFilterForm filterset_form = forms.JournalEntryFilterForm
table = tables.JournalEntryTable table = tables.JournalEntryTable
actions = { actions = (BulkImport, BulkSync, BulkEdit, BulkDelete)
'export': {'view'},
'bulk_import': {'add'},
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
}
@register_model_view(JournalEntry) @register_model_view(JournalEntry)

View File

@ -28,7 +28,8 @@ ADVISORY_LOCK_KEYS = {
'job-schedules': 110100, 'job-schedules': 110100,
} }
# Default view action permission mapping # TODO: Remove in NetBox v4.6
# Legacy default view action permission mapping
DEFAULT_ACTION_PERMISSIONS = { DEFAULT_ACTION_PERMISSIONS = {
'add': {'add'}, 'add': {'add'},
'export': {'view'}, 'export': {'view'},

View File

@ -10,6 +10,7 @@ __all__ = (
'BulkEdit', 'BulkEdit',
'BulkExport', 'BulkExport',
'BulkImport', 'BulkImport',
'BulkRename',
'Delete', 'Delete',
'Edit', 'Edit',
'ObjectAction', 'ObjectAction',
@ -23,11 +24,13 @@ class ObjectAction:
permissions_required = set() permissions_required = set()
url_kwargs = [] url_kwargs = []
def get_context(self, context, obj): @classmethod
viewname = f'{obj._meta.app_label}:{obj._meta.model_name}_{self.name}' def get_context(cls, context, obj):
url = reverse(viewname, kwargs={kwarg: getattr(obj, kwarg) for kwarg in self.url_kwargs}) viewname = f'{obj._meta.app_label}:{obj._meta.model_name}_{cls.name}'
url = reverse(viewname, kwargs={kwarg: getattr(obj, kwarg) for kwarg in cls.url_kwargs})
return { return {
'url': url, 'url': url,
'label': cls.label,
} }
@ -106,18 +109,29 @@ class BulkEdit(ObjectAction):
Change the value of one or more fields on a set of objects. Change the value of one or more fields on a set of objects.
""" """
name = 'bulk_edit' name = 'bulk_edit'
label = _('Edit') label = _('Edit Selected')
bulk = True bulk = True
permissions_required = {'change'} permissions_required = {'change'}
template_name = 'buttons/bulk_edit.html' template_name = 'buttons/bulk_edit.html'
class BulkRename(ObjectAction):
"""
Rename multiple objects at once.
"""
name = 'bulk_rename'
label = _('Rename Selected')
bulk = True
permissions_required = {'change'}
template_name = 'buttons/bulk_rename.html'
class BulkDelete(ObjectAction): class BulkDelete(ObjectAction):
""" """
Delete each of a set of objects. Delete each of a set of objects.
""" """
name = 'bulk_delete' name = 'bulk_delete'
label = _('Delete') label = _('Delete Selected')
bulk = True bulk = True
permissions_required = {'delete'} permissions_required = {'delete'}
template_name = 'buttons/bulk_delete.html' template_name = 'buttons/bulk_delete.html'

View File

@ -1,6 +1,7 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from extras.models import TableConfig from extras.models import TableConfig
from netbox import object_actions
from utilities.permissions import get_permission_for_model from utilities.permissions import get_permission_for_model
__all__ = ( __all__ = (
@ -18,7 +19,27 @@ class ActionsMixin:
Standard actions include: add, import, export, bulk_edit, and bulk_delete. Some views extend this default map Standard actions include: add, import, export, bulk_edit, and bulk_delete. Some views extend this default map
with custom actions, such as bulk_sync. with custom actions, such as bulk_sync.
""" """
# actions = DEFAULT_ACTION_PERMISSIONS
# TODO: Remove in NetBox v4.6
@staticmethod
def _get_legacy_action(name):
"""
Given a legacy action name, return the corresponding action class.
"""
action = {
'add': object_actions.Add,
'edit': object_actions.Edit,
'delete': object_actions.Delete,
'export': object_actions.BulkExport,
'bulk_import': object_actions.BulkImport,
'bulk_edit': object_actions.BulkEdit,
'bulk_rename': object_actions.BulkRename,
'bulk_delete': object_actions.BulkDelete,
}.get(name)
if name is None:
raise ValueError(f"Unknown action: {action}")
return action
def get_permitted_actions(self, user, model=None): def get_permitted_actions(self, user, model=None):
""" """
@ -29,7 +50,8 @@ class ActionsMixin:
# Resolve required permissions for each action # Resolve required permissions for each action
permitted_actions = [] permitted_actions = []
for action in self.actions: for action in self.actions:
perms = action if type(action) is str else action.permissions_required # Backward compatibility # Backward compatibility
perms = self._get_legacy_action(action) if type(action) is str else action.permissions_required
required_permissions = [ required_permissions = [
get_permission_for_model(model, perm) for perm in perms get_permission_for_model(model, perm) for perm in perms
] ]

View File

@ -143,7 +143,8 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin):
# Determine the available actions # Determine the available actions
actions = self.get_permitted_actions(request.user, model=self.child_model) actions = self.get_permitted_actions(request.user, model=self.child_model)
has_bulk_actions = any([a.startswith('bulk_') for a in actions]) # has_bulk_actions = any([a.startswith('bulk_') for a in actions])
has_bulk_actions = True
table_data = self.prep_table_data(request, child_objects, instance) 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_bulk_actions)

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -1,30 +1,5 @@
{% extends 'dcim/device/components_base.html' %} {% extends 'generic/object_children.html' %}
{% load helpers %}
{% load i18n %}
{% block table_controls %} {% 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 %} {% 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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -3,23 +3,23 @@
{% load i18n %} {% load i18n %}
{% load perms %} {% load perms %}
{% block bulk_edit_controls %} {#{% block bulk_edit_controls %}#}
{% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %} {# {% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %}#}
{% if 'bulk_edit' in actions and bulk_edit_view %} {# {% if 'bulk_edit' in actions and bulk_edit_view %}#}
<button type="submit" name="_edit" {# <button type="submit" name="_edit"#}
{% formaction %}="{% url bulk_edit_view %}?device={{ object.pk }}&return_url={{ return_url }}" {# {% formaction %}="{% url bulk_edit_view %}?device={{ object.pk }}&return_url={{ return_url }}"#}
class="btn btn-warning"> {# class="btn btn-warning">#}
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected {# <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected#}
</button> {# </button>#}
{% endif %} {# {% endif %}#}
{% endwith %} {# {% endwith %}#}
{% with bulk_rename_view=child_model|validated_viewname:"bulk_rename" %} {# {% with bulk_rename_view=child_model|validated_viewname:"bulk_rename" %}#}
{% if 'bulk_rename' in actions and bulk_rename_view %} {# {% if 'bulk_rename' in actions and bulk_rename_view %}#}
<button type="submit" name="_rename" {# <button type="submit" name="_rename"#}
{% formaction %}="{% url bulk_rename_view %}?return_url={{ return_url }}" {# {% formaction %}="{% url bulk_rename_view %}?return_url={{ return_url }}"#}
class="btn btn-outline-warning"> {# class="btn btn-outline-warning">#}
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename Selected {# <i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename Selected#}
</button> {# </button>#}
{% endif %} {# {% endif %}#}
{% endwith %} {# {% endwith %}#}
{% endblock bulk_edit_controls %} {#{% endblock bulk_edit_controls %}#}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -1,4 +1,5 @@
{% extends base_template %} {% extends base_template %}
{% load buttons %}
{% load helpers %} {% load helpers %}
{% load i18n %} {% load i18n %}
@ -36,34 +37,37 @@ Context:
</div> </div>
<div class="d-print-none mt-2"> <div class="d-print-none mt-2">
{% block bulk_controls %} {% block bulk_controls %}
<div class="btn-group" role="group"> {% for name, action in actions.items %}
{% bulk_action_button action model %}
{% endfor %}
{# <div class="btn-group" role="group">#}
{# Bulk edit buttons #} {# Bulk edit buttons #}
{% block bulk_edit_controls %} {# {% block bulk_edit_controls %}#}
{% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %} {# {% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %}#}
{% if 'bulk_edit' in actions and bulk_edit_view %} {# {% if 'bulk_edit' in actions and bulk_edit_view %}#}
<button type="submit" name="_edit" {# <button type="submit" name="_edit"#}
{% formaction %}="{% url bulk_edit_view %}?return_url={{ return_url }}" {# {% formaction %}="{% url bulk_edit_view %}?return_url={{ return_url }}"#}
class="btn btn-warning"> {# class="btn btn-warning">#}
<i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit Selected" %} {# <i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit Selected" %}#}
</button> {# </button>#}
{% endif %} {# {% endif %}#}
{% endwith %} {# {% endwith %}#}
{% endblock bulk_edit_controls %} {# {% endblock bulk_edit_controls %}#}
</div> {# </div>#}
<div class="btn-group" role="group"> {# <div class="btn-group" role="group">#}
{# Bulk delete buttons #} {# Bulk delete buttons #}
{% block bulk_delete_controls %} {# {% block bulk_delete_controls %}#}
{% with bulk_delete_view=child_model|validated_viewname:"bulk_delete" %} {# {% with bulk_delete_view=child_model|validated_viewname:"bulk_delete" %}#}
{% if 'bulk_delete' in actions and bulk_delete_view %} {# {% if 'bulk_delete' in actions and bulk_delete_view %}#}
<button type="submit" {# <button type="submit"#}
{% formaction %}="{% url bulk_delete_view %}?return_url={{ return_url }}" {# {% formaction %}="{% url bulk_delete_view %}?return_url={{ return_url }}"#}
class="btn btn-danger"> {# class="btn btn-danger">#}
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete Selected" %} {# <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete Selected" %}#}
</button> {# </button>#}
{% endif %} {# {% endif %}#}
{% endwith %} {# {% endwith %}#}
{% endblock bulk_delete_controls %} {# {% endblock bulk_delete_controls %}#}
</div> {# </div>#}
{# Other bulk action buttons #} {# Other bulk action buttons #}
{% block bulk_extra_controls %}{% endblock %} {% block bulk_extra_controls %}{% endblock %}
{% endblock bulk_controls %} {% endblock bulk_controls %}

View File

@ -88,9 +88,7 @@ Context:
</div> </div>
<div class="bulk-action-buttons"> <div class="bulk-action-buttons">
{% for name, action in actions.items %} {% for name, action in actions.items %}
{% if action.bulk %} {% bulk_action_button action model %}
{% bulk_action_button action model %}
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
@ -120,9 +118,7 @@ Context:
{% block bulk_buttons %} {% block bulk_buttons %}
<div class="bulk-action-buttons"> <div class="bulk-action-buttons">
{% for name, action in actions.items %} {% for name, action in actions.items %}
{% if action.bulk %} {% bulk_action_button action model %}
{% bulk_action_button action model %}
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -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 %}

View File

@ -1,6 +1,7 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from netbox.object_actions import BulkDelete, BulkEdit, BulkExport, BulkImport
from netbox.views import generic from netbox.views import generic
from utilities.query import count_related from utilities.query import count_related
from utilities.views import GetRelatedModelsMixin, register_model_view from utilities.views import GetRelatedModelsMixin, register_model_view
@ -349,12 +350,7 @@ class ContactAssignmentListView(generic.ObjectListView):
filterset = filtersets.ContactAssignmentFilterSet filterset = filtersets.ContactAssignmentFilterSet
filterset_form = forms.ContactAssignmentFilterForm filterset_form = forms.ContactAssignmentFilterForm
table = tables.ContactAssignmentTable table = tables.ContactAssignmentTable
actions = { actions = (BulkExport, BulkImport, BulkEdit, BulkDelete)
'export': {'view'},
'bulk_import': {'add'},
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
}
@register_model_view(ContactAssignment, 'add', detail=False) @register_model_view(ContactAssignment, 'add', detail=False)

View File

@ -0,0 +1,6 @@
{% load i18n %}
{% if url %}
<button type="submit" name="_disconnect" {% formaction %}="{{ url }}" class="btn btn-red">
<i class="mdi mdi-ethernet-cable-off" aria-hidden="true"></i> {{ label }}
</button>
{% endif %}

View File

@ -0,0 +1,6 @@
{% load i18n %}
{% if url %}
<button type="submit" name="_remove" {% formaction %}="{{ url }}?return_url={{ return_url }}" class="btn btn-red">
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {{ label }}
</button>
{% endif %}

View File

@ -0,0 +1,6 @@
{% load i18n %}
{% if url %}
<button type="submit" name="_rename" {% formaction %}="{{ url }}" class="btn btn-yellow">
<i class="mdi mdi-pencil" aria-hidden="true"></i> {{ label }}
</button>
{% endif %}

View File

@ -0,0 +1,6 @@
{% load i18n %}
{% if url %}
<button type="submit" name="_sync" {% formaction %}="{{ url }}" class="btn btn-primary">
<i class="mdi mdi-sync" aria-hidden="true"></i> {{ label }}
</button>
{% endif %}

View File

@ -224,9 +224,13 @@ def bulk_delete_button(context, model, action='bulk_delete', query_params=None):
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def action_button(context, action, obj): def action_button(context, action, obj):
if action.bulk:
return ''
return loader.render_to_string(action.template_name, action.get_context(context, obj)) return loader.render_to_string(action.template_name, action.get_context(context, obj))
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def bulk_action_button(context, action, model): def bulk_action_button(context, action, model):
if not action.bulk:
return ''
return loader.render_to_string(action.template_name, action.get_context(context, model)) return loader.render_to_string(action.template_name, action.get_context(context, model))

View File

@ -13,7 +13,7 @@ from dcim.tables import DeviceTable
from extras.views import ObjectConfigContextView, ObjectRenderConfigView from extras.views import ObjectConfigContextView, ObjectRenderConfigView
from ipam.models import IPAddress, VLANGroup from ipam.models import IPAddress, VLANGroup
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.object_actions import *
from netbox.views import generic from netbox.views import generic
from utilities.query import count_related from utilities.query import count_related
from utilities.query_functions import CollateAsChar from utilities.query_functions import CollateAsChar
@ -223,13 +223,7 @@ class ClusterDevicesView(generic.ObjectChildrenView):
filterset = DeviceFilterSet filterset = DeviceFilterSet
filterset_form = DeviceFilterForm filterset_form = DeviceFilterForm
template_name = 'virtualization/cluster/devices.html' template_name = 'virtualization/cluster/devices.html'
actions = { actions = (Add, BulkExport, BulkImport, BulkEdit)
'add': {'add'},
'export': {'view'},
'bulk_import': {'add'},
'bulk_edit': {'change'},
'bulk_remove_devices': {'change'},
}
tab = ViewTab( tab = ViewTab(
label=_('Devices'), label=_('Devices'),
badge=lambda obj: obj.devices.count(), badge=lambda obj: obj.devices.count(),
@ -386,11 +380,7 @@ class VirtualMachineInterfacesView(generic.ObjectChildrenView):
table = tables.VirtualMachineVMInterfaceTable table = tables.VirtualMachineVMInterfaceTable
filterset = filtersets.VMInterfaceFilterSet filterset = filtersets.VMInterfaceFilterSet
filterset_form = forms.VMInterfaceFilterForm filterset_form = forms.VMInterfaceFilterForm
template_name = 'virtualization/virtualmachine/interfaces.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
tab = ViewTab( tab = ViewTab(
label=_('Interfaces'), label=_('Interfaces'),
badge=lambda obj: obj.interface_count, badge=lambda obj: obj.interface_count,
@ -412,17 +402,13 @@ class VirtualMachineVirtualDisksView(generic.ObjectChildrenView):
table = tables.VirtualMachineVirtualDiskTable table = tables.VirtualMachineVirtualDiskTable
filterset = filtersets.VirtualDiskFilterSet filterset = filtersets.VirtualDiskFilterSet
filterset_form = forms.VirtualDiskFilterForm filterset_form = forms.VirtualDiskFilterForm
template_name = 'virtualization/virtualmachine/virtual_disks.html' actions = (Edit, Delete, BulkEdit, BulkRename, BulkDelete)
tab = ViewTab( tab = ViewTab(
label=_('Virtual Disks'), label=_('Virtual Disks'),
badge=lambda obj: obj.virtual_disk_count, badge=lambda obj: obj.virtual_disk_count,
permission='virtualization.view_virtualdisk', permission='virtualization.view_virtualdisk',
weight=500 weight=500
) )
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
def get_children(self, request, parent): def get_children(self, request, parent):
return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags') return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags')