diff --git a/netbox/templates/wireless/attrs/auth_psk.html b/netbox/templates/wireless/attrs/auth_psk.html
new file mode 100644
index 000000000..3401a3eb4
--- /dev/null
+++ b/netbox/templates/wireless/attrs/auth_psk.html
@@ -0,0 +1,3 @@
+{% load i18n %}
+{{ value }}
+
diff --git a/netbox/templates/wireless/inc/authentication_attrs.html b/netbox/templates/wireless/inc/authentication_attrs.html
deleted file mode 100644
index 555a14f8b..000000000
--- a/netbox/templates/wireless/inc/authentication_attrs.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{% load helpers %}
-{% load i18n %}
-
-
-
-
-
- | {% trans "Type" %} |
- {{ object.get_auth_type_display|placeholder }} |
-
-
- | {% trans "Cipher" %} |
- {{ object.get_auth_cipher_display|placeholder }} |
-
-
- | {% trans "PSK" %} |
-
- {{ object.auth_psk|placeholder }}
- {% if object.auth_psk %}
-
- {% endif %}
- |
-
-
-
diff --git a/netbox/templates/wireless/inc/wirelesslink_interface.html b/netbox/templates/wireless/inc/wirelesslink_interface.html
deleted file mode 100644
index 8b61a5564..000000000
--- a/netbox/templates/wireless/inc/wirelesslink_interface.html
+++ /dev/null
@@ -1,51 +0,0 @@
-{% load helpers %}
-{% load i18n %}
-
-
-
- | {% trans "Device" %} |
- {{ interface.device|linkify }} |
-
-
- | {% trans "Interface" %} |
- {{ interface|linkify }} |
-
-
- | {% trans "Type" %} |
-
- {{ interface.get_type_display }}
- |
-
-
- | {% trans "Role" %} |
-
- {{ interface.get_rf_role_display|placeholder }}
- |
-
-
- | {% trans "Channel" %} |
-
- {{ interface.get_rf_channel_display|placeholder }}
- |
-
-
- | {% trans "Channel Frequency" %} |
-
- {% if interface.rf_channel_frequency %}
- {{ interface.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" context "Abbreviation for megahertz" %}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
- | {% trans "Channel Width" %} |
-
- {% if interface.rf_channel_width %}
- {{ interface.rf_channel_width|floatformat:"-3" }} {% trans "MHz" context "Abbreviation for megahertz" %}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
diff --git a/netbox/templates/wireless/panels/wirelesslink_interface_a.html b/netbox/templates/wireless/panels/wirelesslink_interface_a.html
new file mode 100644
index 000000000..80aaa2d04
--- /dev/null
+++ b/netbox/templates/wireless/panels/wirelesslink_interface_a.html
@@ -0,0 +1,48 @@
+{% load helpers %}
+{% load i18n %}
+
+
+
+
+
+ | {% trans "Device" %} |
+ {{ object.interface_a.device|linkify }} |
+
+
+ | {% trans "Interface" %} |
+ {{ object.interface_a|linkify }} |
+
+
+ | {% trans "Type" %} |
+ {{ object.interface_a.get_type_display }} |
+
+
+ | {% trans "Role" %} |
+ {{ object.interface_a.get_rf_role_display|placeholder }} |
+
+
+ | {% trans "Channel" %} |
+ {{ object.interface_a.get_rf_channel_display|placeholder }} |
+
+
+ | {% trans "Channel Frequency" %} |
+
+ {% if object.interface_a.rf_channel_frequency %}
+ {{ object.interface_a.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" context "Abbreviation for megahertz" %}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ |
+
+
+ | {% trans "Channel Width" %} |
+
+ {% if object.interface_a.rf_channel_width %}
+ {{ object.interface_a.rf_channel_width|floatformat:"-3" }} {% trans "MHz" context "Abbreviation for megahertz" %}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ |
+
+
+
diff --git a/netbox/templates/wireless/panels/wirelesslink_interface_b.html b/netbox/templates/wireless/panels/wirelesslink_interface_b.html
new file mode 100644
index 000000000..2538bfef1
--- /dev/null
+++ b/netbox/templates/wireless/panels/wirelesslink_interface_b.html
@@ -0,0 +1,48 @@
+{% load helpers %}
+{% load i18n %}
+
+
+
+
+
+ | {% trans "Device" %} |
+ {{ object.interface_b.device|linkify }} |
+
+
+ | {% trans "Interface" %} |
+ {{ object.interface_b|linkify }} |
+
+
+ | {% trans "Type" %} |
+ {{ object.interface_b.get_type_display }} |
+
+
+ | {% trans "Role" %} |
+ {{ object.interface_b.get_rf_role_display|placeholder }} |
+
+
+ | {% trans "Channel" %} |
+ {{ object.interface_b.get_rf_channel_display|placeholder }} |
+
+
+ | {% trans "Channel Frequency" %} |
+
+ {% if object.interface_b.rf_channel_frequency %}
+ {{ object.interface_b.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" context "Abbreviation for megahertz" %}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ |
+
+
+ | {% trans "Channel Width" %} |
+
+ {% if object.interface_b.rf_channel_width %}
+ {{ object.interface_b.rf_channel_width|floatformat:"-3" }} {% trans "MHz" context "Abbreviation for megahertz" %}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ |
+
+
+
diff --git a/netbox/templates/wireless/wirelesslan.html b/netbox/templates/wireless/wirelesslan.html
index e2aa1f94b..f15e1d050 100644
--- a/netbox/templates/wireless/wirelesslan.html
+++ b/netbox/templates/wireless/wirelesslan.html
@@ -1,74 +1 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "SSID" %} |
- {{ object.ssid }} |
-
-
- | {% trans "Group" %} |
- {{ object.group|linkify|placeholder }} |
-
-
- | {% trans "Status" %} |
- {% badge object.get_status_display bg_color=object.get_status_color %} |
-
-
- | {% trans "Scope" %} |
- {% if object.scope %}
- {{ object.scope|linkify }} ({% trans object.scope_type.name %}) |
- {% else %}
- {{ ''|placeholder }} |
- {% endif %}
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "VLAN" %} |
- {{ object.vlan|linkify|placeholder }} |
-
-
- | {% trans "Tenant" %} |
-
- {% if object.tenant.group %}
- {{ object.tenant.group|linkify }} /
- {% endif %}
- {{ object.tenant|linkify|placeholder }}
- |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/comments.html' %}
- {% plugin_left_page object %}
-
-
- {% include 'wireless/inc/authentication_attrs.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% plugin_right_page object %}
-
-
-
-
-
-
-
- {% render_table interfaces_table 'inc/table.html' %}
- {% include 'inc/paginator.html' with paginator=interfaces_table.paginator page=interfaces_table.page %}
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/wireless/wirelesslangroup.html b/netbox/templates/wireless/wirelesslangroup.html
index 5440ffe4f..26873c437 100644
--- a/netbox/templates/wireless/wirelesslangroup.html
+++ b/netbox/templates/wireless/wirelesslangroup.html
@@ -1,7 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
{% load i18n %}
{% block breadcrumbs %}
@@ -18,53 +15,3 @@
{% endif %}
{% endblock extra_controls %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Parent" %} |
- {{ object.parent|linkify|placeholder }} |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/comments.html' %}
- {% plugin_left_page object %}
-
-
- {% include 'inc/panels/related_objects.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% plugin_right_page object %}
-
-
-
-
-
-
- {% htmx_table 'wireless:wirelesslangroup_list' parent_id=object.pk %}
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/wireless/wirelesslink.html b/netbox/templates/wireless/wirelesslink.html
index c1a80aba0..f15e1d050 100644
--- a/netbox/templates/wireless/wirelesslink.html
+++ b/netbox/templates/wireless/wirelesslink.html
@@ -1,68 +1 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-
-
-
-
- {% include 'wireless/inc/wirelesslink_interface.html' with interface=object.interface_a %}
-
-
-
-
-
- | {% trans "Status" %} |
- {% badge object.get_status_display bg_color=object.get_status_color %} |
-
-
- | {% trans "SSID" %} |
- {{ object.ssid|placeholder }} |
-
-
- | {% trans "Tenant" %} |
-
- {% if object.tenant.group %}
- {{ object.tenant.group|linkify }} /
- {% endif %}
- {{ object.tenant|linkify|placeholder }}
- |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Distance" %} |
-
- {% if object.distance is not None %}
- {{ object.distance|floatformat }} {{ object.get_distance_unit_display }}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/comments.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% include 'wireless/inc/wirelesslink_interface.html' with interface=object.interface_b %}
-
- {% include 'wireless/inc/authentication_attrs.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/wireless/ui/__init__.py b/netbox/wireless/ui/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/netbox/wireless/ui/panels.py b/netbox/wireless/ui/panels.py
new file mode 100644
index 000000000..a0522853c
--- /dev/null
+++ b/netbox/wireless/ui/panels.py
@@ -0,0 +1,35 @@
+from django.utils.translation import gettext_lazy as _
+
+from netbox.ui import attrs, panels
+
+
+class WirelessLANGroupPanel(panels.NestedGroupObjectPanel):
+ pass
+
+
+class WirelessLANPanel(panels.ObjectAttributesPanel):
+ ssid = attrs.TextAttr('ssid', label=_('SSID'))
+ group = attrs.RelatedObjectAttr('group', linkify=True)
+ status = attrs.ChoiceAttr('status')
+ scope = attrs.GenericForeignKeyAttr('scope', linkify=True)
+ description = attrs.TextAttr('description')
+ vlan = attrs.RelatedObjectAttr('vlan', label=_('VLAN'), linkify=True)
+ tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+
+
+class WirelessLANAuthenticationPanel(panels.ObjectAttributesPanel):
+ title = _('Authentication')
+
+ auth_type = attrs.ChoiceAttr('auth_type', label=_('Type'))
+ auth_cipher = attrs.ChoiceAttr('auth_cipher', label=_('Cipher'))
+ auth_psk = attrs.TemplatedAttr('auth_psk', label=_('PSK'), template_name='wireless/attrs/auth_psk.html')
+
+
+class WirelessLinkPropertiesPanel(panels.ObjectAttributesPanel):
+ title = _('Link Properties')
+
+ status = attrs.ChoiceAttr('status')
+ ssid = attrs.TextAttr('ssid', label=_('SSID'))
+ tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+ description = attrs.TextAttr('description')
+ distance = attrs.NumericAttr('distance', unit_accessor='get_distance_unit_display')
diff --git a/netbox/wireless/views.py b/netbox/wireless/views.py
index 101b0553b..4bd79a329 100644
--- a/netbox/wireless/views.py
+++ b/netbox/wireless/views.py
@@ -1,10 +1,23 @@
+from django.utils.translation import gettext_lazy as _
+
from dcim.models import Interface
+from extras.ui.panels import CustomFieldsPanel, TagsPanel
+from netbox.ui import actions, layout
+from netbox.ui.layout import Column, Row
+from netbox.ui.panels import (
+ CommentsPanel,
+ ObjectsTablePanel,
+ PluginContentPanel,
+ RelatedObjectsPanel,
+ TemplatePanel,
+)
from netbox.views import generic
from utilities.query import count_related
from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
from .models import *
+from .ui import panels
#
# Wireless LAN groups
@@ -28,6 +41,33 @@ class WirelessLANGroupListView(generic.ObjectListView):
@register_model_view(WirelessLANGroup)
class WirelessLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
queryset = WirelessLANGroup.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.WirelessLANGroupPanel(),
+ TagsPanel(),
+ CommentsPanel(),
+ ],
+ right_panels=[
+ RelatedObjectsPanel(),
+ CustomFieldsPanel(),
+ ],
+ bottom_panels=[
+ ObjectsTablePanel(
+ model='wireless.WirelessLANGroup',
+ title=_('Child Groups'),
+ filters={'parent_id': lambda ctx: ctx['object'].pk},
+ actions=[
+ actions.AddObject(
+ 'wireless.WirelessLANGroup',
+ label=_('Add Wireless LAN Group'),
+ url_params={
+ 'parent': lambda ctx: ctx['object'].pk,
+ }
+ ),
+ ],
+ ),
+ ],
+ )
def get_extra_context(self, request, instance):
groups = instance.get_descendants(include_self=True)
@@ -105,17 +145,24 @@ class WirelessLANListView(generic.ObjectListView):
@register_model_view(WirelessLAN)
class WirelessLANView(generic.ObjectView):
queryset = WirelessLAN.objects.all()
-
- def get_extra_context(self, request, instance):
- attached_interfaces = Interface.objects.restrict(request.user, 'view').filter(
- wireless_lans=instance
- )
- interfaces_table = tables.WirelessLANInterfacesTable(attached_interfaces)
- interfaces_table.configure(request)
-
- return {
- 'interfaces_table': interfaces_table,
- }
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.WirelessLANPanel(),
+ TagsPanel(),
+ CommentsPanel(),
+ ],
+ right_panels=[
+ panels.WirelessLANAuthenticationPanel(),
+ CustomFieldsPanel(),
+ ],
+ bottom_panels=[
+ ObjectsTablePanel(
+ model='dcim.Interface',
+ title=_('Attached Interfaces'),
+ filters={'wireless_lan_id': lambda ctx: ctx['object'].pk},
+ ),
+ ],
+ )
@register_model_view(WirelessLAN, 'add', detail=False)
@@ -173,6 +220,28 @@ class WirelessLinkListView(generic.ObjectListView):
@register_model_view(WirelessLink)
class WirelessLinkView(generic.ObjectView):
queryset = WirelessLink.objects.all()
+ layout = layout.Layout(
+ Row(
+ Column(
+ TemplatePanel('wireless/panels/wirelesslink_interface_a.html'),
+ panels.WirelessLinkPropertiesPanel(),
+ TagsPanel(),
+ CommentsPanel(),
+ PluginContentPanel('left_page'),
+ ),
+ Column(
+ TemplatePanel('wireless/panels/wirelesslink_interface_b.html'),
+ panels.WirelessLANAuthenticationPanel(),
+ CustomFieldsPanel(),
+ PluginContentPanel('right_page'),
+ ),
+ ),
+ Row(
+ Column(
+ PluginContentPanel('full_width_page'),
+ ),
+ ),
+ )
@register_model_view(WirelessLink, 'add', detail=False)