Jelajahi Sumber

Initial work on #7679

jeremystretch 4 tahun lalu
induk
melakukan
58f7eb319f

+ 2 - 2
netbox/circuits/tables.py

@@ -2,7 +2,7 @@ import django_tables2 as tables
 from django_tables2.utils import Accessor
 
 from tenancy.tables import TenantColumn
-from utilities.tables import BaseTable, ButtonsColumn, ChoiceFieldColumn, MarkdownColumn, TagColumn, ToggleColumn
+from utilities.tables import ActionsColumn, BaseTable, ChoiceFieldColumn, MarkdownColumn, TagColumn, ToggleColumn
 from .models import *
 
 
@@ -88,7 +88,7 @@ class CircuitTypeTable(BaseTable):
     circuit_count = tables.Column(
         verbose_name='Circuits'
     )
-    actions = ButtonsColumn(CircuitType)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = CircuitType

+ 4 - 4
netbox/dcim/tables/devices.py

@@ -7,7 +7,7 @@ from dcim.models import (
 )
 from tenancy.tables import TenantColumn
 from utilities.tables import (
-    BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ColoredLabelColumn, LinkedCountColumn,
+    ActionsColumn, BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ColoredLabelColumn, LinkedCountColumn,
     MarkdownColumn, TagColumn, TemplateColumn, ToggleColumn,
 )
 from .template_code import *
@@ -94,7 +94,7 @@ class DeviceRoleTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:devicerole_list'
     )
-    actions = ButtonsColumn(DeviceRole)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = DeviceRole
@@ -127,7 +127,7 @@ class PlatformTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:platform_list'
     )
-    actions = ButtonsColumn(Platform)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = Platform
@@ -839,7 +839,7 @@ class InventoryItemRoleTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:inventoryitemrole_list'
     )
-    actions = ButtonsColumn(InventoryItemRole)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = InventoryItemRole

+ 3 - 2
netbox/dcim/tables/devicetypes.py

@@ -6,7 +6,8 @@ from dcim.models import (
     InventoryItemTemplate, Manufacturer, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate,
 )
 from utilities.tables import (
-    BaseTable, BooleanColumn, ButtonsColumn, ColorColumn, LinkedCountColumn, MarkdownColumn, TagColumn, ToggleColumn,
+    ActionsColumn, BaseTable, BooleanColumn, ButtonsColumn, ColorColumn, LinkedCountColumn, MarkdownColumn, TagColumn,
+    ToggleColumn,
 )
 from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS
 
@@ -48,7 +49,7 @@ class ManufacturerTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:manufacturer_list'
     )
-    actions = ButtonsColumn(Manufacturer)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = Manufacturer

+ 3 - 3
netbox/dcim/tables/racks.py

@@ -4,7 +4,7 @@ from django_tables2.utils import Accessor
 from dcim.models import Rack, RackReservation, RackRole
 from tenancy.tables import TenantColumn
 from utilities.tables import (
-    BaseTable, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ColoredLabelColumn, LinkedCountColumn, MarkdownColumn,
+    ActionsColumn, BaseTable, ChoiceFieldColumn, ColorColumn, ColoredLabelColumn, LinkedCountColumn, MarkdownColumn,
     TagColumn, ToggleColumn, UtilizationColumn,
 )
 
@@ -27,7 +27,7 @@ class RackRoleTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:rackrole_list'
     )
-    actions = ButtonsColumn(RackRole)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = RackRole
@@ -121,7 +121,7 @@ class RackReservationTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:rackreservation_list'
     )
-    actions = ButtonsColumn(RackReservation)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = RackReservation

+ 4 - 3
netbox/dcim/tables/sites.py

@@ -3,7 +3,8 @@ import django_tables2 as tables
 from dcim.models import Location, Region, Site, SiteGroup
 from tenancy.tables import TenantColumn
 from utilities.tables import (
-    BaseTable, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, MarkdownColumn, MPTTColumn, TagColumn, ToggleColumn,
+    ActionsColumn, BaseTable, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, MarkdownColumn, MPTTColumn,
+    TagColumn, ToggleColumn,
 )
 from .template_code import LOCATION_ELEVATIONS
 
@@ -32,7 +33,7 @@ class RegionTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:region_list'
     )
-    actions = ButtonsColumn(Region)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = Region
@@ -57,7 +58,7 @@ class SiteGroupTable(BaseTable):
     tags = TagColumn(
         url_name='dcim:sitegroup_list'
     )
-    actions = ButtonsColumn(SiteGroup)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = SiteGroup

+ 3 - 5
netbox/extras/tables.py

@@ -2,7 +2,7 @@ import django_tables2 as tables
 from django.conf import settings
 
 from utilities.tables import (
-    BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ContentTypeColumn, ContentTypesColumn,
+    ActionsColumn, BaseTable, BooleanColumn, ChoiceFieldColumn, ColorColumn, ContentTypeColumn, ContentTypesColumn,
     MarkdownColumn, ToggleColumn,
 )
 from .models import *
