瀏覽代碼

#9072: Add weight parameter to influence ViewTab ordering

jeremystretch 3 年之前
父節點
當前提交
83a0576ca4

+ 32 - 1
netbox/dcim/views.py

@@ -953,6 +953,7 @@ class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
         label=_('Console Ports'),
         badge=lambda obj: obj.consoleporttemplates.count(),
         permission='dcim.view_consoleporttemplate',
+        weight=550,
         hide_if_empty=True
     )
 
@@ -967,6 +968,7 @@ class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
         label=_('Console Server Ports'),
         badge=lambda obj: obj.consoleserverporttemplates.count(),
         permission='dcim.view_consoleserverporttemplate',
+        weight=560,
         hide_if_empty=True
     )
 
@@ -981,6 +983,7 @@ class DeviceTypePowerPortsView(DeviceTypeComponentsView):
         label=_('Power Ports'),
         badge=lambda obj: obj.powerporttemplates.count(),
         permission='dcim.view_powerporttemplate',
+        weight=570,
         hide_if_empty=True
     )
 
@@ -995,6 +998,7 @@ class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
         label=_('Power Outlets'),
         badge=lambda obj: obj.poweroutlettemplates.count(),
         permission='dcim.view_poweroutlettemplate',
+        weight=580,
         hide_if_empty=True
     )
 
@@ -1009,6 +1013,7 @@ class DeviceTypeInterfacesView(DeviceTypeComponentsView):
         label=_('Interfaces'),
         badge=lambda obj: obj.interfacetemplates.count(),
         permission='dcim.view_interfacetemplate',
+        weight=520,
         hide_if_empty=True
     )
 
@@ -1023,6 +1028,7 @@ class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
         label=_('Front Ports'),
         badge=lambda obj: obj.frontporttemplates.count(),
         permission='dcim.view_frontporttemplate',
+        weight=530,
         hide_if_empty=True
     )
 
@@ -1037,6 +1043,7 @@ class DeviceTypeRearPortsView(DeviceTypeComponentsView):
         label=_('Rear Ports'),
         badge=lambda obj: obj.rearporttemplates.count(),
         permission='dcim.view_rearporttemplate',
+        weight=540,
         hide_if_empty=True
     )
 
@@ -1051,6 +1058,7 @@ class DeviceTypeModuleBaysView(DeviceTypeComponentsView):
         label=_('Module Bays'),
         badge=lambda obj: obj.modulebaytemplates.count(),
         permission='dcim.view_modulebaytemplate',
+        weight=510,
         hide_if_empty=True
     )
 
@@ -1065,6 +1073,7 @@ class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
         label=_('Device Bays'),
         badge=lambda obj: obj.devicebaytemplates.count(),
         permission='dcim.view_devicebaytemplate',
+        weight=500,
         hide_if_empty=True
     )
 
@@ -1079,6 +1088,7 @@ class DeviceTypeInventoryItemsView(DeviceTypeComponentsView):
         label=_('Inventory Items'),
         badge=lambda obj: obj.inventoryitemtemplates.count(),
         permission='dcim.view_invenotryitemtemplate',
+        weight=590,
         hide_if_empty=True
     )
 
@@ -1181,6 +1191,7 @@ class ModuleTypeConsolePortsView(ModuleTypeComponentsView):
         label=_('Console Ports'),
         badge=lambda obj: obj.consoleporttemplates.count(),
         permission='dcim.view_consoleporttemplate',
+        weight=530,
         hide_if_empty=True
     )
 
@@ -1195,6 +1206,7 @@ class ModuleTypeConsoleServerPortsView(ModuleTypeComponentsView):
         label=_('Console Server Ports'),
         badge=lambda obj: obj.consoleserverporttemplates.count(),
         permission='dcim.view_consoleserverporttemplate',
+        weight=540,
         hide_if_empty=True
     )
 
@@ -1209,6 +1221,7 @@ class ModuleTypePowerPortsView(ModuleTypeComponentsView):
         label=_('Power Ports'),
         badge=lambda obj: obj.powerporttemplates.count(),
         permission='dcim.view_powerporttemplate',
+        weight=550,
         hide_if_empty=True
     )
 
@@ -1223,6 +1236,7 @@ class ModuleTypePowerOutletsView(ModuleTypeComponentsView):
         label=_('Power Outlets'),
         badge=lambda obj: obj.poweroutlettemplates.count(),
         permission='dcim.view_poweroutlettemplate',
+        weight=560,
         hide_if_empty=True
     )
 
@@ -1237,6 +1251,7 @@ class ModuleTypeInterfacesView(ModuleTypeComponentsView):
         label=_('Interfaces'),
         badge=lambda obj: obj.interfacetemplates.count(),
         permission='dcim.view_interfacetemplate',
+        weight=500,
         hide_if_empty=True
     )
 
@@ -1251,6 +1266,7 @@ class ModuleTypeFrontPortsView(ModuleTypeComponentsView):
         label=_('Front Ports'),
         badge=lambda obj: obj.frontporttemplates.count(),
         permission='dcim.view_frontporttemplate',
