Просмотр исходного кода

#20923: Migrate remaining DCIM views to new UI layouts (#21706)

Jeremy Stretch 3 дней назад
Родитель
Сommit
aa01c16db0
36 измененных файлов с 1186 добавлено и 1885 удалено
  1. 341 1
      netbox/dcim/ui/panels.py
  2. 321 18
      netbox/dcim/views.py
  3. 0 86
      netbox/templates/dcim/cable.html
  4. 0 87
      netbox/templates/dcim/consoleport.html
  5. 0 87
      netbox/templates/dcim/consoleserverport.html
  6. 0 62
      netbox/templates/dcim/devicebay.html
  7. 0 148
      netbox/templates/dcim/frontport.html
  8. 0 436
      netbox/templates/dcim/interface.html
  9. 3 0
      netbox/templates/dcim/interface/attrs/mac_address.html
  10. 2 0
      netbox/templates/dcim/interface/attrs/speed.html
  11. 0 73
      netbox/templates/dcim/inventoryitem.html
  12. 0 52
      netbox/templates/dcim/inventoryitemrole.html
  13. 0 54
      netbox/templates/dcim/macaddress.html
  14. 0 82
      netbox/templates/dcim/modulebay.html
  15. 6 0
      netbox/templates/dcim/panels/cable_termination_a.html
  16. 6 0
      netbox/templates/dcim/panels/cable_termination_b.html
  17. 40 0
      netbox/templates/dcim/panels/component_inventory_items.html
  18. 96 0
      netbox/templates/dcim/panels/connection.html
  19. 29 0
      netbox/templates/dcim/panels/front_port_mappings.html
  20. 21 0
      netbox/templates/dcim/panels/installed_device.html
  21. 33 0
      netbox/templates/dcim/panels/installed_module.html
  22. 105 0
      netbox/templates/dcim/panels/interface_connection.html
  23. 35 0
      netbox/templates/dcim/panels/interface_virtual_circuit.html
  24. 72 0
      netbox/templates/dcim/panels/interface_wireless.html
  25. 25 0
      netbox/templates/dcim/panels/interface_wireless_lans.html
  26. 29 0
      netbox/templates/dcim/panels/rear_port_mappings.html
  27. 1 1
      netbox/templates/dcim/panels/virtual_chassis_members.html
  28. 0 128
      netbox/templates/dcim/powerfeed.html
  29. 6 0
      netbox/templates/dcim/powerfeed/attrs/connected_device.html
  30. 15 0
      netbox/templates/dcim/powerfeed/attrs/utilization.html
  31. 0 92
      netbox/templates/dcim/poweroutlet.html
  32. 0 73
      netbox/templates/dcim/powerpanel.html
  33. 0 88
      netbox/templates/dcim/powerport.html
  34. 0 142
      netbox/templates/dcim/rearport.html
  35. 0 89
      netbox/templates/dcim/virtualchassis.html
  36. 0 86
      netbox/templates/dcim/virtualdevicecontext.html

+ 341 - 1
netbox/dcim/ui/panels.py

@@ -1,6 +1,8 @@
+from django.contrib.contenttypes.models import ContentType
+from django.template.loader import render_to_string
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from netbox.ui import attrs, panels
+from netbox.ui import actions, attrs, panels
 
 
 
 
 class SitePanel(panels.ObjectAttributesPanel):
 class SitePanel(panels.ObjectAttributesPanel):
@@ -189,16 +191,251 @@ class PlatformPanel(panels.NestedGroupObjectPanel):
     config_template = attrs.RelatedObjectAttr('config_template', linkify=True)
     config_template = attrs.RelatedObjectAttr('config_template', linkify=True)
 
 
 
 
+class ConsolePortPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    type = attrs.ChoiceAttr('type')
+    speed = attrs.ChoiceAttr('speed')
+    description = attrs.TextAttr('description')
+
+
+class ConsoleServerPortPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    type = attrs.ChoiceAttr('type')
+    speed = attrs.ChoiceAttr('speed')
+    description = attrs.TextAttr('description')
+
+
+class PowerPortPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    type = attrs.ChoiceAttr('type')
+    description = attrs.TextAttr('description')
+    maximum_draw = attrs.TextAttr('maximum_draw')
+    allocated_draw = attrs.TextAttr('allocated_draw')
+
+
+class PowerOutletPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    type = attrs.ChoiceAttr('type')
+    status = attrs.ChoiceAttr('status')
+    description = attrs.TextAttr('description')
+    color = attrs.ColorAttr('color')
+    power_port = attrs.RelatedObjectAttr('power_port', linkify=True)
+    feed_leg = attrs.ChoiceAttr('feed_leg')
+
+
+class FrontPortPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    type = attrs.ChoiceAttr('type')
+    color = attrs.ColorAttr('color')
+    positions = attrs.TextAttr('positions')
+    description = attrs.TextAttr('description')
+
+
+class RearPortPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    type = attrs.ChoiceAttr('type')
+    color = attrs.ColorAttr('color')
+    positions = attrs.TextAttr('positions')
+    description = attrs.TextAttr('description')
+
+
+class ModuleBayPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    position = attrs.TextAttr('position')
+    description = attrs.TextAttr('description')
+
+
+class DeviceBayPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    description = attrs.TextAttr('description')
+
+
+class InventoryItemPanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    parent = attrs.RelatedObjectAttr('parent', linkify=True, label=_('Parent item'))
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    status = attrs.ChoiceAttr('status')
+    role = attrs.RelatedObjectAttr('role', linkify=True)
+    component = attrs.GenericForeignKeyAttr('component', linkify=True)
+    manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True)
+    part_id = attrs.TextAttr('part_id', label=_('Part ID'))
+    serial = attrs.TextAttr('serial')
+    asset_tag = attrs.TextAttr('asset_tag')
+    description = attrs.TextAttr('description')
+
+
+class InventoryItemRolePanel(panels.OrganizationalObjectPanel):
+    color = attrs.ColorAttr('color')
+
+
+class CablePanel(panels.ObjectAttributesPanel):
+    type = attrs.ChoiceAttr('type')
+    status = attrs.ChoiceAttr('status')
+    profile = attrs.ChoiceAttr('profile')
+    tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+    label = attrs.TextAttr('label')
+    description = attrs.TextAttr('description')
+    color = attrs.ColorAttr('color')
+    length = attrs.NumericAttr('length', unit_accessor='get_length_unit_display')
+
+
+class VirtualChassisPanel(panels.ObjectAttributesPanel):
+    domain = attrs.TextAttr('domain')
+    master = attrs.RelatedObjectAttr('master', linkify=True)
+    description = attrs.TextAttr('description')
+
+
+class PowerPanelPanel(panels.ObjectAttributesPanel):
+    site = attrs.RelatedObjectAttr('site', linkify=True)
+    location = attrs.NestedObjectAttr('location', linkify=True)
+    description = attrs.TextAttr('description')
+
+
+class PowerFeedPanel(panels.ObjectAttributesPanel):
+    power_panel = attrs.RelatedObjectAttr('power_panel', linkify=True)
+    rack = attrs.RelatedObjectAttr('rack', linkify=True)
+    type = attrs.ChoiceAttr('type')
+    status = attrs.ChoiceAttr('status')
+    description = attrs.TextAttr('description')
+    tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+    connected_device = attrs.TemplatedAttr(
+        'connected_endpoints',
+        label=_('Connected device'),
+        template_name='dcim/powerfeed/attrs/connected_device.html',
+    )
+    utilization = attrs.TemplatedAttr(
+        'connected_endpoints',
+        label=_('Utilization (allocated)'),
+        template_name='dcim/powerfeed/attrs/utilization.html',
+    )
+
+
+class PowerFeedElectricalPanel(panels.ObjectAttributesPanel):
+    title = _('Electrical Characteristics')
+
+    supply = attrs.ChoiceAttr('supply')
+    voltage = attrs.TextAttr('voltage', format_string=_('{}V'))
+    amperage = attrs.TextAttr('amperage', format_string=_('{}A'))
+    phase = attrs.ChoiceAttr('phase')
+    max_utilization = attrs.TextAttr('max_utilization', format_string='{}%')
+
+
+class VirtualDeviceContextPanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    identifier = attrs.TextAttr('identifier')
+    status = attrs.ChoiceAttr('status')
+    primary_ip4 = attrs.TemplatedAttr(
+        'primary_ip4',
+        label=_('Primary IPv4'),
+        template_name='dcim/device/attrs/ipaddress.html',
+    )
+    primary_ip6 = attrs.TemplatedAttr(
+        'primary_ip6',
+        label=_('Primary IPv6'),
+        template_name='dcim/device/attrs/ipaddress.html',
+    )
+    tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+
+
+class MACAddressPanel(panels.ObjectAttributesPanel):
+    mac_address = attrs.TextAttr('mac_address', label=_('MAC address'), style='font-monospace', copy_button=True)
+    description = attrs.TextAttr('description')
+    assignment = attrs.RelatedObjectAttr('assigned_object', linkify=True, grouped_by='parent_object')
+    is_primary = attrs.BooleanAttr('is_primary', label=_('Primary for interface'))
+
+
+class ConnectionPanel(panels.ObjectPanel):
+    """
+    A panel which displays connection information for a cabled object.
+    """
+    template_name = 'dcim/panels/connection.html'
+    title = _('Connection')
+
+    def __init__(self, trace_url_name, connect_options=None, show_endpoints=True, **kwargs):
+        super().__init__(**kwargs)
+        self.trace_url_name = trace_url_name
+        self.connect_options = connect_options or []
+        self.show_endpoints = show_endpoints
+
+    def get_context(self, context):
+        return {
+            **super().get_context(context),
+            'trace_url_name': self.trace_url_name,
+            'connect_options': self.connect_options,
+            'show_endpoints': self.show_endpoints,
+        }
+
+    def render(self, context):
+        ctx = self.get_context(context)
+        return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
+class InventoryItemsPanel(panels.ObjectPanel):
+    """
+    A panel which displays inventory items associated with a component.
+    """
+    template_name = 'dcim/panels/component_inventory_items.html'
+    title = _('Inventory Items')
+    actions = [
+        actions.AddObject(
+            'dcim.inventoryitem',
+            url_params={
+                'component_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk,
+                'component_id': lambda ctx: ctx['object'].pk,
+            },
+        ),
+    ]
+
+    def render(self, context):
+        ctx = self.get_context(context)
+        return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
 class VirtualChassisMembersPanel(panels.ObjectPanel):
 class VirtualChassisMembersPanel(panels.ObjectPanel):
     """
     """
     A panel which lists all members of a virtual chassis.
     A panel which lists all members of a virtual chassis.
     """
     """
     template_name = 'dcim/panels/virtual_chassis_members.html'
     template_name = 'dcim/panels/virtual_chassis_members.html'
     title = _('Virtual Chassis Members')
     title = _('Virtual Chassis Members')
+    actions = [
+        actions.AddObject(
+            'dcim.device',
+            url_params={
+                'site': lambda ctx: ctx['object'].master.site_id if ctx['object'].master else '',
+                'rack': lambda ctx: ctx['object'].master.rack_id if ctx['object'].master else '',
+            },
+        ),
+    ]
 
 
     def get_context(self, context):
     def get_context(self, context):
         return {
         return {
             **super().get_context(context),
             **super().get_context(context),
+            'virtual_chassis': context.get('virtual_chassis'),
             'vc_members': context.get('vc_members'),
             'vc_members': context.get('vc_members'),
         }
         }
 
 
@@ -226,3 +463,106 @@ class PowerUtilizationPanel(panels.ObjectPanel):
         if not obj.powerports.exists() or not obj.poweroutlets.exists():
         if not obj.powerports.exists() or not obj.poweroutlets.exists():
             return ''
             return ''
         return super().render(context)
         return super().render(context)