@@ -152,7 +152,7 @@ class TagTable(BaseTable):
         linkify=True
     )
     color = ColorColumn()
-    actions = ButtonsColumn(Tag)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = Tag
@@ -233,9 +233,7 @@ class ObjectJournalTable(BaseTable):
     comments = tables.TemplateColumn(
         template_code='{% load helpers %}{{ value|render_markdown|truncatewords_html:50 }}'
     )
-    actions = ButtonsColumn(
-        model=JournalEntry
-    )
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = JournalEntry

+ 3 - 4
netbox/ipam/tables/fhrp.py

@@ -1,6 +1,6 @@
 import django_tables2 as tables
 
-from utilities.tables import BaseTable, ButtonsColumn, MarkdownColumn, TagColumn, ToggleColumn
+from utilities.tables import ActionsColumn, BaseTable, MarkdownColumn, TagColumn, ToggleColumn
 from ipam.models import *
 
 __all__ = (
@@ -58,9 +58,8 @@ class FHRPGroupAssignmentTable(BaseTable):
     group = tables.Column(
         linkify=True
     )
-    actions = ButtonsColumn(
-        model=FHRPGroupAssignment,
-        buttons=('edit', 'delete', 'foo')
+    actions = ActionsColumn(
+        actions=('edit', 'delete')
     )
 
     class Meta(BaseTable.Meta):

+ 6 - 8
netbox/ipam/tables/ip.py

@@ -4,8 +4,8 @@ from django_tables2.utils import Accessor
 
 from tenancy.tables import TenantColumn
 from utilities.tables import (
-    BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, TagColumn,
-    ToggleColumn, UtilizationColumn,
+    ActionsColumn, BaseTable, BooleanColumn, ChoiceFieldColumn, LinkedCountColumn, TagColumn, ToggleColumn,
+    UtilizationColumn,
 )
 from ipam.models import *
 
@@ -89,7 +89,7 @@ class RIRTable(BaseTable):
     tags = TagColumn(
         url_name='ipam:rir_list'
     )
-    actions = ButtonsColumn(RIR)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = RIR
@@ -111,7 +111,7 @@ class ASNTable(BaseTable):
         url_params={'asn_id': 'pk'},
         verbose_name='Sites'
     )
-    actions = ButtonsColumn(ASN)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = ASN
@@ -173,7 +173,7 @@ class RoleTable(BaseTable):
     tags = TagColumn(
         url_name='ipam:role_list'
     )
-    actions = ButtonsColumn(Role)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = Role
@@ -405,9 +405,7 @@ class AssignedIPAddressesTable(BaseTable):
     )
     status = ChoiceFieldColumn()
     tenant = TenantColumn()
-    actions = ButtonsColumn(
-        model=IPAddress
-    )
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = IPAddress

+ 8 - 4
netbox/ipam/tables/vlans.py

@@ -5,8 +5,8 @@ from django_tables2.utils import Accessor
 from dcim.models import Interface
 from tenancy.tables import TenantColumn
 from utilities.tables import (
-    BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ContentTypeColumn, LinkedCountColumn, TagColumn,
-    TemplateColumn, ToggleColumn,
+    ActionsColumn, BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ContentTypeColumn, LinkedCountColumn,
+    TagColumn, TemplateColumn, ToggleColumn,
 )
 from virtualization.models import VMInterface
 from ipam.models import *
@@ -153,7 +153,9 @@ class VLANDevicesTable(VLANMembersTable):
     device = tables.Column(
         linkify=True
     )
-    actions = ButtonsColumn(Interface, buttons=['edit'])
+    actions = ActionsColumn(
+        actions=('edit',)
+    )
 
     class Meta(BaseTable.Meta):
         model = Interface
@@ -165,7 +167,9 @@ class VLANVirtualMachinesTable(VLANMembersTable):
     virtual_machine = tables.Column(
         linkify=True
     )
-    actions = ButtonsColumn(VMInterface, buttons=['edit'])
+    actions = ActionsColumn(
+        actions=('edit',)
+    )
 
     class Meta(BaseTable.Meta):
         model = VMInterface

+ 6 - 7
netbox/tenancy/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 
 from utilities.tables import (
-    BaseTable, ButtonsColumn, ContentTypeColumn, LinkedCountColumn, linkify_phone, MarkdownColumn, MPTTColumn,
+    ActionsColumn, BaseTable, ContentTypeColumn, LinkedCountColumn, linkify_phone, MarkdownColumn, MPTTColumn,
     TagColumn, ToggleColumn,
 )
 from .models import *
@@ -59,7 +59,7 @@ class TenantGroupTable(BaseTable):
     tags = TagColumn(
         url_name='tenancy:tenantgroup_list'
     )
-    actions = ButtonsColumn(TenantGroup)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = TenantGroup
@@ -103,7 +103,7 @@ class ContactGroupTable(BaseTable):
     tags = TagColumn(
         url_name='tenancy:contactgroup_list'
     )