+        weight=510,
         hide_if_empty=True
     )
 
@@ -1265,6 +1281,7 @@ class ModuleTypeRearPortsView(ModuleTypeComponentsView):
         label=_('Rear Ports'),
         badge=lambda obj: obj.rearporttemplates.count(),
         permission='dcim.view_rearporttemplate',
+        weight=520,
         hide_if_empty=True
     )
 
@@ -1873,6 +1890,7 @@ class DeviceConsolePortsView(DeviceComponentsView):
         label=_('Console Ports'),
         badge=lambda obj: obj.consoleports.count(),
         permission='dcim.view_consoleport',
+        weight=550,
         hide_if_empty=True
     )
 
@@ -1887,6 +1905,7 @@ class DeviceConsoleServerPortsView(DeviceComponentsView):
         label=_('Console Server Ports'),
         badge=lambda obj: obj.consoleserverports.count(),
         permission='dcim.view_consoleserverport',
+        weight=560,
         hide_if_empty=True
     )
 
@@ -1901,6 +1920,7 @@ class DevicePowerPortsView(DeviceComponentsView):
         label=_('Power Ports'),
         badge=lambda obj: obj.powerports.count(),
         permission='dcim.view_powerport',
+        weight=570,
         hide_if_empty=True
     )
 
@@ -1915,6 +1935,7 @@ class DevicePowerOutletsView(DeviceComponentsView):
         label=_('Power Outlets'),
         badge=lambda obj: obj.poweroutlets.count(),
         permission='dcim.view_poweroutlet',
+        weight=580,
         hide_if_empty=True
     )
 
@@ -1929,6 +1950,7 @@ class DeviceInterfacesView(DeviceComponentsView):
         label=_('Interfaces'),
         badge=lambda obj: obj.interfaces.count(),
         permission='dcim.view_interface',
+        weight=520,
         hide_if_empty=True
     )
 
@@ -1949,6 +1971,7 @@ class DeviceFrontPortsView(DeviceComponentsView):
         label=_('Front Ports'),
         badge=lambda obj: obj.frontports.count(),
         permission='dcim.view_frontport',
+        weight=530,
         hide_if_empty=True
     )
 
@@ -1963,6 +1986,7 @@ class DeviceRearPortsView(DeviceComponentsView):
         label=_('Rear Ports'),
         badge=lambda obj: obj.rearports.count(),
         permission='dcim.view_rearport',
+        weight=540,
         hide_if_empty=True
     )
 
@@ -1977,6 +2001,7 @@ class DeviceModuleBaysView(DeviceComponentsView):
         label=_('Module Bays'),
         badge=lambda obj: obj.modulebays.count(),
         permission='dcim.view_modulebay',
+        weight=510,
         hide_if_empty=True
     )
 
@@ -1991,6 +2016,7 @@ class DeviceDeviceBaysView(DeviceComponentsView):
         label=_('Device Bays'),
         badge=lambda obj: obj.devicebays.count(),
         permission='dcim.view_devicebay',
+        weight=500,
         hide_if_empty=True
     )
 
@@ -2005,6 +2031,7 @@ class DeviceInventoryView(DeviceComponentsView):
         label=_('Inventory Items'),
         badge=lambda obj: obj.inventoryitems.count(),
         permission='dcim.view_inventoryitem',
+        weight=590,
         hide_if_empty=True
     )
 
@@ -2015,7 +2042,8 @@ class DeviceConfigContextView(ObjectConfigContextView):
     base_template = 'dcim/device/base.html'
     tab = ViewTab(
         label=_('Config Context'),
-        permission='extras.view_configcontext'
+        permission='extras.view_configcontext',
+        weight=2000
     )
 
 
@@ -2088,6 +2116,7 @@ class DeviceStatusView(generic.ObjectView):
     tab = NAPALMViewTab(
         label=_('Status'),
         permission='dcim.napalm_read_device',
+        weight=3000
     )
 
 
@@ -2099,6 +2128,7 @@ class DeviceLLDPNeighborsView(generic.ObjectView):
     tab = NAPALMViewTab(
         label=_('LLDP Neighbors'),
         permission='dcim.napalm_read_device',
+        weight=3100
     )
 
     def get_extra_context(self, request, instance):
@@ -2121,6 +2151,7 @@ class DeviceConfigView(generic.ObjectView):
     tab = NAPALMViewTab(
         label=_('Config'),
         permission='dcim.napalm_read_device',
+        weight=3200
     )
 
 

+ 14 - 7
netbox/ipam/views.py

@@ -314,7 +314,8 @@ class AggregatePrefixesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('Prefixes'),
         badge=lambda x: x.get_child_prefixes().count(),
-        permission='ipam.view_prefix'
+        permission='ipam.view_prefix',
+        weight=500
     )
 
     def get_children(self, request, parent):
@@ -502,7 +503,8 @@ class PrefixPrefixesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('Child Prefixes'),
         badge=lambda x: x.get_child_prefixes().count(),
-        permission='ipam.view_prefix'
+        permission='ipam.view_prefix',
+        weight=500
     )
 
     def get_children(self, request, parent):