+
+
+class InterfacePanel(panels.ObjectAttributesPanel):
+    device = attrs.RelatedObjectAttr('device', linkify=True)
+    module = attrs.RelatedObjectAttr('module', linkify=True)
+    name = attrs.TextAttr('name')
+    label = attrs.TextAttr('label')
+    type = attrs.ChoiceAttr('type')
+    speed = attrs.TemplatedAttr('speed', template_name='dcim/interface/attrs/speed.html', label=_('Speed'))
+    duplex = attrs.ChoiceAttr('duplex')
+    mtu = attrs.TextAttr('mtu', label=_('MTU'))
+    enabled = attrs.BooleanAttr('enabled')
+    mgmt_only = attrs.BooleanAttr('mgmt_only', label=_('Management only'))
+    description = attrs.TextAttr('description')
+    poe_mode = attrs.ChoiceAttr('poe_mode', label=_('PoE mode'))
+    poe_type = attrs.ChoiceAttr('poe_type', label=_('PoE type'))
+    mode = attrs.ChoiceAttr('mode', label=_('802.1Q mode'))
+    qinq_svlan = attrs.RelatedObjectAttr('qinq_svlan', linkify=True, label=_('Q-in-Q SVLAN'))
+    untagged_vlan = attrs.RelatedObjectAttr('untagged_vlan', linkify=True, label=_('Untagged VLAN'))
+    tx_power = attrs.TextAttr('tx_power', label=_('Transmit power (dBm)'))
+    tunnel = attrs.RelatedObjectAttr('tunnel_termination.tunnel', linkify=True, label=_('Tunnel'))
+    l2vpn = attrs.RelatedObjectAttr('l2vpn_termination.l2vpn', linkify=True, label=_('L2VPN'))
+
+
+class RelatedInterfacesPanel(panels.ObjectAttributesPanel):
+    title = _('Related Interfaces')
+
+    parent = attrs.RelatedObjectAttr('parent', linkify=True)
+    bridge = attrs.RelatedObjectAttr('bridge', linkify=True)
+    lag = attrs.RelatedObjectAttr('lag', linkify=True, label=_('LAG'))
+
+
+class InterfaceAddressingPanel(panels.ObjectAttributesPanel):
+    title = _('Addressing')
+
+    mac_address = attrs.TemplatedAttr(
+        'primary_mac_address',
+        template_name='dcim/interface/attrs/mac_address.html',
+        label=_('MAC address'),
+    )
+    wwn = attrs.TextAttr('wwn', style='font-monospace', label=_('WWN'))
+    vrf = attrs.RelatedObjectAttr('vrf', linkify=True, label=_('VRF'))
+    vlan_translation = attrs.RelatedObjectAttr('vlan_translation_policy', linkify=True, label=_('VLAN translation'))
+
+
+class InterfaceConnectionPanel(panels.ObjectPanel):
+    """
+    A connection panel for interfaces, which handles cable, wireless link, and virtual circuit cases.
+    """
+    template_name = 'dcim/panels/interface_connection.html'
+    title = _('Connection')
+
+    def render(self, context):
+        obj = context.get('object')
+        if obj and obj.is_virtual:
+            return ''
+        ctx = self.get_context(context)
+        return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
+class VirtualCircuitPanel(panels.ObjectPanel):
+    """
+    A panel which displays virtual circuit information for a virtual interface.
+    """
+    template_name = 'dcim/panels/interface_virtual_circuit.html'
+    title = _('Virtual Circuit')
+
+    def render(self, context):
+        obj = context.get('object')
+        if not obj or not obj.is_virtual or not obj.virtual_circuit_termination:
+            return ''
+        ctx = self.get_context(context)
+        return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
+class InterfaceWirelessPanel(panels.ObjectPanel):
+    """
+    A panel which displays wireless RF attributes for an interface, comparing local and peer values.
+    """
+    template_name = 'dcim/panels/interface_wireless.html'
+    title = _('Wireless')
+
+    def render(self, context):
+        obj = context.get('object')
+        if not obj or not obj.is_wireless:
+            return ''
+        ctx = self.get_context(context)
+        return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
+class WirelessLANsPanel(panels.ObjectPanel):
+    """
+    A panel which lists the wireless LANs associated with an interface.
+    """
+    template_name = 'dcim/panels/interface_wireless_lans.html'
+    title = _('Wireless LANs')
+
+    def render(self, context):
+        obj = context.get('object')
+        if not obj or not obj.is_wireless:
+            return ''
+        ctx = self.get_context(context)
+        return render_to_string(self.template_name, ctx, request=ctx.get('request'))

+ 321 - 18
netbox/dcim/views.py

@@ -17,10 +17,12 @@ from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel
 from extras.views import ObjectConfigContextView, ObjectRenderConfigView
 from extras.views import ObjectConfigContextView, ObjectRenderConfigView
 from ipam.models import ASN, VLAN, IPAddress, Prefix, VLANGroup
 from ipam.models import ASN, VLAN, IPAddress, Prefix, VLANGroup
 from ipam.tables import VLANTranslationRuleTable
 from ipam.tables import VLANTranslationRuleTable
+from ipam.ui.panels import FHRPGroupAssignmentsPanel
 from netbox.object_actions import *
 from netbox.object_actions import *
 from netbox.ui import actions, layout
 from netbox.ui import actions, layout
 from netbox.ui.panels import (
 from netbox.ui.panels import (
     CommentsPanel,
     CommentsPanel,
+    ContextTablePanel,
     JSONPanel,
     JSONPanel,
     NestedGroupObjectPanel,
     NestedGroupObjectPanel,
     ObjectsTablePanel,
     ObjectsTablePanel,
@@ -1577,7 +1579,7 @@ class ModuleTypeProfileListView(generic.ObjectListView):
 
 
 
 
 @register_model_view(ModuleTypeProfile)
 @register_model_view(ModuleTypeProfile)
-class ModuleTypeProfileView(GetRelatedModelsMixin, generic.ObjectView):
+class ModuleTypeProfileView(generic.ObjectView):
     template_name = 'generic/object.html'
     template_name = 'generic/object.html'
     queryset = ModuleTypeProfile.objects.all()
     queryset = ModuleTypeProfile.objects.all()
     layout = layout.SimpleLayout(
     layout = layout.SimpleLayout(
@@ -2555,6 +2557,7 @@ class DeviceView(generic.ObjectView):
             vc_members = []
             vc_members = []
 
 
         return {
         return {
+            'virtual_chassis': instance.virtual_chassis,
             'vc_members': vc_members,
             'vc_members': vc_members,
             'svg_extra': f'highlight=id:{instance.pk}',
             'svg_extra': f'highlight=id:{instance.pk}',
         }
         }
@@ -2907,6 +2910,28 @@ class ConsolePortListView(generic.ObjectListView):
 @register_model_view(ConsolePort)
 @register_model_view(ConsolePort)
 class ConsolePortView(generic.ObjectView):
 class ConsolePortView(generic.ObjectView):
     queryset = ConsolePort.objects.all()
     queryset = ConsolePort.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.ConsolePortPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            panels.ConnectionPanel(
+                trace_url_name='dcim:consoleport_trace',
+                connect_options=[
+                    {
+                        'a_type': 'dcim.consoleport',
+                        'b_type': 'dcim.consoleserverport',
+                        'label': _('Console Server Port'),
+                    },
+                    {'a_type': 'dcim.consoleport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+                    {'a_type': 'dcim.consoleport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+                ],
+            ),
+            panels.InventoryItemsPanel(),
+        ],
+    )
 
 
 
 
 @register_model_view(ConsolePort, 'add', detail=False)
 @register_model_view(ConsolePort, 'add', detail=False)
@@ -2978,6 +3003,24 @@ class ConsoleServerPortListView(generic.ObjectListView):
 @register_model_view(ConsoleServerPort)
 @register_model_view(ConsoleServerPort)
 class ConsoleServerPortView(generic.ObjectView):
 class ConsoleServerPortView(generic.ObjectView):
     queryset = ConsoleServerPort.objects.all()
     queryset = ConsoleServerPort.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.ConsoleServerPortPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            panels.ConnectionPanel(
+                trace_url_name='dcim:consoleserverport_trace',
+                connect_options=[
+                    {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.consoleport', 'label': _('Console Port')},
+                    {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+                    {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+                ],
+            ),
+            panels.InventoryItemsPanel(),
+        ],
+    )
 
 
 
 
 @register_model_view(ConsoleServerPort, 'add', detail=False)
 @register_model_view(ConsoleServerPort, 'add', detail=False)
@@ -3049,6 +3092,23 @@ class PowerPortListView(generic.ObjectListView):
 @register_model_view(PowerPort)
 @register_model_view(PowerPort)
 class PowerPortView(generic.ObjectView):
 class PowerPortView(generic.ObjectView):
     queryset = PowerPort.objects.all()
     queryset = PowerPort.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.PowerPortPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            panels.ConnectionPanel(
+                trace_url_name='dcim:powerport_trace',
+                connect_options=[
+                    {'a_type': 'dcim.powerport', 'b_type': 'dcim.poweroutlet', 'label': _('Power Outlet')},
+                    {'a_type': 'dcim.powerport', 'b_type': 'dcim.powerfeed', 'label': _('Power Feed')},
+                ],
+            ),
+            panels.InventoryItemsPanel(),
+        ],
+    )
 
 
 
 
 @register_model_view(PowerPort, 'add', detail=False)
 @register_model_view(PowerPort, 'add', detail=False)
@@ -3120,6 +3180,22 @@ class PowerOutletListView(generic.ObjectListView):
 @register_model_view(PowerOutlet)
 @register_model_view(PowerOutlet)
 class PowerOutletView(generic.ObjectView):
 class PowerOutletView(generic.ObjectView):
     queryset = PowerOutlet.objects.all()
     queryset = PowerOutlet.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.PowerOutletPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            panels.ConnectionPanel(
+                trace_url_name='dcim:poweroutlet_trace',
+                connect_options=[
+                    {'a_type': 'dcim.poweroutlet', 'b_type': 'dcim.powerport', 'label': _('Power Port')},
+                ],
+            ),
+            panels.InventoryItemsPanel(),
+        ],
+    )
 
 
 
 
 @register_model_view(PowerOutlet, 'add', detail=False)
 @register_model_view(PowerOutlet, 'add', detail=False)
@@ -3191,6 +3267,45 @@ class InterfaceListView(generic.ObjectListView):
 @register_model_view(Interface)
 @register_model_view(Interface)
 class InterfaceView(generic.ObjectView):
 class InterfaceView(generic.ObjectView):
     queryset = Interface.objects.all()
     queryset = Interface.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.InterfacePanel(),
+            panels.RelatedInterfacesPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            ContextTablePanel('vdc_table', title=_('Virtual Device Contexts')),
+            panels.InterfaceAddressingPanel(),
+            panels.VirtualCircuitPanel(),
+            panels.InterfaceConnectionPanel(),
+            panels.InterfaceWirelessPanel(),
+            panels.WirelessLANsPanel(),
+            FHRPGroupAssignmentsPanel(),
+            panels.InventoryItemsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                model='ipam.IPAddress',
+                filters={'interface_id': lambda ctx: ctx['object'].pk},
+                title=_('IP Addresses'),
+            ),
+            ObjectsTablePanel(
+                model='dcim.MACAddress',
+                filters={'interface_id': lambda ctx: ctx['object'].pk},
+                title=_('MAC Addresses'),
+            ),
+            ObjectsTablePanel(
+                model='ipam.VLAN',
+                filters={'interface_id': lambda ctx: ctx['object'].pk},
+                title=_('VLANs'),
+            ),
+            ContextTablePanel('lag_interfaces_table', title=_('LAG Members')),
+            ContextTablePanel('vlan_translation_table', title=_('VLAN Translation')),
+            ContextTablePanel('bridge_interfaces_table', title=_('Bridged Interfaces')),
+            ContextTablePanel('child_interfaces_table', title=_('Child Interfaces')),
+        ],
+    )
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
         # Get assigned VDCs
         # Get assigned VDCs
@@ -3205,30 +3320,29 @@ class InterfaceView(generic.ObjectView):
         vdc_table.configure(request)
         vdc_table.configure(request)
 
 
         # Get bridge interfaces
         # Get bridge interfaces
-        bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance)
         bridge_interfaces_table = tables.InterfaceTable(
         bridge_interfaces_table = tables.InterfaceTable(
-            bridge_interfaces,
+            Interface.objects.restrict(request.user, 'view').filter(bridge=instance),
             exclude=('device', 'parent'),
             exclude=('device', 'parent'),
             orderable=False
             orderable=False
         )
         )
         bridge_interfaces_table.configure(request)
         bridge_interfaces_table.configure(request)
 
 
         # Get child interfaces
         # Get child interfaces
-        child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
         child_interfaces_table = tables.InterfaceTable(
         child_interfaces_table = tables.InterfaceTable(
-            child_interfaces,
+            Interface.objects.restrict(request.user, 'view').filter(parent=instance),
             exclude=('device', 'parent'),
             exclude=('device', 'parent'),
             orderable=False
             orderable=False
         )
         )
         child_interfaces_table.configure(request)
         child_interfaces_table.configure(request)
 
 
-        # Get LAG interfaces
-        lag_interfaces = Interface.objects.restrict(request.user, 'view').filter(lag=instance)
-        lag_interfaces_table = tables.InterfaceLAGMemberTable(
-            lag_interfaces,
-            orderable=False
-        )
-        lag_interfaces_table.configure(request)
+        # Get LAG members (only for LAG interfaces)
+        lag_interfaces_table = None
+        if instance.is_lag:
+            lag_interfaces_table = tables.InterfaceLAGMemberTable(
+                Interface.objects.restrict(request.user, 'view').filter(lag=instance),
+                orderable=False
+            )
+            lag_interfaces_table.configure(request)
 
 
         # Get VLAN translation rules
         # Get VLAN translation rules
         vlan_translation_table = None
         vlan_translation_table = None