-    actions = ButtonsColumn(ContactGroup)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = ContactGroup
@@ -116,7 +116,7 @@ class ContactRoleTable(BaseTable):
     name = tables.Column(
         linkify=True
     )
-    actions = ButtonsColumn(ContactRole)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = ContactRole
@@ -164,9 +164,8 @@ class ContactAssignmentTable(BaseTable):
     role = tables.Column(
         linkify=True
     )
-    actions = ButtonsColumn(
-        model=ContactAssignment,
-        buttons=('edit', 'delete')
+    actions = ActionsColumn(
+        actions=('edit', 'delete')
     )
 
     class Meta(BaseTable.Meta):

+ 48 - 0
netbox/utilities/tables.py

@@ -1,3 +1,5 @@
+from collections import namedtuple
+
 import django_tables2 as tables
 from django.conf import settings
 from django.contrib.auth.models import AnonymousUser
@@ -205,6 +207,52 @@ class TemplateColumn(tables.TemplateColumn):
         return ret
 
 
+ActionsMenuItem = namedtuple('ActionsMenuItem', ['title', 'icon'])
+
+
+class ActionsColumn(tables.Column):
+    attrs = {'td': {'class': 'text-end noprint'}}
+    empty_values = ()
+    _actions = {
+        'edit': ActionsMenuItem('Edit', 'pencil'),
+        'delete': ActionsMenuItem('Delete', 'trash-can-outline'),
+        'changelog': ActionsMenuItem('Changelog', 'history'),
+    }
+
+    def __init__(self, *args, actions=('edit', 'delete', 'changelog'), **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Determine which actions to enable
+        self.actions = {
+            name: self._actions[name] for name in actions
+        }
+
+    def header(self):
+        return ''
+
+    def render(self, record, table, **kwargs):
+        if not self.actions:
+            return ''
+
+        model = table.Meta.model
+        viewname_base = f'{model._meta.app_label}:{model._meta.model_name}'
+        request = getattr(table, 'context', {}).get('request')
+        url_appendix = f'?return_url={request.path}' if request else ''
+
+        menu = '<div class="dropdown">' \
+               '<a class="btn btn-sm btn-outline-secondary dropdown-toggle" href="#" type="button" data-bs-toggle="dropdown"><i class="mdi mdi-wrench"></i></a>' \
+               '<ul class="dropdown-menu">'
+
+        for action, attrs in self.actions.items():
+            viewname = f'{viewname_base}_{action}'
+            url = reverse(viewname, kwargs={'pk': record.pk})
+            menu += f'<li><a class="dropdown-item" href="{url}{url_appendix}"><i class="mdi mdi-{attrs.icon}"></i> {attrs.title}</a></li>'
+
+        menu += '</ul></div>'
+
+        return mark_safe(menu)
+
+
 class ButtonsColumn(tables.TemplateColumn):
     """
     Render edit, delete, and changelog buttons for an object.

+ 4 - 4
netbox/virtualization/tables.py

@@ -2,8 +2,8 @@ import django_tables2 as tables
 from dcim.tables.devices import BaseInterfaceTable
 from tenancy.tables import TenantColumn
 from utilities.tables import (
-    BaseTable, ButtonsColumn, ChoiceFieldColumn, ColoredLabelColumn, LinkedCountColumn, MarkdownColumn, TagColumn,
-    ToggleColumn,
+    ActionsColumn, BaseTable, ButtonsColumn, ChoiceFieldColumn, ColoredLabelColumn, LinkedCountColumn, MarkdownColumn,
+    TagColumn, ToggleColumn,
 )
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
 
@@ -40,7 +40,7 @@ class ClusterTypeTable(BaseTable):
     tags = TagColumn(
         url_name='virtualization:clustertype_list'
     )
-    actions = ButtonsColumn(ClusterType)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = ClusterType
@@ -63,7 +63,7 @@ class ClusterGroupTable(BaseTable):
     tags = TagColumn(
         url_name='virtualization:clustergroup_list'
     )
-    actions = ButtonsColumn(ClusterGroup)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = ClusterGroup

+ 2 - 2
netbox/wireless/tables.py

@@ -2,7 +2,7 @@ import django_tables2 as tables
 
 from dcim.models import Interface
 from utilities.tables import (
-    BaseTable, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, MPTTColumn, TagColumn, ToggleColumn,
+    ActionsColumn, BaseTable, ChoiceFieldColumn, LinkedCountColumn, MPTTColumn, TagColumn, ToggleColumn,
 )
 from .models import *
 
@@ -26,7 +26,7 @@ class WirelessLANGroupTable(BaseTable):
     tags = TagColumn(
         url_name='wireless:wirelesslangroup_list'
     )
-    actions = ButtonsColumn(WirelessLANGroup)
+    actions = ActionsColumn()
 
     class Meta(BaseTable.Meta):
         model = WirelessLANGroup