Closes #20923: Migrate wireless app views to declarative UI layouts (#21646)

* #20923: Migrate wireless app views to declarative UI layouts

Convert WirelessLANGroup, WirelessLAN, and WirelessLink detail views
from legacy HTML templates to declarative Python layout definitions.

New files:
- wireless/ui/panels.py: Panel classes for all three model detail views
- templates/wireless/attrs/auth_psk.html: Secret toggle for PSK field
- templates/wireless/panels/wirelesslink_interface_{a,b}.html: Interface
  panels for WirelessLink detail view

Removed:
- templates/wireless/inc/authentication_attrs.html
- templates/wireless/inc/wirelesslink_interface.html

* Consolidate wireless link interface templates into ObjectPanel subclass

Replace duplicate wirelesslink_interface_{a,b}.html templates with a
single shared template and WirelessLinkInterfacePanel(ObjectPanel)
subclass that injects the correct interface via get_context().

* Rename WirelessLANAuthenticationPanel to WirelessAuthenticationPanel

Drop the 'LAN' qualifier since the panel is shared by both WirelessLAN
and WirelessLink views.

* Fix accessor shadowing in WirelessLinkInterfacePanel

Rename __init__ parameter from 'accessor' to 'interface_attr' to avoid
shadowing ObjectPanel.accessor, which would cause super().get_context()
to resolve the wrong context key.

* Use SimpleLayout for WirelessLinkView

Replace explicit Layout with SimpleLayout, which auto-includes plugin
content panels. Remove unused Row, Column, and PluginContentPanel
imports.
This commit is contained in:
Jason Novinger
2026-03-12 07:55:50 -05:00
committed by GitHub
parent 068fce4d7c
commit 8ccb0f7b63
10 changed files with 170 additions and 280 deletions

View File

@@ -0,0 +1,3 @@
{% load i18n %}
<span id="secret" class="font-monospace" data-secret="{{ value }}">{{ value }}</span>
<button type="button" class="btn btn-primary toggle-secret float-end" data-bs-toggle="button">{% trans "Show Secret" %}</button>

View File

@@ -1,25 +0,0 @@
{% load helpers %}
{% load i18n %}
<div class="card">
<h2 class="card-header">{% trans "Authentication" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Type" %}</th>
<td>{{ object.get_auth_type_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Cipher" %}</th>
<td>{{ object.get_auth_cipher_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "PSK" %}</th>
<td>
<span id="secret" class="font-monospace" data-secret="{{ object.auth_psk }}">{{ object.auth_psk|placeholder }}</span>
{% if object.auth_psk %}
<button type="button" class="btn btn-primary toggle-secret float-end" data-bs-toggle="button">{% trans "Show Secret" %}</button>
{% endif %}
</td>
</tr>
</table>
</div>

View File

@@ -1,51 +0,0 @@
{% load helpers %}
{% load i18n %}
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Device" %}</th>
<td>{{ interface.device|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Interface" %}</th>
<td>{{ interface|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Type" %}</th>
<td>
{{ interface.get_type_display }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Role" %}</th>
<td>
{{ interface.get_rf_role_display|placeholder }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Channel" %}</th>
<td>
{{ interface.get_rf_channel_display|placeholder }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Channel Frequency" %}</th>
<td>
{% if interface.rf_channel_frequency %}
{{ interface.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" context "Abbreviation for megahertz" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Channel Width" %}</th>
<td>
{% if interface.rf_channel_width %}
{{ interface.rf_channel_width|floatformat:"-3" }} {% trans "MHz" context "Abbreviation for megahertz" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
</table>

View File

@@ -0,0 +1,48 @@
{% extends "ui/panels/_base.html" %}
{% load helpers %}
{% load i18n %}
{% block panel_content %}
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Device" %}</th>
<td>{{ interface.device|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Interface" %}</th>
<td>{{ interface|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Type" %}</th>
<td>{{ interface.get_type_display }}</td>
</tr>
<tr>
<th scope="row">{% trans "Role" %}</th>
<td>{{ interface.get_rf_role_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Channel" %}</th>
<td>{{ interface.get_rf_channel_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Channel Frequency" %}</th>
<td>
{% if interface.rf_channel_frequency %}
{{ interface.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" context "Abbreviation for megahertz" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Channel Width" %}</th>
<td>
{% if interface.rf_channel_width %}
{{ interface.rf_channel_width|floatformat:"-3" }} {% trans "MHz" context "Abbreviation for megahertz" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
</table>
{% endblock panel_content %}

View File

@@ -1,74 +1 @@
{% 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 "Wireless LAN" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "SSID" %}</th>
<td>{{ object.ssid }}</td>
</tr>
<tr>
<th scope="row">{% trans "Group" %}</th>
<td>{{ object.group|linkify|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 "Scope" %}</th>
{% if object.scope %}
<td>{{ object.scope|linkify }} ({% trans object.scope_type.name %})</td>
{% else %}
<td>{{ ''|placeholder }}</td>
{% endif %}
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "VLAN" %}</th>
<td>{{ object.vlan|linkify|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>
</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 'wireless/inc/authentication_attrs.html' %}
{% include 'inc/panels/custom_fields.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div class="card">
<h2 class="card-header">{% trans "Attached Interfaces" %}</h2>
<div class="card-body table-responsive">
{% render_table interfaces_table 'inc/table.html' %}
{% include 'inc/paginator.html' with paginator=interfaces_table.paginator page=interfaces_table.page %}
</div>
</div>
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -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 @@
</a>
{% endif %}
{% endblock extra_controls %}
{% block content %}
<div class="row mb-3">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Wireless LAN Group" %}</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 "Parent" %}</th>
<td>{{ object.parent|linkify|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' %}
{% 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 "Child Groups" %}
{% if perms.wireless.add_wirelesslangroup %}
<div class="card-actions">
<a href="{% url 'wireless:wirelesslangroup_add' %}?parent={{ object.pk }}" class="btn btn-ghost-primary btn-sm">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> {% trans "Add Wireless LAN Group" %}
</a>
</div>
{% endif %}
</h2>
{% htmx_table 'wireless:wirelesslangroup_list' parent_id=object.pk %}
</div>
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,68 +1 @@
{% extends 'generic/object.html' %}
{% load helpers %}
{% 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 "Interface" %} A</h2>
{% include 'wireless/inc/wirelesslink_interface.html' with interface=object.interface_a %}
</div>
<div class="card">
<h2 class="card-header">{% trans "Link Properties" %}</h2>
<table class="table table-hover attr-table">
<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 "SSID" %}</th>
<td>{{ object.ssid|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 "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Distance" %}</th>
<td>
{% if object.distance is not None %}
{{ object.distance|floatformat }} {{ object.get_distance_unit_display }}
{% else %}
{{ ''|placeholder }}
{% endif %}
</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">
<div class="card">
<h2 class="card-header">{% trans "Interface" %} B</h2>
{% include 'wireless/inc/wirelesslink_interface.html' with interface=object.interface_b %}
</div>
{% include 'wireless/inc/authentication_attrs.html' %}
{% include 'inc/panels/custom_fields.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}