@@ -3241,7 +3355,6 @@ class InterfaceView(generic.ObjectView):
 
 
         return {
         return {
             'vdc_table': vdc_table,
             'vdc_table': vdc_table,
-            'bridge_interfaces': bridge_interfaces,
             'bridge_interfaces_table': bridge_interfaces_table,
             'bridge_interfaces_table': bridge_interfaces_table,
             'child_interfaces_table': child_interfaces_table,
             'child_interfaces_table': child_interfaces_table,
             'lag_interfaces_table': lag_interfaces_table,
             'lag_interfaces_table': lag_interfaces_table,
@@ -3329,6 +3442,33 @@ class FrontPortListView(generic.ObjectListView):
 @register_model_view(FrontPort)
 @register_model_view(FrontPort)
 class FrontPortView(generic.ObjectView):
 class FrontPortView(generic.ObjectView):
     queryset = FrontPort.objects.all()
     queryset = FrontPort.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.FrontPortPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+            panels.InventoryItemsPanel(),
+        ],
+        right_panels=[
+            panels.ConnectionPanel(
+                trace_url_name='dcim:frontport_trace',
+                show_endpoints=False,
+                connect_options=[
+                    {'a_type': 'dcim.frontport', 'b_type': 'dcim.interface', 'label': _('Interface')},
+                    {'a_type': 'dcim.frontport', 'b_type': 'dcim.consoleserverport', 'label': _('Console Server Port')},
+                    {'a_type': 'dcim.frontport', 'b_type': 'dcim.consoleport', 'label': _('Console Port')},
+                    {'a_type': 'dcim.frontport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+                    {'a_type': 'dcim.frontport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+                    {
+                        'a_type': 'dcim.frontport',
+                        'b_type': 'circuits.circuittermination',
+                        'label': _('Circuit Termination'),
+                    },
+                ],
+            ),
+            TemplatePanel('dcim/panels/front_port_mappings.html'),
+        ],
+    )
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
         return {
         return {
@@ -3405,6 +3545,31 @@ class RearPortListView(generic.ObjectListView):
 @register_model_view(RearPort)
 @register_model_view(RearPort)
 class RearPortView(generic.ObjectView):
 class RearPortView(generic.ObjectView):
     queryset = RearPort.objects.all()
     queryset = RearPort.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.RearPortPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+            panels.InventoryItemsPanel(),
+        ],
+        right_panels=[
+            panels.ConnectionPanel(
+                trace_url_name='dcim:rearport_trace',
+                show_endpoints=False,
+                connect_options=[
+                    {'a_type': 'dcim.rearport', 'b_type': 'dcim.interface', 'label': _('Interface')},
+                    {'a_type': 'dcim.rearport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+                    {'a_type': 'dcim.rearport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+                    {
+                        'a_type': 'dcim.rearport',
+                        'b_type': 'circuits.circuittermination',
+                        'label': _('Circuit Termination'),
+                    },
+                ],
+            ),
+            TemplatePanel('dcim/panels/rear_port_mappings.html'),
+        ],
+    )
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
         return {
         return {
@@ -3481,6 +3646,19 @@ class ModuleBayListView(generic.ObjectListView):
 @register_model_view(ModuleBay)
 @register_model_view(ModuleBay)
 class ModuleBayView(generic.ObjectView):
 class ModuleBayView(generic.ObjectView):
     queryset = ModuleBay.objects.all()
     queryset = ModuleBay.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.ModuleBayPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            Panel(
+                title=_('Installed Module'),
+                template_name='dcim/panels/installed_module.html',
+            ),
+        ],
+    )
 
 
 
 
 @register_model_view(ModuleBay, 'add', detail=False)
 @register_model_view(ModuleBay, 'add', detail=False)
@@ -3543,6 +3721,19 @@ class DeviceBayListView(generic.ObjectListView):
 @register_model_view(DeviceBay)
 @register_model_view(DeviceBay)
 class DeviceBayView(generic.ObjectView):
 class DeviceBayView(generic.ObjectView):
     queryset = DeviceBay.objects.all()
     queryset = DeviceBay.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.DeviceBayPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            Panel(
+                title=_('Installed Device'),
+                template_name='dcim/panels/installed_device.html',
+            ),
+        ],
+    )
 
 
 
 
 @register_model_view(DeviceBay, 'add', detail=False)
 @register_model_view(DeviceBay, 'add', detail=False)
@@ -3686,6 +3877,13 @@ class InventoryItemListView(generic.ObjectListView):
 @register_model_view(InventoryItem)
 @register_model_view(InventoryItem)
 class InventoryItemView(generic.ObjectView):
 class InventoryItemView(generic.ObjectView):
     queryset = InventoryItem.objects.all()
     queryset = InventoryItem.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.InventoryItemPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+    )
 
 
 
 
 @register_model_view(InventoryItem, 'edit')
 @register_model_view(InventoryItem, 'edit')
@@ -3767,12 +3965,23 @@ class InventoryItemRoleListView(generic.ObjectListView):
 
 
 
 
 @register_model_view(InventoryItemRole)
 @register_model_view(InventoryItemRole)
-class InventoryItemRoleView(generic.ObjectView):
+class InventoryItemRoleView(GetRelatedModelsMixin, generic.ObjectView):
     queryset = InventoryItemRole.objects.all()
     queryset = InventoryItemRole.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.InventoryItemRolePanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            RelatedObjectsPanel(),
+            CustomFieldsPanel(),
+            CommentsPanel(),
+        ],
+    )
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
         return {
         return {
-            'inventoryitem_count': InventoryItem.objects.filter(role=instance).count(),
+            'related_models': self.get_related_models(request, instance),
         }
         }
 
 
 
 
@@ -3940,6 +4149,24 @@ class CableListView(generic.ObjectListView):
 @register_model_view(Cable)
 @register_model_view(Cable)
 class CableView(generic.ObjectView):
 class CableView(generic.ObjectView):
     queryset = Cable.objects.all()
     queryset = Cable.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.CablePanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+            CommentsPanel(),
+        ],
+        right_panels=[
+            Panel(
+                title=_('Termination A'),
+                template_name='dcim/panels/cable_termination_a.html',
+            ),
+            Panel(
+                title=_('Termination B'),
+                template_name='dcim/panels/cable_termination_b.html',
+            ),
+        ],
+    )
 
 
 
 
 @register_model_view(Cable, 'add', detail=False)
 @register_model_view(Cable, 'add', detail=False)
@@ -4072,12 +4299,23 @@ class VirtualChassisListView(generic.ObjectListView):
 @register_model_view(VirtualChassis)
 @register_model_view(VirtualChassis)
 class VirtualChassisView(generic.ObjectView):
 class VirtualChassisView(generic.ObjectView):
     queryset = VirtualChassis.objects.all()
     queryset = VirtualChassis.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.VirtualChassisPanel(),
+            TagsPanel(),
+            CustomFieldsPanel(),
+        ],
+        right_panels=[
+            panels.VirtualChassisMembersPanel(),
+            CommentsPanel(),
+        ],
+    )
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
-        members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
-
+        vc_members = Device.objects.restrict(request.user).filter(virtual_chassis=instance).order_by('vc_position')
         return {
         return {
-            'members': members,
+            'virtual_chassis': instance,
+            'vc_members': vc_members,
         }
         }
 
 
 
 
@@ -4317,6 +4555,27 @@ class PowerPanelListView(generic.ObjectListView):
 @register_model_view(PowerPanel)
 @register_model_view(PowerPanel)
 class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
 class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
     queryset = PowerPanel.objects.all()
     queryset = PowerPanel.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.PowerPanelPanel(),
+            TagsPanel(),
+            CommentsPanel(),
+        ],
+        right_panels=[
+            RelatedObjectsPanel(),
+            CustomFieldsPanel(),
+            ImageAttachmentsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                model='dcim.PowerFeed',
+                filters={'power_panel_id': lambda ctx: ctx['object'].pk},
+                actions=[
+                    actions.AddObject('dcim.PowerFeed', url_params={'power_panel': lambda ctx: ctx['object'].pk}),
+                ],
+            ),
+        ],
+    )
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
         return {
         return {
@@ -4380,6 +4639,23 @@ class PowerFeedListView(generic.ObjectListView):
 @register_model_view(PowerFeed)
 @register_model_view(PowerFeed)
 class PowerFeedView(generic.ObjectView):
 class PowerFeedView(generic.ObjectView):
     queryset = PowerFeed.objects.all()
     queryset = PowerFeed.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.PowerFeedPanel(),
+            panels.PowerFeedElectricalPanel(),
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            panels.ConnectionPanel(
+                trace_url_name='dcim:powerfeed_trace',
+                connect_options=[
+                    {'a_type': 'dcim.powerfeed', 'b_type': 'dcim.powerport', 'label': _('Power Port')},
+                ],
+            ),
+            CommentsPanel(),
+        ],
+    )
 
 
 
 
 @register_model_view(PowerFeed, 'add', detail=False)
 @register_model_view(PowerFeed, 'add', detail=False)
@@ -4448,6 +4724,23 @@ class VirtualDeviceContextListView(generic.ObjectListView):
 @register_model_view(VirtualDeviceContext)
 @register_model_view(VirtualDeviceContext)
 class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
 class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
     queryset = VirtualDeviceContext.objects.all()
     queryset = VirtualDeviceContext.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.VirtualDeviceContextPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            RelatedObjectsPanel(),
+            CommentsPanel(),
+            CustomFieldsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                model='dcim.Interface',
+                filters={'vdc_id': lambda ctx: ctx['object'].pk},
+            ),
+        ],
+    )
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
         return {
         return {
@@ -4516,6 +4809,16 @@ class MACAddressListView(generic.ObjectListView):
 @register_model_view(MACAddress)
 @register_model_view(MACAddress)
 class MACAddressView(generic.ObjectView):
 class MACAddressView(generic.ObjectView):
     queryset = MACAddress.objects.all()
     queryset = MACAddress.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.MACAddressPanel(),
+            TagsPanel(),
+            CustomFieldsPanel(),
+        ],
+        right_panels=[
+            CommentsPanel(),
+        ],
+    )
 
 
 
 
 @register_model_view(MACAddress, 'add', detail=False)
 @register_model_view(MACAddress, 'add', detail=False)

+ 0 - 86
netbox/templates/dcim/cable.html