@@ -536,7 +538,8 @@ class PrefixIPRangesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('Child Ranges'),
         badge=lambda x: x.get_child_ranges().count(),
-        permission='ipam.view_iprange'
+        permission='ipam.view_iprange',
+        weight=600
     )
 
     def get_children(self, request, parent):
@@ -561,7 +564,8 @@ class PrefixIPAddressesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('IP Addresses'),
         badge=lambda x: x.get_child_ips().count(),
-        permission='ipam.view_ipaddress'
+        permission='ipam.view_ipaddress',
+        weight=700
     )
 
     def get_children(self, request, parent):
@@ -635,7 +639,8 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('IP Addresses'),
         badge=lambda x: x.get_child_ips().count(),
-        permission='ipam.view_ipaddress'
+        permission='ipam.view_ipaddress',
+        weight=500
     )
 
     def get_children(self, request, parent):
@@ -1075,7 +1080,8 @@ class VLANInterfacesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('Device Interfaces'),
         badge=lambda x: x.get_interfaces().count(),
-        permission='dcim.view_interface'
+        permission='dcim.view_interface',
+        weight=500
     )
 
     def get_children(self, request, parent):
@@ -1092,7 +1098,8 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('VM Interfaces'),
         badge=lambda x: x.get_vminterfaces().count(),
-        permission='virtualization.view_vminterface'
+        permission='virtualization.view_vminterface',
+        weight=510
     )
 
     def get_children(self, request, parent):

+ 4 - 2
netbox/netbox/views/generic/feature_views.py

@@ -27,7 +27,8 @@ class ObjectChangeLogView(View):
     base_template = None
     tab = ViewTab(
         label=_('Changelog'),
-        permission='extras.view_objectchange'
+        permission='extras.view_objectchange',
+        weight=10000
     )
 
     def get(self, request, model, **kwargs):
@@ -80,7 +81,8 @@ class ObjectJournalView(View):
     tab = ViewTab(
         label=_('Journal'),
         badge=lambda obj: obj.journal_entries.count(),
-        permission='extras.view_journalentry'
+        permission='extras.view_journalentry',
+        weight=9000
     )
 
     def get(self, request, model, **kwargs):

+ 4 - 0
netbox/utilities/templatetags/tabs.py

@@ -47,9 +47,13 @@ def model_view_tabs(context, instance):
                     'url': url,
                     'label': attrs['label'],
                     'badge': attrs['badge'],
+                    'weight': attrs['weight'],
                     'is_active': active_tab and active_tab == tab,
                 })
 
+    # Order tabs by weight
+    tabs = sorted(tabs, key=lambda x: x['weight'])
+
     return {
         'tabs': tabs,
     }

+ 4 - 1
netbox/utilities/views.py

@@ -142,13 +142,15 @@ class ViewTab:
         label: Human-friendly text
         badge: A static value or callable to display alongside the label (optional). If a callable is used, it must
             accept a single argument representing the object being viewed.
+        weight: Numeric weight to influence ordering among other tabs (default: 1000)
         permission: The permission required to display the tab (optional).
         hide_if_empty: If true, the tab will be displayed only if its badge has a meaningful value. (Tabs without a
             badge are always displayed.)
     """
-    def __init__(self, label, badge=None, permission=None, hide_if_empty=False):
+    def __init__(self, label, badge=None, weight=1000, permission=None, hide_if_empty=False):
         self.label = label
         self.badge = badge
+        self.weight = weight
         self.permission = permission
         self.hide_if_empty = hide_if_empty
 
@@ -160,6 +162,7 @@ class ViewTab:
         return {
             'label': self.label,
             'badge': badge_value,
+            'weight': self.weight,
         }
 
     def _get_badge_value(self, instance):

+ 8 - 4
netbox/virtualization/views.py

@@ -180,7 +180,8 @@ class ClusterVirtualMachinesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('Virtual Machines'),
         badge=lambda obj: obj.virtual_machines.count(),
-        permission='virtualization.view_virtualmachine'
+        permission='virtualization.view_virtualmachine',
+        weight=500
     )
 
     def get_children(self, request, parent):
@@ -197,7 +198,8 @@ class ClusterDevicesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('Devices'),
         badge=lambda obj: obj.devices.count(),
-        permission='virtualization.view_virtualmachine'
+        permission='virtualization.view_virtualmachine',
+        weight=600
     )
 
     def get_children(self, request, parent):
@@ -370,7 +372,8 @@ class VirtualMachineInterfacesView(generic.ObjectChildrenView):
     tab = ViewTab(
         label=_('Interfaces'),
         badge=lambda obj: obj.interfaces.count(),
-        permission='virtualization.view_vminterface'
+        permission='virtualization.view_vminterface',
+        weight=500
     )
 
     def get_children(self, request, parent):
@@ -386,7 +389,8 @@ class VirtualMachineConfigContextView(ObjectConfigContextView):
     base_template = 'virtualization/virtualmachine.html'
     tab = ViewTab(
         label=_('Config Context'),
-        permission='extras.view_configcontext'
+        permission='extras.view_configcontext',
+        weight=2000
     )