@@ -1,87 +1 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load buttons %}
-{% load helpers %}
-{% load perms %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Cable" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Type" %}</th>
-            <td>{{ object.get_type_display|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Status" %}</th>
-            <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Profile" %}</th>
-            <td>{% badge object.get_profile_display %}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Tenant" %}</th>
-            <td>
-              {% if object.tenant.group %}
-                {{ object.tenant.group|linkify }} /
-              {% endif %}
-              {{ object.tenant|linkify|placeholder }}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Label" %}</th>
-            <td>{{ object.label|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Color" %}</th>
-            <td>
-              {% if object.color %}
-                <span class="color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
-              {% else %}
-                {{ ''|placeholder }}
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Length" %}</th>
-            <td>
-              {% if object.length is not None %}
-                {{ object.length|floatformat }} {{ object.get_length_unit_display }}
-              {% else %}
-                {{ ''|placeholder }}
-              {% endif %}
-            </td>
-          </tr>
-        </table>
-      </div>
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Termination" %} A</h2>
-        {% include 'dcim/inc/cable_termination.html' with terminations=object.a_terminations %}
-      </div>
-      <div class="card">
-        <h2 class="card-header">{% trans "Termination" %} B</h2>
-        {% include 'dcim/inc/cable_termination.html' with terminations=object.b_terminations %}
-      </div>
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 87
netbox/templates/dcim/consoleport.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,88 +7,3 @@
     <a href="{% url 'dcim:device_consoleports' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_consoleports' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Console Port" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Module" %}</th>
-                        <td>{{ object.module|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Type" %}</th>
-                        <td>{{ object.get_type_display }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Speed" %}</th>
-                        <td>{{ object.get_speed_display }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-          <div class="card">
-            <h2 class="card-header">{% trans "Connection" %}</h2>
-            {% if object.mark_connected %}
-              <div class="card-body">
-                <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
-                {% trans "Marked as connected" %}
-              </div>
-            {% elif object.cable %}
-              {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:consoleport_trace' %}
-            {% else %}
-              <div class="card-body text-muted">
-                {% trans "Not Connected" %}
-                {% if perms.dcim.add_cable %}
-                  <div class="dropdown float-end">
-                    <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
-                      <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
-                    </button>
-                    <ul class="dropdown-menu dropdown-menu-end">
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.consoleport&a_terminations={{ object.pk }}&b_terminations_type=dcim.consoleserverport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Console Server Port" %}</a>
-                      </li>
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.consoleport&a_terminations={{ object.pk }}&b_terminations_type=dcim.frontport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Front Port" %}</a>
-                      </li>
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.consoleport&a_terminations={{ object.pk }}&b_terminations_type=dcim.rearport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Rear Port" %}</a>
-                      </li>
-                    </ul>
-                  </div>
-                {% endif %}
-              </div>
-            {% endif %}
-          </div>
-          {% include 'dcim/inc/panels/inventory_items.html' %}
-          {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 87
netbox/templates/dcim/consoleserverport.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,88 +7,3 @@
     <a href="{% url 'dcim:device_consoleserverports' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_consoleserverports' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Console Server Port" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Module" %}</th>
-                        <td>{{ object.module|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Type" %}</th>
-                        <td>{{ object.get_type_display|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Speed" %}</th>
-                        <td>{{ object.get_speed_display|placeholder }}</td>
-                </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-          <div class="card">
-            <h2 class="card-header">{% trans "Connection" %}</h2>
-            {% if object.mark_connected %}
-              <div class="card-body">
-                <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
-                {% trans "Marked as connected" %}
-              </div>
-            {% elif object.cable %}
-              {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:consoleserverport_trace' %}
-            {% else %}
-              <div class="card-body text-muted">
-                {% trans "Not Connected" %}
-                {% if perms.dcim.add_cable %}
-                  <div class="dropdown float-end">
-                    <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
-                      <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
-                    </button>
-                    <ul class="dropdown-menu dropdown-menu-end">
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.consoleserverport&a_terminations={{ object.pk }}&b_terminations_type=dcim.consoleport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Console Port" %}</a>
-                      </li>
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.consoleserverport&a_terminations={{ object.pk }}&b_terminations_type=dcim.frontport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Front Port" %}</a>
-                      </li>
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.consoleserverport&a_terminations={{ object.pk }}&b_terminations_type=dcim.rearport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Rear Port" %}</a>
-                      </li>
-                    </ul>
-                  </div>
-                {% endif %}
-              </div>
-            {% endif %}
-          </div>
-          {% include 'dcim/inc/panels/inventory_items.html' %}
-          {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 62
netbox/templates/dcim/devicebay.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,63 +7,3 @@
     <a href="{% url 'dcim:device_devicebays' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_devicebays' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Device Bay" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-        {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/tags.html' %}
-        {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Installed Device" %}</h2>
-                {% if object.installed_device %}
-                    {% with device=object.installed_device %}
-                        <table class="table table-hover attr-table">
-                            <tr>
-                                <th scope="row">{% trans "Device" %}</th>
-                                <td>{{ device|linkify }}</td>
-                            </tr>
-                            <tr>
-                                <th scope="row">{% trans "Device Type" %}</th>
-                                <td>{{ device.device_type }}</td>
-                            </tr>
-                        </table>
-                    {% endwith %}
-                {% else %}
-                    <div class="card-body text-muted">
-                        {% trans "None" %}
-                    </div>
-                {% endif %}
-            </div>
-            {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 148
netbox/templates/dcim/frontport.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,149 +7,3 @@
     <a href="{% url 'dcim:device_frontports' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_frontports' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Front Port" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Module" %}</th>
-                        <td>{{ object.module|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Type" %}</th>
-                        <td>{{ object.get_type_display }}</td>
-                    </tr>
-                    <tr>
-                      <th scope="row">{% trans "Color" %}</th>
-                      <td>
-                        {% if object.color %}
-                          <span class="badge color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
-                        {% else %}
-                          {{ ''|placeholder }}
-                        {% endif %}
-                      </td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Positions" %}</th>
-                        <td>{{ object.positions }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% include 'dcim/inc/panels/inventory_items.html' %}
-            {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Connection" %}</h2>
-                {% if object.mark_connected %}
-                    <div class="card-body text-muted">
-                      <span class="text-success"><i class="mdi mdi-check-bold"></i></span> {% trans "Marked as Connected" %}
-                    </div>
-                {% elif object.cable %}
-                    <table class="table table-hover attr-table">
-                        <tr>
-                            <th scope="row">{% trans "Cable" %}</th>
-                            <td>
-                                {{ object.cable|linkify }}
-                                <a href="{% url 'dcim:frontport_trace' pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
-                                    <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
-                                </a>
-                            </td>
-                        </tr>
-                        <tr>
-                            <th scope="row">{% trans "Connection Status" %}</th>
-                            <td>
-                                {% if object.cable.status %}
-                                    <span class="badge text-bg-success">{{ object.cable.get_status_display }}</span>
-                                {% else %}
-                                    <span class="badge text-bg-info">{{ object.cable.get_status_display }}</span>
-                                {% endif %}
-                            </td>
-                        </tr>
-                    </table>
-                {% else %}
-                    <div class="card-body text-muted">
-                        {% trans "Not Connected" %}
-                        {% if perms.dcim.add_cable %}
-                            <div class="dropdown float-end">
-                                <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                                    <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
-                                </button>
-                                <ul class="dropdown-menu dropdown-menu-end">
-                                    <li>
-                                        <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.frontport&a_terminations={{ object.pk }}&b_terminations_type=dcim.interface&return_url={{ object.get_absolute_url }}">{% trans "Interface" %}</a>
-                                    </li>
-                                    <li>
-                                        <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.frontport&a_terminations={{ object.pk }}&b_terminations_type=dcim.consoleserverport&return_url={{ object.get_absolute_url }}">{% trans "Console Server Port" %}</a>
-                                    </li>
-                                    <li>
-                                        <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.frontport&a_terminations={{ object.pk }}&b_terminations_type=dcim.consoleport&return_url={{ object.get_absolute_url }}">{% trans "Console Port" %}</a>
-                                    </li>
-                                    <li>
-                                        <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.frontport&a_terminations={{ object.pk }}&b_terminations_type=dcim.frontport&return_url={{ object.get_absolute_url }}">{% trans "Front Port" %}</a>
-                                    </li>
-                                    <li>
-                                        <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.frontport&a_terminations={{ object.pk }}&b_terminations_type=dcim.rearport&return_url={{ object.get_absolute_url }}">{% trans "Rear Port" %}</a>
-                                    </li>
-                                    <li>
-                                        <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.frontport&a_terminations={{ object.pk }}&b_terminations_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">{% trans "Circuit Termination" %}</a>
-                                    </li>
-                                </ul>
-                            </div>
-                        {% endif %}
-                    </div>
-                {% endif %}
-            </div>
-            <div class="card">
-              <h2 class="card-header">{% trans "Port Mappings" %}</h2>
-              <table class="table table-hover">
-                {% if rear_port_mappings %}
-                  <thead>
-                    <tr>
-                      <th>{% trans "Position" %}</th>
-                      <th>{% trans "Rear Port" %}</th>
-                    </tr>
-                  </thead>
-                {% endif %}
-                {% for mapping in rear_port_mappings %}
-                  <tr>
-                    <td>{{ mapping.front_port_position }}</td>
-                    <td>
-                      <a href="{{ mapping.rear_port.get_absolute_url }}">{{ mapping.rear_port }}:{{ mapping.rear_port_position }}</a>
-                    </td>
-                  </tr>
-                {% empty %}
-                  {% trans "No mappings defined" %}
-                {% endfor %}
-              </table>
-            </div>
-            {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 436
netbox/templates/dcim/interface.html

@@ -1,7 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -19,436 +16,3 @@
   {% endif %}
   {% endif %}
   {{ block.super }}
   {{ block.super }}
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-  <div class="row mb-3">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Interface" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Device" %}</th>
-            <td>{{ object.device|linkify }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Module" %}</th>
-            <td>{{ object.module|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Label" %}</th>
-            <td>{{ object.label|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Type" %}</th>
-            <td>{{ object.get_type_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Speed/Duplex" %}</th>
-            <td>
-              {{ object.speed|humanize_speed|placeholder }} /
-              {{ object.get_duplex_display|placeholder }}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "MTU" %}</th>
-            <td>{{ object.mtu|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Enabled" %}</th>
-            <td>{% checkmark object.enabled %}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Management Only" %}</th>
-            <td>{% checkmark object.mgmt_only %}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }} </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "PoE Mode" %}</th>
-            <td>{{ object.get_poe_mode_display|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "PoE Type" %}</th>
-            <td>{{ object.get_poe_type_display|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "802.1Q Mode" %}</th>
-            <td>{{ object.get_mode_display|placeholder }}</td>
-          </tr>
-          {% if object.mode == 'q-in-q' %}
-            <tr>
-              <th scope="row">{% trans "Q-in-Q SVLAN" %}</th>
-              <td>{{ object.qinq_svlan|linkify|placeholder }}</td>
-            </tr>
-          {% elif object.mode %}
-            <tr>
-              <th scope="row">{% trans "Untagged VLAN" %}</th>
-              <td>{{ object.untagged_vlan|linkify|placeholder }}</td>
-            </tr>
-          {% endif %}
-          <tr>
-            <th scope="row">{% trans "Transmit power (dBm)" %}</th>
-            <td>{{ object.tx_power|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Tunnel" %}</th>
-            <td>{{ object.tunnel_termination.tunnel|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "L2VPN" %}</th>
-            <td>{{ object.l2vpn_termination.l2vpn|linkify|placeholder }}</td>
-          </tr>
-        </table>
-      </div>
-      <div class="card">
-        <h2 class="card-header">{% trans "Related Interfaces" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Parent" %}</th>
-            <td>{{ object.parent|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Bridge" %}</th>
-            <td>{{ object.bridge|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Bridged Interfaces" %}</th>
-            <td>
-              {% if bridge_interfaces %}
-                {% for interface in bridge_interfaces %}
-                  {{ interface|linkify }}
-                  {% if not forloop.last %}<br />{% endif %}
-                {% endfor %}
-              {% else %}
-                {{ ''|placeholder }}
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "LAG" %}</th>
-            <td>{{ object.lag|linkify|placeholder }}</td>
-          </tr>
-        </table>
-      </div>
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panel_table.html' with table=vdc_table heading="Virtual Device Contexts" %}
-      <div class="card">
-        <h2 class="card-header">{% trans "Addressing" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "MAC Address" %}</th>
-            <td>
-              {% if object.primary_mac_address %}
-                <span class="font-monospace">{{ object.primary_mac_address|linkify }}</span>
-                <span class="badge text-bg-primary">{% trans "Primary" %}</span>
-              {% else %}
-                {{ ''|placeholder }}
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "WWN" %}</th>
-            <td>
-              {% if object.wwn %}
-                <span class="font-monospace">{{ object.wwn }}</span>
-              {% else %}
-                {{ ''|placeholder }}
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "VRF" %}</th>
-            <td>{{ object.vrf|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "VLAN Translation" %}</th>
-            <td>{{ object.vlan_translation_policy|linkify|placeholder }}</td>
-          </tr>
-        </table>
-      </div>
-      {% if object.is_virtual and object.virtual_circuit_termination %}
-        <div class="card">
-          <h2 class="card-header">{% trans "Virtual Circuit" %}</h2>
-          <table class="table table-hover attr-table">
-            <tr>
-              <th scope="row">{% trans "Provider" %}</th>
-              <td>{{ object.virtual_circuit_termination.virtual_circuit.provider|linkify }}</td>
-            </tr>
-            <tr>
-              <th scope="row">{% trans "Provider Network" %}</th>
-              <td>{{ object.virtual_circuit_termination.virtual_circuit.provider_network|linkify }}</td>
-            </tr>
-            <tr>
-              <th scope="row">{% trans "Circuit ID" %}</th>
-              <td>{{ object.virtual_circuit_termination.virtual_circuit|linkify }}</td>
-            </tr>
-            <tr>
-              <th scope="row">{% trans "Role" %}</th>
-              <td>{{ object.virtual_circuit_termination.get_role_display }}</td>
-            </tr>
-            <tr>
-              <th scope="row">{% trans "Connections" %}</th>
-              <td>
-                {% for termination in object.virtual_circuit_termination.peer_terminations %}
-                  <a href="{{ termination.interface.parent_object.get_absolute_url }}">{{ termination.interface.parent_object }}</a>
-                  <i class="mdi mdi-chevron-right"></i>
-                  <a href="{{ termination.interface.get_absolute_url }}">{{ termination.interface }}</a>
-                  ({{ termination.get_role_display }})
-                  {% if not forloop.last %}<br />{% endif %}
-                {% endfor %}
-              </td>
-            </tr>
-          </table>
-        </div>
-      {% elif not object.is_virtual %}
-        <div class="card">
-          <h2 class="card-header">{% trans "Connection" %}</h2>
-          {% if object.mark_connected %}
-            <div class="card-body">
-              <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
-              {% trans "Marked as Connected" %}
-            </div>
-          {% elif object.cable %}
-            {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:interface_trace' %}
-          {% elif object.wireless_link %}
-            <table class="table table-hover attr-table">
-              <tr>
-                <th scope="row">{% trans "Wireless Link" %}</th>
-                <td>
-                  {{ object.wireless_link|linkify }}
-                  <a href="{% url 'dcim:interface_trace' pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
-                    <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
-                  </a>
-                </td>
-              </tr>
-              {% with peer_interface=object.link_peers.0 %}
-                <tr>
-                  <th scope="row">{% trans "Device" %}</th>
-                  <td>{{ peer_interface.device|linkify }}</td>
-                </tr>
-                <tr>
-                  <th scope="row">{% trans "Name" %}</th>
-                  <td>{{ peer_interface|linkify }}</td>
-                </tr>
-                <tr>
-                  <th scope="row">{% trans "Type" %}</th>
-                  <td>{{ peer_interface.get_type_display }}</td>
-                </tr>
-              {% endwith %}
-            </table>
-          {% else %}
-            <div class="card-body text-muted">
-              {% trans "Not Connected" %}
-              {% if object.is_wired and perms.dcim.add_cable %}
-                <div class="dropdown float-end">
-                  <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                    <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
-                  </button>
-                  <ul class="dropdown-menu dropdown-menu-end">
-                    <li>
-                      <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=dcim.interface&return_url={{ object.get_absolute_url }}">{% trans "Interface" %}</a>
-                    </li>
-                    <li>
-                      <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=dcim.frontport&return_url={{ object.get_absolute_url }}">{% trans "Front Port" %}</a>
-                    </li>
-                    <li>
-                      <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=dcim.rearport&return_url={{ object.get_absolute_url }}">{% trans "Rear Port" %}</a>
-                    </li>
-                    <li>
-                      <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">{% trans "Circuit Termination" %}</a>
-                    </li>
-                  </ul>
-                </div>
-              {% elif object.is_wireless and perms.wireless.add_wirelesslink %}
-                <div class="dropdown float-end">
-                  <a href="{% url 'wireless:wirelesslink_add' %}?interface_a={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
-                    <span class="mdi mdi-wifi-plus" aria-hidden="true"></span> {% trans "Connect" %}
-                  </a>
-                </div>
-              {% endif %}
-            </div>
-          {% endif %}
-        </div>
-      {% endif %}
-      {% if object.is_wireless %}
-        <div class="card">
-          <h2 class="card-header">{% trans "Wireless" %}</h2>
-          {% with peer=object.connected_endpoints.0 %}
-            <table class="table table-hover">
-              <thead>
-                <tr>
-                  <th></th>
-                  <th>{% trans "Local" %}</th>
-                  {% if peer %}
-                    <th>{% trans "Peer" %}</th>
-                  {% endif %}
-                </tr>
-              </thead>
-              <tr>
-                <th scope="row">{% trans "Role" %}</th>
-                <td>{{ object.get_rf_role_display|placeholder }}</td>
-                {% if peer %}
-                  <td>{{ peer.get_rf_role_display|placeholder }}</td>
-                {% endif %}
-              </tr>
-              <tr>
-                <th scope="row">{% trans "Channel" %}</th>
-                <td>{{ object.get_rf_channel_display|placeholder }}</td>
-                {% if peer %}
-                  <td{% if peer.rf_channel != object.rf_channel %} class="text-danger"{% endif %}>
-                    {{ peer.get_rf_channel_display|placeholder }}
-                  </td>
-                {% endif %}
-              </tr>
-              <tr>
-                <th scope="row">{% trans "Channel Frequency" %}</th>
-                <td>
-                  {% if object.rf_channel_frequency %}
-                    {{ object.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %}
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                </td>
-                {% if peer %}
-                  <td{% if peer.rf_channel_frequency != object.rf_channel_frequency %} class="text-danger"{% endif %}>
-                    {% if peer.rf_channel_frequency %}
-                      {{ peer.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %}
-                    {% else %}
-                      {{ ''|placeholder }}
-                    {% endif %}
-                  </td>
-                {% endif %}
-              </tr>
-              <tr>
-                <th scope="row">{% trans "Channel Width" %}</th>
-                <td>
-                  {% if object.rf_channel_width %}
-                    {{ object.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %}
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                </td>
-                {% if peer %}
-                  <td{% if peer.rf_channel_width != object.rf_channel_width %} class="text-danger"{% endif %}>
-                    {% if peer.rf_channel_width %}
-                      {{ peer.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %}
-                    {% else %}
-                      {{ ''|placeholder }}
-                    {% endif %}
-                  </td>
-                {% endif %}
-              </tr>
-            </table>
-          {% endwith %}
-        </div>
-        <div class="card">
-          <h2 class="card-header">{% trans "Wireless LANs" %}</h2>
-          <table class="table table-hover">
-            <thead>
-              <tr>
-                <th>{% trans "Group" %}</th>
-                <th>{% trans "SSID" %}</th>
-              </tr>
-            </thead>
-            <tbody>
-              {% for wlan in object.wireless_lans.all %}
-                <tr>
-                  <td>{{ wlan.group|linkify|placeholder }}</td>
-                  <td>{{ wlan|linkify:"ssid" }}</td>
-                </tr>
-              {% empty %}
-                <tr>
-                  <td colspan="3" class="text-muted">{% trans "None" %}</td>
-                </tr>
-              {% endfor %}
-            </tbody>
-          </table>
-        </div>
-      {% endif %}
-      {% include 'ipam/inc/panels/fhrp_groups.html' %}
-      {% include 'dcim/inc/panels/inventory_items.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">
-          {% trans "IP Addresses" %}
-          {% if perms.ipam.add_ipaddress %}
-            <div class="card-actions">
-              <a href="{% url 'ipam:ipaddress_add' %}?device={{ object.device.pk }}&interface={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
-                <span class="mdi mdi-plus-thick" aria-hidden="true"></span> {% trans "Add IP Address" %}
-              </a>
-            </div>
-          {% endif %}
-        </h2>
-        {% htmx_table 'ipam:ipaddress_list' interface_id=object.pk %}
-      </div>
-    </div>
-  </div>
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">
-          {% trans "MAC Addresses" %}
-          {% if perms.dcim.add_macaddress %}
-            <div class="card-actions">
-              <a href="{% url 'dcim:macaddress_add' %}?device={{ object.device.pk }}&interface={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
-                <span class="mdi mdi-plus-thick" aria-hidden="true"></span> {% trans "Add MAC Address" %}
-              </a>
-            </div>
-          {% endif %}
-        </h2>
-        {% htmx_table 'dcim:macaddress_list' interface_id=object.pk %}
-      </div>
-    </div>
-  </div>
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">{% trans "VLANs" %}</h2>
-        {% htmx_table 'ipam:vlan_list' interface_id=object.pk %}
-      </div>
-    </div>
-  </div>
-  {% if object.is_lag %}
-    <div class="row mb-3">
-      <div class="col col-md-12">
-        {% include 'inc/panel_table.html' with table=lag_interfaces_table heading="LAG Members" %}
-      </div>
-    </div>
-  {% endif %}
-  {% if object.vlan_translation_policy %}
-    <div class="row mb-3">
-      <div class="col col-md-12">
-        {% include 'inc/panel_table.html' with table=vlan_translation_table heading="VLAN Translation" %}
-      </div>
-    </div>
-  {% endif %}
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      {% include 'inc/panel_table.html' with table=bridge_interfaces_table heading="Bridged Interfaces" %}
-    </div>
-  </div>
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      {% include 'inc/panel_table.html' with table=child_interfaces_table heading="Child Interfaces" %}
-    </div>
-  </div>
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 3 - 0
netbox/templates/dcim/interface/attrs/mac_address.html

@@ -0,0 +1,3 @@
+{% load helpers i18n %}
+<span class="font-monospace">{{ value|linkify }}</span>
+<span class="badge text-bg-primary">{% trans "Primary" %}</span>

+ 2 - 0
netbox/templates/dcim/interface/attrs/speed.html

@@ -0,0 +1,2 @@
+{% load helpers %}
+{{ value|humanize_speed }}

+ 0 - 73
netbox/templates/dcim/inventoryitem.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,74 +7,3 @@
     <a href="{% url 'dcim:device_inventory' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_inventory' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row mb-3">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Inventory Item" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Parent Item" %}</th>
-                        <td>{{ object.parent|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                      <th scope="row">{% trans "Status" %}</th>
-                      <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Role" %}</th>
-                        <td>{{ object.role|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Component" %}</th>
-                        <td>{{ object.component|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Manufacturer" %}</th>
-                        <td>{{ object.manufacturer|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Part ID" %}</th>
-                        <td>{{ object.part_id|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Serial" %}</th>
-                        <td>{{ object.serial|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Asset Tag" %}</th>
-                        <td>{{ object.asset_tag|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-            {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row mb-3">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 52
netbox/templates/dcim/inventoryitemrole.html

@@ -1,53 +1 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block breadcrumbs %}
-  <li class="breadcrumb-item"><a href="{% url 'dcim:inventoryitemrole_list' %}">{% trans "Inventory Item Roles" %}</a></li>
-{% endblock %}
-
-{% block content %}
-<div class="row mb-3">
-	<div class="col col-12 col-md-6">
-    <div class="card">
-      <h2 class="card-header">{% trans "Inventory Item Role" %}</h2>
-      <table class="table table-hover attr-table">
-        <tr>
-          <th scope="row">{% trans "Name" %}</th>
-          <td>{{ object.name }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Description" %}</th>
-          <td>{{ object.description|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Color" %}</th>
-          <td>
-            <span class="badge color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
-          </td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Inventory Items" %}</th>
-          <td>
-            <a href="{% url 'dcim:inventoryitem_list' %}?role_id={{ object.pk }}">{{ inventoryitem_count }}</a>
-          </td>
-        </tr>
-      </table>
-    </div>
-    {% include 'inc/panels/tags.html' %}
-    {% plugin_left_page object %}
-	</div>
-	<div class="col col-12 col-md-6">
-    {% include 'inc/panels/comments.html' %}
-    {% include 'inc/panels/custom_fields.html' %}
-    {% plugin_right_page object %}
-  </div>
-</div>
-<div class="row mb-3">
-	<div class="col col-md-12">
-    {% plugin_full_width_page object %}
-  </div>
-</div>
-{% endblock %}

+ 0 - 54
netbox/templates/dcim/macaddress.html

@@ -1,55 +1 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "MAC Address" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "MAC Address" %}</th>
-            <td>
-              <span id="macaddress_{{ object.pk }}">{{ object.mac_address|placeholder }}</span>
-              {% copy_content object.pk prefix="macaddress_" %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Assignment" %}</th>
-            <td>
-              {% if object.assigned_object %}
-                {{ object.assigned_object.parent_object|linkify }} /
-                {{ object.assigned_object|linkify }}
-              {% else %}
-                {{ ''|placeholder }}
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Primary for interface" %}</th>
-            <td>{% checkmark object.is_primary %}</td>
-          </tr>
-        </table>
-      </div>
-      {% include 'inc/panels/tags.html' %}
-      {% include 'inc/panels/custom_fields.html' %}
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/comments.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 82
netbox/templates/dcim/modulebay.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,83 +7,3 @@
     <a href="{% url 'dcim:device_modulebays' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_modulebays' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Module Bay" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Device" %}</th>
-            <td>
-              <a href="{% url 'dcim:device_modulebays' pk=object.device.pk %}">{{ object.device }}</a>
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Module" %}</th>
-            <td>{{ object.module|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Label" %}</th>
-            <td>{{ object.label|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Position" %}</th>
-            <td>{{ object.position|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-        </table>
-      </div>
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      <div class="card">
-        <h2 class="card-header">{% trans "Installed Module" %}</h2>
-        {% if object.installed_module %}
-          {% with module=object.installed_module %}
-            <table class="table table-hover attr-table">
-              <tr>
-                <th scope="row">{% trans "Module" %}</th>
-                <td>{{ module|linkify }}</td>
-              </tr>
-              <tr>
-                <th scope="row">{% trans "Manufacturer" %}</th>
-                <td>{{ module.module_type.manufacturer|linkify }}</td>
-              </tr>
-              <tr>
-                <th scope="row">{% trans "Module Type" %}</th>
-                <td>{{ module.module_type|linkify }}</td>
-              </tr>
-              <tr>
-                <th scope="row">{% trans "Serial Number" %}</th>
-                <td class="font-monospace">{{ module.serial|placeholder }}</td>
-              </tr>
-              <tr>
-                <th scope="row">{% trans "Asset Tag" %}</th>
-                <td class="font-monospace">{{ module.asset_tag|placeholder }}</td>
-              </tr>
-            </table>
-          {% endwith %}
-        {% else %}
-          <div class="card-body text-muted">{% trans "None" %}</div>
-        {% endif %}
-      </div>
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 6 - 0
netbox/templates/dcim/panels/cable_termination_a.html

@@ -0,0 +1,6 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  {% include 'dcim/inc/cable_termination.html' with terminations=object.a_terminations %}
+{% endblock panel_content %}

+ 6 - 0
netbox/templates/dcim/panels/cable_termination_b.html

@@ -0,0 +1,6 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  {% include 'dcim/inc/cable_termination.html' with terminations=object.b_terminations %}
+{% endblock panel_content %}

+ 40 - 0
netbox/templates/dcim/panels/component_inventory_items.html

@@ -0,0 +1,40 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  <table class="table table-hover">
+    <thead>
+      <tr>
+        <th>{% trans "Name" %}</th>
+        <th>{% trans "Label" %}</th>
+        <th>{% trans "Role" %}</th>
+        <th></th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for item in object.inventory_items.all %}
+        <tr>
+          <td>{{ item|linkify:"name" }}</td>
+          <td>{{ item.label|placeholder }}</td>
+          <td>{{ item.role|linkify|placeholder }}</td>
+          <td class="text-end d-print-none">
+            {% if perms.dcim.change_inventoryitem %}
+              <a href="{% url 'dcim:inventoryitem_edit' pk=item.pk %}?return_url={{ object.get_absolute_url }}" class="btn btn-sm btn-warning" title="{% trans "Edit" %}">
+                <i class="mdi mdi-pencil" aria-hidden="true"></i>
+              </a>
+            {% endif %}
+            {% if perms.dcim.delete_inventoryitem %}
+              <a href="{% url 'dcim:inventoryitem_delete' pk=item.pk %}?return_url={{ object.get_absolute_url }}" class="btn btn-sm btn-danger" title="{% trans "Delete" %}">
+                <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i>
+              </a>
+            {% endif %}
+          </td>
+        </tr>
+      {% empty %}
+        <tr>
+          <td colspan="4" class="text-muted">{% trans "None" %}</td>
+        </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+{% endblock panel_content %}

+ 96 - 0
netbox/templates/dcim/panels/connection.html

@@ -0,0 +1,96 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  {% if object.mark_connected %}
+    <div class="card-body">
+      <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
+      {% trans "Marked as connected" %}
+    </div>
+  {% elif object.cable %}
+    {% if show_endpoints %}
+      <table class="table table-hover attr-table">
+        <tr>
+          <th scope="row">{% trans "Cable" %}</th>
+          <td>
+            {{ object.cable|linkify }}
+            <a href="{% url trace_url_name pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
+              <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
+            </a>
+          </td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Path status" %}</th>
+          <td>
+            {% if object.path.is_complete and object.path.is_active %}
+              <span class="badge text-bg-success">{% trans "Reachable" %}</span>
+            {% else %}
+              <span class="badge text-bg-danger">{% trans "Not Reachable" %}</span>
+            {% endif %}
+          </td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Path endpoints" %}</th>
+          <td>
+            {% for endpoint in object.connected_endpoints %}
+              {% if endpoint.parent_object %}
+                {{ endpoint.parent_object|linkify }}
+                <i class="mdi mdi-chevron-right"></i>
+              {% endif %}
+              {{ endpoint|linkify }}
+              {% if not forloop.last %}<br />{% endif %}
+            {% empty %}
+              {{ ''|placeholder }}
+            {% endfor %}
+          </td>
+        </tr>
+      </table>
+    {% else %}
+      <table class="table table-hover attr-table">
+        <tr>
+          <th scope="row">{% trans "Cable" %}</th>
+          <td>
+            {{ object.cable|linkify }}
+            <a href="{% url trace_url_name pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
+              <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
+            </a>
+          </td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Connection status" %}</th>
+          <td>
+            {% if object.cable.status %}
+              <span class="badge text-bg-success">{{ object.cable.get_status_display }}</span>
+            {% else %}
+              <span class="badge text-bg-info">{{ object.cable.get_status_display }}</span>
+            {% endif %}
+          </td>
+        </tr>
+      </table>
+    {% endif %}
+  {% else %}
+    <div class="card-body text-muted">
+      {% trans "Not Connected" %}
+      {% if perms.dcim.add_cable %}
+        {% if connect_options|length > 1 %}
+          <div class="dropdown float-end">
+            <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
+              <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
+            </button>
+            <ul class="dropdown-menu dropdown-menu-end">
+              {% for option in connect_options %}
+                <li>
+                  <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type={{ option.a_type }}&a_terminations={{ object.pk }}&b_terminations_type={{ option.b_type }}&return_url={{ object.get_absolute_url }}">{{ option.label }}</a>
+                </li>
+              {% endfor %}
+            </ul>
+          </div>
+        {% elif connect_options|length == 1 %}
+          <a href="{% url 'dcim:cable_add' %}?a_terminations_type={{ connect_options.0.a_type }}&a_terminations={{ object.pk }}&b_terminations_type={{ connect_options.0.b_type }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary float-end">
+            <i class="mdi mdi-ethernet-cable" aria-hidden="true"></i> {% trans "Connect" %}
+          </a>
+        {% endif %}
+      {% endif %}
+    </div>
+  {% endif %}
+{% endblock panel_content %}

+ 29 - 0
netbox/templates/dcim/panels/front_port_mappings.html

@@ -0,0 +1,29 @@
+{% load i18n %}
+
+<div class="card">
+  <h2 class="card-header">{% trans "Port Mappings" %}</h2>
+  <table class="table table-hover">
+    {% if rear_port_mappings %}
+      <thead>
+        <tr>
+          <th>{% trans "Position" %}</th>
+          <th>{% trans "Rear Port" %}</th>
+        </tr>
+      </thead>
+    {% endif %}
+    <tbody>
+      {% for mapping in rear_port_mappings %}
+        <tr>
+          <td>{{ mapping.front_port_position }}</td>
+          <td>
+            <a href="{{ mapping.rear_port.get_absolute_url }}">{{ mapping.rear_port }}:{{ mapping.rear_port_position }}</a>
+          </td>
+        </tr>
+      {% empty %}
+        <tr>
+          <td class="text-muted">{% trans "No mappings defined" %}</td>
+        </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+</div>

+ 21 - 0
netbox/templates/dcim/panels/installed_device.html

@@ -0,0 +1,21 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  {% if object.installed_device %}
+    {% with device=object.installed_device %}
+      <table class="table table-hover attr-table">
+        <tr>
+          <th scope="row">{% trans "Device" %}</th>
+          <td>{{ device|linkify }}</td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Device type" %}</th>
+          <td>{{ device.device_type }}</td>
+        </tr>
+      </table>
+    {% endwith %}
+  {% else %}
+    <div class="card-body text-muted">{% trans "None" %}</div>
+  {% endif %}
+{% endblock panel_content %}

+ 33 - 0
netbox/templates/dcim/panels/installed_module.html

@@ -0,0 +1,33 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  {% if object.installed_module %}
+    {% with module=object.installed_module %}
+      <table class="table table-hover attr-table">
+        <tr>
+          <th scope="row">{% trans "Module" %}</th>
+          <td>{{ module|linkify }}</td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Manufacturer" %}</th>
+          <td>{{ module.module_type.manufacturer|linkify }}</td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Module type" %}</th>
+          <td>{{ module.module_type|linkify }}</td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Serial number" %}</th>
+          <td class="font-monospace">{{ module.serial|placeholder }}</td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Asset tag" %}</th>
+          <td class="font-monospace">{{ module.asset_tag|placeholder }}</td>
+        </tr>
+      </table>
+    {% endwith %}
+  {% else %}
+    <div class="card-body text-muted">{% trans "None" %}</div>
+  {% endif %}
+{% endblock panel_content %}

+ 105 - 0
netbox/templates/dcim/panels/interface_connection.html

@@ -0,0 +1,105 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  {% if object.mark_connected %}
+    <div class="card-body">
+      <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
+      {% trans "Marked as connected" %}
+    </div>
+  {% elif object.cable %}
+    <table class="table table-hover attr-table">
+      <tr>
+        <th scope="row">{% trans "Cable" %}</th>
+        <td>
+          {{ object.cable|linkify }}
+          <a href="{% url 'dcim:interface_trace' pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
+            <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
+          </a>
+        </td>
+      </tr>
+      <tr>
+        <th scope="row">{% trans "Path status" %}</th>
+        <td>
+          {% if object.path.is_complete and object.path.is_active %}
+            <span class="badge text-bg-success">{% trans "Reachable" %}</span>
+          {% else %}
+            <span class="badge text-bg-danger">{% trans "Not Reachable" %}</span>
+          {% endif %}
+        </td>
+      </tr>
+      <tr>
+        <th scope="row">{% trans "Path endpoints" %}</th>
+        <td>
+          {% for endpoint in object.connected_endpoints %}
+            {% if endpoint.parent_object %}
+              {{ endpoint.parent_object|linkify }}
+              <i class="mdi mdi-chevron-right"></i>
+            {% endif %}
+            {{ endpoint|linkify }}
+            {% if not forloop.last %}<br />{% endif %}
+          {% empty %}
+            {{ ''|placeholder }}
+          {% endfor %}
+        </td>
+      </tr>
+    </table>
+  {% elif object.wireless_link %}
+    <table class="table table-hover attr-table">
+      <tr>
+        <th scope="row">{% trans "Wireless Link" %}</th>
+        <td>
+          {{ object.wireless_link|linkify }}
+          <a href="{% url 'dcim:interface_trace' pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
+            <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
+          </a>
+        </td>
+      </tr>
+      {% with peer_interface=object.link_peers.0 %}
+        <tr>
+          <th scope="row">{% trans "Device" %}</th>
+          <td>{{ peer_interface.device|linkify }}</td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Name" %}</th>
+          <td>{{ peer_interface|linkify }}</td>
+        </tr>
+        <tr>
+          <th scope="row">{% trans "Type" %}</th>
+          <td>{{ peer_interface.get_type_display }}</td>
+        </tr>
+      {% endwith %}
+    </table>
+  {% else %}
+    <div class="card-body text-muted">
+      {% trans "Not Connected" %}
+      {% if object.is_wired and perms.dcim.add_cable %}
+        <div class="dropdown float-end">
+          <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
+            <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
+          </button>
+          <ul class="dropdown-menu dropdown-menu-end">
+            <li>
+              <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=dcim.interface&return_url={{ object.get_absolute_url }}">{% trans "Interface" %}</a>
+            </li>
+            <li>
+              <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=dcim.frontport&return_url={{ object.get_absolute_url }}">{% trans "Front Port" %}</a>
+            </li>
+            <li>
+              <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=dcim.rearport&return_url={{ object.get_absolute_url }}">{% trans "Rear Port" %}</a>
+            </li>
+            <li>
+              <a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.interface&a_terminations={{ object.pk }}&b_terminations_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">{% trans "Circuit Termination" %}</a>
+            </li>
+          </ul>
+        </div>
+      {% elif object.is_wireless and perms.wireless.add_wirelesslink %}
+        <div class="dropdown float-end">
+          <a href="{% url 'wireless:wirelesslink_add' %}?interface_a={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
+            <span class="mdi mdi-wifi-plus" aria-hidden="true"></span> {% trans "Connect" %}
+          </a>
+        </div>
+      {% endif %}
+    </div>
+  {% endif %}
+{% endblock panel_content %}

+ 35 - 0
netbox/templates/dcim/panels/interface_virtual_circuit.html

@@ -0,0 +1,35 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  <table class="table table-hover attr-table">
+    <tr>
+      <th scope="row">{% trans "Provider" %}</th>
+      <td>{{ object.virtual_circuit_termination.virtual_circuit.provider|linkify }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Provider Network" %}</th>
+      <td>{{ object.virtual_circuit_termination.virtual_circuit.provider_network|linkify }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Circuit ID" %}</th>
+      <td>{{ object.virtual_circuit_termination.virtual_circuit|linkify }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Role" %}</th>
+      <td>{{ object.virtual_circuit_termination.get_role_display }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Connections" %}</th>
+      <td>
+        {% for termination in object.virtual_circuit_termination.peer_terminations %}
+          <a href="{{ termination.interface.parent_object.get_absolute_url }}">{{ termination.interface.parent_object }}</a>
+          <i class="mdi mdi-chevron-right"></i>
+          <a href="{{ termination.interface.get_absolute_url }}">{{ termination.interface }}</a>
+          ({{ termination.get_role_display }})
+          {% if not forloop.last %}<br />{% endif %}
+        {% endfor %}
+      </td>
+    </tr>
+  </table>
+{% endblock panel_content %}

+ 72 - 0
netbox/templates/dcim/panels/interface_wireless.html

@@ -0,0 +1,72 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  {% with peer=object.connected_endpoints.0 %}
+    <table class="table table-hover attr-table">
+      <thead>
+        <tr class="border-bottom">
+          <th></th>
+          <th>{% trans "Local" %}</th>
+          {% if peer %}
+            <th>{% trans "Peer" %}</th>
+          {% endif %}
+        </tr>
+      </thead>
+      <tr>
+        <th scope="row">{% trans "Role" %}</th>
+        <td>{{ object.get_rf_role_display|placeholder }}</td>
+        {% if peer %}
+          <td>{{ peer.get_rf_role_display|placeholder }}</td>
+        {% endif %}
+      </tr>
+      <tr>
+        <th scope="row">{% trans "Channel" %}</th>
+        <td>{{ object.get_rf_channel_display|placeholder }}</td>
+        {% if peer %}
+          <td{% if peer.rf_channel != object.rf_channel %} class="text-danger"{% endif %}>
+            {{ peer.get_rf_channel_display|placeholder }}
+          </td>
+        {% endif %}
+      </tr>
+      <tr>
+        <th scope="row">{% trans "Channel frequency" %}</th>
+        <td>
+          {% if object.rf_channel_frequency %}
+            {{ object.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %}
+          {% else %}
+            {{ ''|placeholder }}
+          {% endif %}
+        </td>
+        {% if peer %}
+          <td{% if peer.rf_channel_frequency != object.rf_channel_frequency %} class="text-danger"{% endif %}>
+            {% if peer.rf_channel_frequency %}
+              {{ peer.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %}
+            {% else %}
+              {{ ''|placeholder }}
+            {% endif %}
+          </td>
+        {% endif %}
+      </tr>
+      <tr>
+        <th scope="row">{% trans "Channel width" %}</th>
+        <td>
+          {% if object.rf_channel_width %}
+            {{ object.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %}
+          {% else %}
+            {{ ''|placeholder }}
+          {% endif %}
+        </td>
+        {% if peer %}
+          <td{% if peer.rf_channel_width != object.rf_channel_width %} class="text-danger"{% endif %}>
+            {% if peer.rf_channel_width %}
+              {{ peer.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %}
+            {% else %}
+              {{ ''|placeholder }}
+            {% endif %}
+          </td>
+        {% endif %}
+      </tr>
+    </table>
+  {% endwith %}
+{% endblock panel_content %}

+ 25 - 0
netbox/templates/dcim/panels/interface_wireless_lans.html

@@ -0,0 +1,25 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  <table class="table table-hover">
+    <thead>
+      <tr>
+        <th>{% trans "Group" %}</th>
+        <th>{% trans "SSID" %}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for wlan in object.wireless_lans.all %}
+        <tr>
+          <td>{{ wlan.group|linkify|placeholder }}</td>
+          <td>{{ wlan|linkify:"ssid" }}</td>
+        </tr>
+      {% empty %}
+        <tr>
+          <td colspan="2" class="text-muted">{% trans "None" %}</td>
+        </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+{% endblock panel_content %}

+ 29 - 0
netbox/templates/dcim/panels/rear_port_mappings.html

@@ -0,0 +1,29 @@
+{% load i18n %}
+
+<div class="card">
+  <h2 class="card-header">{% trans "Port Mappings" %}</h2>
+  <table class="table table-hover">
+    {% if front_port_mappings %}
+      <thead>
+        <tr>
+          <th>{% trans "Position" %}</th>
+          <th>{% trans "Front Port" %}</th>
+        </tr>
+      </thead>
+    {% endif %}
+    <tbody>
+      {% for mapping in front_port_mappings %}
+        <tr>
+          <td>{{ mapping.rear_port_position }}</td>
+          <td>
+            <a href="{{ mapping.front_port.get_absolute_url }}">{{ mapping.front_port }}:{{ mapping.front_port_position }}</a>
+          </td>
+        </tr>
+      {% empty %}
+        <tr>
+          <td class="text-muted">{% trans "No mappings defined" %}</td>
+        </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+</div>

+ 1 - 1
netbox/templates/dcim/panels/virtual_chassis_members.html

@@ -17,7 +17,7 @@
           <td>{{ vc_member|linkify }}</td>
           <td>{{ vc_member|linkify }}</td>
           <td>{% badge vc_member.vc_position show_empty=True %}</td>
           <td>{% badge vc_member.vc_position show_empty=True %}</td>
           <td>
           <td>
-            {% if object.virtual_chassis.master == vc_member %}
+            {% if virtual_chassis.master == vc_member %}
               {% checkmark True %}
               {% checkmark True %}
             {% else %}
             {% else %}
               {{ ''|placeholder }}
               {{ ''|placeholder }}

+ 0 - 128
netbox/templates/dcim/powerfeed.html

@@ -1,9 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load buttons %}
-{% load static %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
   {{ block.super }}
   {{ block.super }}
@@ -13,126 +8,3 @@
     <li class="breadcrumb-item"><a href="{% url 'dcim:powerfeed_list' %}?rack_id={{ object.rack.pk }}">{{ object.rack }}</a></li>
     <li class="breadcrumb-item"><a href="{% url 'dcim:powerfeed_list' %}?rack_id={{ object.rack.pk }}">{{ object.rack }}</a></li>
   {% endif %}
   {% endif %}
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-<div class="row">
-	<div class="col col-12 col-md-6">
-        <div class="card">
-            <h2 class="card-header">{% trans "Power Feed" %}</h2>
-            <table class="table table-hover attr-table">
-                <tr>
-                    <th scope="row">{% trans "Power Panel" %}</th>
-                    <td>{{ object.power_panel|linkify }}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Rack" %}</th>
-                    <td>{{ object.rack|linkify|placeholder }}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Type" %}</th>
-                    <td>{% badge object.get_type_display bg_color=object.get_type_color %}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Status" %}</th>
-                    <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Description" %}</th>
-                    <td>{{ object.description|placeholder }}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Tenant" %}</th>
-                    <td>
-                        {% if object.tenant.group %}
-                          {{ object.tenant.group|linkify }} /
-                        {% endif %}
-                        {{ object.tenant|linkify|placeholder }}
-                    </td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Connected Device" %}</th>
-                    <td>
-                        {% if object.connected_endpoints %}
-                            {{ object.connected_endpoints.0.device|linkify }} ({{ object.connected_endpoints.0|linkify:"name" }})
-                        {% else %}
-                            {{ ''|placeholder }}
-                        {% endif %}
-                    </td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Utilization (Allocated" %})</th>
-                    {% with utilization=object.connected_endpoints.0.get_power_draw %}
-                        {% if utilization %}
-                            <td>
-                                {{ utilization.allocated }}{% trans "VA" %} / {{ object.available_power }}{% trans "VA" %}
-                                {% if object.available_power > 0 %}
-                                    {% utilization_graph utilization.allocated|percentage:object.available_power %}
-                                {% endif %}
-                            </td>
-                        {% else %}
-                            <td>{{ ''|placeholder }}</td>
-                        {% endif %}
-                    {% endwith %}
-                </tr>
-            </table>
-        </div>
-        <div class="card">
-            <h2 class="card-header">{% trans "Electrical Characteristics" %}</h2>
-            <table class="table table-hover attr-table">
-                <tr>
-                    <th scope="row">{% trans "Supply" %}</th>
-                    <td>{{ object.get_supply_display }}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Voltage" %}</th>
-                    <td>{{ object.voltage }}{% trans "V" context "Abbreviation for volts" %}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Amperage" %}</th>
-                    <td>{{ object.amperage }}{% trans "A" context "Abbreviation for amperes" %}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Phase" %}</th>
-                    <td>{{ object.get_phase_display }}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Max Utilization" %}</th>
-                    <td>{{ object.max_utilization }}%</td>
-                </tr>
-            </table>
-        </div>
-        {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/tags.html' %}
-        {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Connection" %}</h2>
-        {% if object.mark_connected %}
-          <div class="card-body">
-            <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
-            {% trans "Marked as connected" %}
-          </div>
-        {% elif object.cable %}
-          {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:powerfeed_trace' %}
-        {% else %}
-          <div class="card-body text-muted">
-            {% trans "Not connected" %}
-            {% if perms.dcim.add_cable %}
-              <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.powerfeed&a_terminations={{ object.pk }}&b_terminations_type=dcim.powerport&return_url={{ object.get_absolute_url }}" class="btn btn-primary float-end">
-                <i class="mdi mdi-ethernet-cable" aria-hidden="true"></i> {% trans "Connect" %}
-              </a>
-            {% endif %}
-          </div>
-        {% endif %}
-      </div>
-      {% include 'inc/panels/comments.html' %}
-      {% plugin_right_page object %}
-    </div>
-</div>
-<div class="row">
-    <div class="col col-md-12">
-        {% plugin_full_width_page object %}
-    </div>
-</div>
-{% endblock %}

+ 6 - 0
netbox/templates/dcim/powerfeed/attrs/connected_device.html

@@ -0,0 +1,6 @@
+{% load helpers %}
+{% if value %}
+  {{ value.0.device|linkify }} ({{ value.0|linkify:"name" }})
+{% else %}
+  {{ ''|placeholder }}
+{% endif %}

+ 15 - 0
netbox/templates/dcim/powerfeed/attrs/utilization.html

@@ -0,0 +1,15 @@
+{% load helpers i18n %}
+{% if value %}
+  {% with utilization=value.0.get_power_draw %}
+    {% if utilization %}
+      {{ utilization.allocated }}{% trans "VA" %} / {{ object.available_power }}{% trans "VA" %}
+      {% if object.available_power > 0 %}
+        {% utilization_graph utilization.allocated|percentage:object.available_power %}
+      {% endif %}
+    {% else %}
+      {{ ''|placeholder }}
+    {% endif %}
+  {% endwith %}
+{% else %}
+  {{ ''|placeholder }}
+{% endif %}

+ 0 - 92
netbox/templates/dcim/poweroutlet.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,93 +7,3 @@
     <a href="{% url 'dcim:device_poweroutlets' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_poweroutlets' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row mb-3">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Power Outlet" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Module" %}</th>
-                        <td>{{ object.module|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Type" %}</th>
-                        <td>{{ object.get_type_display }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Status" %}</th>
-                        <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                    <tr>
-                      <th scope="row">{% trans "Color" %}</th>
-                      <td>
-                        {% if object.color %}
-                          <span class="badge color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
-                        {% else %}
-                          {{ ''|placeholder }}
-                        {% endif %}
-                      </td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Power Port" %}</th>
-                        <td>{{ object.power_port|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Feed Leg" %}</th>
-                        <td>{{ object.get_feed_leg_display|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-          <div class="card">
-            <h2 class="card-header">{% trans "Connection" %}</h2>
-            {% if object.mark_connected %}
-              <div class="card-body">
-                <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
-                {% trans "Marked as Connected" %}
-              </div>
-            {% elif object.cable %}
-              {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:poweroutlet_trace' %}
-            {% else %}
-              <div class="card-body text-muted">
-                {% trans "Not Connected" %}
-                {% if perms.dcim.add_cable %}
-                  <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.poweroutlet&a_terminations={{ object.pk }}&b_terminations_type=dcim.powerport&return_url={{ object.get_absolute_url }}" title="{% trans "Connect" %}" class="btn btn-primary float-end">
-                    <i class="mdi mdi-ethernet-cable" aria-hidden="true"></i> {% trans "Connect" %}
-                  </a>
-                {% endif %}
-              </div>
-            {% endif %}
-          </div>
-          {% include 'dcim/inc/panels/inventory_items.html' %}
-          {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row mb-3">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 73
netbox/templates/dcim/powerpanel.html

@@ -1,8 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
   {{ block.super }}
   {{ block.super }}
@@ -11,72 +7,3 @@
     <li class="breadcrumb-item">{{ object.location|linkify }}</li>
     <li class="breadcrumb-item">{{ object.location|linkify }}</li>
   {% endif %}
   {% endif %}
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-<div class="row">
-	<div class="col col-12 col-md-6">
-    <div class="card">
-      <h2 class="card-header">{% trans "Power Panel" %}</h2>
-      <table class="table table-hover attr-table">
-        <tr>
-          <th scope="row">{% trans "Site" %}</th>
-          <td>{{ object.site|linkify }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Location" %}</th>
-          <td>{{ object.location|linkify|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Description" %}</th>
-          <td>{{ object.description|placeholder }}</td>
-        </tr>
-      </table>
-    </div>
-    {% include 'inc/panels/tags.html' %}
-    {% include 'inc/panels/comments.html' %}
-    {% plugin_left_page object %}
-  </div>
-	<div class="col col-12 col-md-6">
-    {% include 'inc/panels/related_objects.html' %}
-    {% include 'inc/panels/custom_fields.html' %}
-    {% include 'inc/panels/image_attachments.html' %}
-    {% plugin_right_page object %}
-  </div>
-</div>
-<div class="row my-3">
-  <div class="col col-md-12">
-    <form method="post">
-      {% csrf_token %}
-      <div class="card">
-        <h2 class="card-header">{% trans "Power Feeds" %}</h2>
-        {% htmx_table 'dcim:powerfeed_list' power_panel_id=object.pk %}
-        <div class="card-footer d-print-none">
-          {% if perms.dcim.change_powerfeed %}
-            <button type="submit" name="_edit" {% formaction %}="{% url 'dcim:powerfeed_bulk_edit' %}?return_url={% url 'dcim:powerpanel' pk=object.pk %}" class="btn btn-warning">
-              <span class="mdi mdi-pencil" aria-hidden="true"></span> {% trans "Edit" %}
-            </button>
-          {% endif %}
-          {% if perms.dcim.delete_cable %}
-            <button type="submit" name="_disconnect" {% formaction %}="{% url 'dcim:powerfeed_bulk_disconnect' %}?return_url={% url 'dcim:powerpanel' pk=object.pk %}" class="btn btn-outline-danger">
-              <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> {% trans "Disconnect" %}
-            </button>
-          {% endif %}
-          {% if perms.dcim.delete_powerfeed %}
-            <button type="submit" name="_delete" {% formaction %}="{% url 'dcim:powerfeed_bulk_delete' %}?return_url={% url 'dcim:powerpanel' pk=object.pk %}" class="btn btn-danger">
-              <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> {% trans "Delete" %}
-            </button>
-          {% endif %}
-          {% if perms.dcim.add_powerfeed %}
-            <div class="float-end">
-              <a href="{% url 'dcim:powerfeed_add' %}?power_panel={{ object.pk }}&return_url={% url 'dcim:powerpanel' pk=object.pk %}" class="btn btn-primary">
-                <span class="mdi mdi-plus-thick" aria-hidden="true"></span> {% trans "Add Power Feeds" %}
-              </a>
-            </div>
-          {% endif %}
-        </div>
-      </div>
-    </form>
-    {% plugin_full_width_page object %}
-  </div>
-</div>
-{% endblock %}

+ 0 - 88
netbox/templates/dcim/powerport.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,89 +7,3 @@
     <a href="{% url 'dcim:device_powerports' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_powerports' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row mb-3">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Power Port" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Module" %}</th>
-                        <td>{{ object.module|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Type" %}</th>
-                        <td>{{ object.get_type_display|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Maximum Draw" %}</th>
-                        <td>{{ object.maximum_draw|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Allocated Draw" %}</th>
-                        <td>{{ object.allocated_draw|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-          <div class="card">
-            <h2 class="card-header">{% trans "Connection" %}</h2>
-            {% if object.mark_connected %}
-              <div class="card-body">
-                <span class="text-success"><i class="mdi mdi-check-bold"></i></span>
-                {% trans "Marked as Connected" %}
-              </div>
-            {% elif object.cable %}
-              {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:powerport_trace' %}
-            {% else %}
-              <div class="card-body text-muted">
-                {% trans "Not Connected" %}
-                {% if perms.dcim.add_cable %}
-                  <span class="dropdown float-end">
-                    <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                      <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
-                    </button>
-                    <ul class="dropdown-menu dropdown-menu-end">
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.powerport&a_terminations={{ object.pk }}&b_terminations_type=dcim.poweroutlet&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Power Outlet" %}</a>
-                      </li>
-                      <li>
-                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.powerport&a_terminations={{ object.pk }}&b_terminations_type=dcim.powerfeed&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Power Feed" %}</a>
-                      </li>
-                    </ul>
-                  </span>
-                {% endif %}
-              </div>
-            {% endif %}
-          </div>
-          {% include 'dcim/inc/panels/inventory_items.html' %}
-          {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 142
netbox/templates/dcim/rearport.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 {% load i18n %}
 
 
 {% block breadcrumbs %}
 {% block breadcrumbs %}
@@ -9,143 +7,3 @@
     <a href="{% url 'dcim:device_rearports' pk=object.device.pk %}">{{ object.device }}</a>
     <a href="{% url 'dcim:device_rearports' pk=object.device.pk %}">{{ object.device }}</a>
   </li>
   </li>
 {% endblock %}
 {% endblock %}
-
-{% block content %}
-    <div class="row">
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Rear Port" %}</h2>
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">{% trans "Device" %}</th>
-                        <td>{{ object.device|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Module" %}</th>
-                        <td>{{ object.module|linkify|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Name" %}</th>
-                        <td>{{ object.name }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Label" %}</th>
-                        <td>{{ object.label|placeholder }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Type" %}</th>
-                        <td>{{ object.get_type_display }}</td>
-                    </tr>
-                    <tr>
-                      <th scope="row">{% trans "Color" %}</th>
-                      <td>
-                        {% if object.color %}
-                          <span class="badge color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
-                        {% else %}
-                          {{ ''|placeholder }}
-                        {% endif %}
-                      </td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Positions" %}</th>
-                        <td>{{ object.positions }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">{% trans "Description" %}</th>
-                        <td>{{ object.description|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% include 'dcim/inc/panels/inventory_items.html' %}
-            {% plugin_left_page object %}
-        </div>
-        <div class="col col-12 col-md-6">
-            <div class="card">
-                <h2 class="card-header">{% trans "Connection" %}</h2>
-                {% if object.mark_connected %}
-                    <div class="card-body text-muted">
-                      <span class="text-success"><i class="mdi mdi-check-bold"></i></span> {% trans "Marked as Connected" %}
-                    </div>
-                {% elif object.cable %}
-                    <table class="table table-hover attr-table">
-                        <tr>
-                            <th scope="row">{% trans "Cable" %}</th>
-                            <td>
-                                {{ object.cable|linkify }}
-                                <a href="{% url 'dcim:rearport_trace' pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
-                                    <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
-                                </a>
-                            </td>
-                        </tr>
-                        <tr>
-                            <th scope="row">{% trans "Connection Status" %}</th>
-                            <td>
-                                {% if object.cable.status %}
-                                    <span class="badge text-bg-success">{{ object.cable.get_status_display }}</span>
-                                {% else %}
-                                    <span class="badge text-bg-info">{{ object.cable.get_status_display }}</span>
-                                {% endif %}
-                            </td>
-                        </tr>
-                    </table>
-                {% else %}
-                    <div class="card-body text-muted">
-                        {% trans "Not connected" %}
-                        {% if perms.dcim.add_cable %}
-                            <span class="dropdown float-end">
-                                <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                                    <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
-                                </button>
-                                <ul class="dropdown-menu dropdown-menu-end">
-                                    <li>
-                                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.rearport&a_terminations={{ object.pk }}&b_terminations_type=dcim.interface&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Interface" %}</a>
-                                    </li>
-                                    <li>
-                                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.rearport&a_terminations={{ object.pk }}&b_terminations_type=dcim.frontport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Front Port" %}</a>
-                                    </li>
-                                    <li>
-                                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.rearport&a_terminations={{ object.pk }}&b_terminations_type=dcim.rearport&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Rear Port" %}</a>
-                                    </li>
-                                    <li>
-                                        <a href="{% url 'dcim:cable_add' %}?a_terminations_type=dcim.rearport&a_terminations={{ object.pk }}&b_terminations_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}" class="dropdown-item">{% trans "Circuit Termination" %}</a>
-                                    </li>
-                                </ul>
-                            </span>
-                        {% endif %}
-                    </div>
-                {% endif %}
-            </div>
-            <div class="card">
-              <h2 class="card-header">{% trans "Port Mappings" %}</h2>
-              <table class="table table-hover">
-                {% if front_port_mappings %}
-                  <thead>
-                    <tr>
-                      <th>{% trans "Position" %}</th>
-                      <th>{% trans "Front Port" %}</th>
-                    </tr>
-                  </thead>
-                {% endif %}
-                {% for mapping in front_port_mappings %}
-                  <tr>
-                    <td>{{ mapping.rear_port_position }}</td>
-                    <td>
-                      <a href="{{ mapping.front_port.get_absolute_url }}">{{ mapping.front_port }}:{{ mapping.front_port_position }}</a>
-                    </td>
-                  </tr>
-                {% empty %}
-                  {% trans "No mappings defined" %}
-                {% endfor %}
-              </table>
-            </div>
-            {% plugin_right_page object %}
-        </div>
-    </div>
-    <div class="row">
-        <div class="col col-md-12">
-            {% plugin_full_width_page object %}
-        </div>
-    </div>
-{% endblock %}

+ 0 - 89
netbox/templates/dcim/virtualchassis.html

@@ -1,90 +1 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-<div class="row">
-	<div class="col col-12 col-md-4">
-    <div class="card">
-      <h2 class="card-header">{% trans "Virtual Chassis" %}</h2>
-      <table class="table table-hover attr-table">
-        <tr>
-          <th scope="row">{% trans "Domain" %}</th>
-          <td>{{ object.domain|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Master" %}</th>
-          <td>{{ object.master|linkify }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Description" %}</th>
-          <td>{{ object.description|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">Members</th>
-          <td>
-            {% if object.member_count %}
-              <a href="{% url 'dcim:device_list' %}?virtual_chassis_id={{ object.pk }}">{{ object.member_count }}</a>
-            {% else %}
-              {{ object.member_count }}
-            {% endif %}
-          </td>
-        </tr>
-      </table>
-    </div>
-    {% include 'inc/panels/tags.html' %}
-    {% include 'inc/panels/custom_fields.html' %}
-    {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-8">
-      <div class="card">
-        <h2 class="card-header">
-          {% trans "Members" %}
-          {% if perms.dcim.change_virtualchassis %}
-            <div class="card-actions">
-              <a href="{% url 'dcim:virtualchassis_add_member' pk=object.pk %}?site={{ object.master.site.pk }}&rack={{ object.master.rack.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
-                <i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Member" %}
-              </a>
-            </div>
-          {% endif %}
-        </h2>
-        <table class="table table-hover object-list">
-          <thead>
-            <tr>
-              <th>{% trans "Device" %}</th>
-              <th>{% trans "Position" %}</th>
-              <th>{% trans "Master" %}</th>
-              <th>{% trans "Priority" %}</th>
-            </tr>
-          </thead>
-          {% for vc_member in members %}
-            <tr{% if vc_member == device %} class="info"{% endif %}>
-              <td>
-                {{ vc_member|linkify }}
-              </td>
-              <td>
-                {% badge vc_member.vc_position show_empty=True %}
-              </td>
-              <td>
-                {% if object.master == vc_member %}
-                  {% checkmark True %}
-                {% endif %}
-              </td>
-              <td>
-                {{ vc_member.vc_priority|placeholder }}
-              </td>
-            </tr>
-          {% endfor %}
-        </table>
-      </div>
-      {% include 'inc/panels/comments.html' %}
-      {% plugin_right_page object %}
-	</div>
-</div>
-<div class="row">
-  <div class="col col-md-12">
-    {% plugin_full_width_page object %}
-  </div>
-</div>
-{% endblock %}

+ 0 - 86
netbox/templates/dcim/virtualdevicecontext.html

@@ -1,87 +1 @@
 {% extends 'generic/object.html' %}
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block breadcrumbs %}
-  <li class="breadcrumb-item"><a href="{% url 'dcim:virtualdevicecontext_list' %}">{% trans "Virtual Device Contexts" %}</a></li>
-{% endblock %}
-
-{% block content %}
-<div class="row mb-3">
-	<div class="col col-12 col-md-6">
-    <div class="card">
-      <h2 class="card-header">{% trans "Virtual Device Context" %}</h2>
-      <table class="table table-hover attr-table">
-        <tr>
-          <th scope="row">{% trans "Name" %}</th>
-          <td>{{ object.name }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Device" %}</th>
-          <td>{{ object.device|linkify }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Identifier" %}</th>
-          <td>{{ object.identifier|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Primary IPv4" %}</th>
-          <td>
-            {% if object.primary_ip4 %}
-              <a href="{{ object.primary_ip4.get_absolute_url }}" id="primary_ip4">{{ object.primary_ip4 }}</a>
-              {% copy_content "primary_ip4" %}
-           {% else %}
-              <span class="text-muted">—</span>
-           {% endif %}
-          </td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Primary IPv6" %}</th>
-          <td>
-            {% if object.primary_ip6 %}
-              <a href="{{ object.primary_ip6.get_absolute_url }}" id="primary_ip6">{{ object.primary_ip6 }}</a>
-              {% copy_content "primary_ip6" %}
-           {% else %}
-              <span class="text-muted">—</span>
-           {% endif %}
-          </td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Tenant" %}</th>
-          <td>
-            {% if object.tenant.group %}
-              {{ object.tenant.group|linkify }} /
-            {% endif %}
-            {{ object.tenant|linkify|placeholder }}
-          </td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Interfaces" %}</th>
-          <td>
-            <a href="{% url 'dcim:interface_list' %}?vdc_id={{ object.pk }}">{{ object.interfaces.count }}</a>
-          </td>
-        </tr>
-      </table>
-    </div>
-    {% plugin_left_page object %}
-    {% include 'inc/panels/tags.html' %}
-  </div>
-  <div class="col col-12 col-md-6">
-    {% include 'inc/panels/related_objects.html' %}
-    {% include 'inc/panels/comments.html' %}
-    {% include 'inc/panels/custom_fields.html' %}
-    {% plugin_right_page object %}
-  </div>
-</div>
-<div class="row mb-3">
-  <div class="col col-md-12">
-    <div class="card">
-      <h2 class="card-header">{% trans "Interfaces" %}</h2>
-      {% htmx_table 'dcim:interface_list' vdc_id=object.pk %}
-    </div>
-    {% plugin_full_width_page object %}
-  </div>
-</div>
-{% endblock %}