Merge branch 'jstretch-ui-work' into feature

This commit is contained in:
jeremystretch 2021-07-12 09:56:33 -04:00
commit 5406acf329
12 changed files with 557 additions and 576 deletions

View File

@ -0,0 +1,322 @@
from dataclasses import dataclass
from typing import Sequence, Optional
@dataclass
class MenuItem:
"""A navigation menu item link. Example: Sites, Platforms, RIRs, etc."""
label: str
url: str
disabled: bool = True
add_url: Optional[str] = None
import_url: Optional[str] = None
has_add: bool = False
has_import: bool = False
@dataclass
class MenuGroup:
"""A group of menu items within a menu."""
label: str
items: Sequence[MenuItem]
@dataclass
class Menu:
"""A top level menu group. Example: Organization, Devices, IPAM."""
label: str
icon: str
groups: Sequence[MenuGroup]
ORGANIZATION_MENU = Menu(
label="Organization",
icon="domain",
groups=(
MenuGroup(
label="Sites",
items=(
MenuItem(label="Sites", url="dcim:site_list",
add_url="dcim:site_add", import_url="dcim:site_import"),
MenuItem(label="Site Groups", url="dcim:sitegroup_list",
add_url="dcim:sitegroup_add", import_url="dcim:sitegroup_import"),
MenuItem(label="Regions", url="dcim:region_list",
add_url="dcim:region_add", import_url="dcim:region_import"),
MenuItem(label="Locations", url="dcim:location_list",
add_url="dcim:location_add", import_url="dcim:location_import"),
),
),
MenuGroup(
label="Racks",
items=(
MenuItem(label="Racks", url="dcim:rack_list",
add_url="dcim:rack_add", import_url="dcim:rack_import"),
MenuItem(label="Rack Roles", url="dcim:rackrole_list",
add_url="dcim:rackrole_add", import_url="dcim:rackrole_import"),
MenuItem(label="Elevations", url="dcim:rack_elevation_list",
add_url=None, import_url=None),
),
),
MenuGroup(
label="Tenancy",
items=(
MenuItem(label="Tenants", url="tenancy:tenant_list",
add_url="tenancy:tenant_add", import_url="tenancy:tenant_import"),
MenuItem(label="Tenant Groups",
url="tenancy:tenantgroup_list", add_url="tenancy:tenantgroup_add",
import_url="tenancy:tenantgroup_import"),
),
),
MenuGroup(
label="Tags",
items=(MenuItem(label="Tags", url="extras:tag_list",
add_url="extras:tag_add", import_url="extras:tag_import"),),
),
),
)
DEVICES_MENU = Menu(
label="Devices",
icon="server",
groups=(
MenuGroup(
label="Devices",
items=(
MenuItem(label="Devices", url="dcim:device_list",
add_url="dcim:device_add", import_url="dcim:device_import"),
MenuItem(label="Device Roles", url="dcim:devicerole_list",
add_url="dcim:devicerole_add", import_url="dcim:devicerole_import"),
MenuItem(label="Platforms", url="dcim:platform_list",
add_url="dcim:platform_add", import_url="dcim:platform_import"),
MenuItem(label="Virtual Chassis",
url="dcim:virtualchassis_list", add_url="dcim:virtualchassis_add",
import_url="dcim:virtualchassis_import"),
),
),
MenuGroup(
label="Device Types",
items=(
MenuItem(label="Device Types", url="dcim:devicetype_list",
add_url="dcim:devicetype_add", import_url="dcim:devicetype_import"),
MenuItem(label="Manufacturers", url="dcim:manufacturer_list",
add_url="dcim:manufacturer_add", import_url="dcim:manufacturer_import"),
),
),
MenuGroup(
label="Connections",
items=(
MenuItem(label="Cables", url="dcim:cable_list",
add_url=None, import_url="dcim:cable_import"),
MenuItem(
label="Console Connections", url="dcim:console_connections_list", add_url=None, import_url=None,
),
MenuItem(
label="Interface Connections", url="dcim:interface_connections_list", add_url=None, import_url=None,
),
MenuItem(label="Power Connections",
url="dcim:power_connections_list", add_url=None, import_url=None,),
),
),
MenuGroup(
label="Device Components",
items=(
MenuItem(label="Interfaces", url="dcim:interface_list",
add_url=None, import_url="dcim:interface_import"),
MenuItem(label="Front Ports", url="dcim:frontport_list",
add_url=None, import_url="dcim:frontport_import"),
MenuItem(label="Rear Ports", url="dcim:rearport_list",
add_url=None, import_url="dcim:rearport_import"),
MenuItem(label="Console Ports", url="dcim:consoleport_list",
add_url=None, import_url="dcim:consoleport_import"),
MenuItem(label="Console Server Ports", url="dcim:consoleserverport_list",
add_url=None, import_url="dcim:consoleserverport_import"),
MenuItem(label="Power Ports", url="dcim:powerport_list",
add_url=None, import_url="dcim:powerport_import"),
MenuItem(label="Power Outlets", url="dcim:poweroutlet_list",
add_url=None, import_url="dcim:poweroutlet_import"),
MenuItem(label="Device Bays", url="dcim:devicebay_list",
add_url=None, import_url="dcim:devicebay_import"),
MenuItem(label="Inventory Items",
url="dcim:inventoryitem_list", add_url=None, import_url="dcim:inventoryitem_import"),
),
),
),
)
IPAM_MENU = Menu(
label="IPAM",
icon="counter",
groups=(
MenuGroup(
label="IP Addresses",
items=(
MenuItem(label="IP Addresses", url="ipam:ipaddress_list",
add_url="ipam:ipaddress_add", import_url="ipam:ipaddress_import"),
),
),
MenuGroup(
label="Prefixes",
items=(
MenuItem(label="Prefixes", url="ipam:prefix_list",
add_url="ipam:prefix_add", import_url="ipam:prefix_import"),
MenuItem(label="Prefix & VLAN Roles", url="ipam:role_list",
add_url="ipam:role_add", import_url="ipam:role_import"),
),
),
MenuGroup(
label="Aggregates",
items=(
MenuItem(label="Aggregates", url="ipam:aggregate_list",
add_url="ipam:aggregate_add", import_url="ipam:aggregate_import"),
MenuItem(label="RIRs", url="ipam:rir_list",
add_url="ipam:rir_add", import_url="ipam:rir_import"),
),
),
MenuGroup(
label="VRFs",
items=(
MenuItem(label="VRFs", url="ipam:vrf_list",
add_url="ipam:vrf_add", import_url="ipam:vrf_import"),
MenuItem(label="Route Targets", url="ipam:routetarget_list",
add_url="ipam:routetarget_add", import_url="ipam:routetarget_import"),
),
),
MenuGroup(
label="VLANs",
items=(
MenuItem(label="VLANs", url="ipam:vlan_list",
add_url="ipam:vlan_add", import_url="ipam:vlan_import"),
MenuItem(label="VLAN Groups", url="ipam:vlangroup_list",
add_url="ipam:vlangroup_add", import_url="ipam:vlangroup_import"),
),
),
MenuGroup(
label="Services",
items=(MenuItem(label="Services", url="ipam:service_list",
add_url=None, import_url="ipam:service_import"),),
),
),
)
VIRTUALIZATION_MENU = Menu(
label="Virtualization",
icon="monitor",
groups=(
MenuGroup(
label="Virtual Machines",
items=(
MenuItem(
label="Virtual Machines",
url="virtualization:virtualmachine_list", add_url="virtualization:virtualmachine_add", import_url="virtualization:virtualmachine_import"),
MenuItem(label="Interfaces",
url="virtualization:vminterface_list", add_url="virtualization:vminterface_add", import_url="virtualization:vminterface_import"),
),
),
MenuGroup(
label="Clusters",
items=(
MenuItem(label="Clusters", url="virtualization:cluster_list",
add_url="virtualization:cluster_add", import_url="virtualization:cluster_import"),
MenuItem(label="Cluster Types",
url="virtualization:clustertype_list", add_url="virtualization:clustertype_add", import_url="virtualization:clustertype_import"),
MenuItem(
label="Cluster Groups", url="virtualization:clustergroup_list", add_url="virtualization:clustergroup_add", import_url="virtualization:clustergroup_import"),
),
),
),
)
CIRCUITS_MENU = Menu(
label="Circuits",
icon="transit-connection-variant",
groups=(
MenuGroup(
label="Circuits",
items=(
MenuItem(label="Circuits", url="circuits:circuit_list",
add_url="circuits:circuit_add", import_url="circuits:circuit_import"),
MenuItem(label="Circuit Types",
url="circuits:circuittype_list", add_url="circuits:circuittype_add", import_url="circuits:circuittype_import"),
),
),
MenuGroup(
label="Providers",
items=(
MenuItem(label="Providers", url="circuits:provider_list",
add_url="circuits:provider_add", import_url="circuits:provider_import"),
MenuItem(
label="Provider Networks", url="circuits:providernetwork_list", add_url="circuits:providernetwork_add", import_url="circuits:providernetwork_import"
),
),
),
),
)
POWER_MENU = Menu(
label="Power",
icon="flash",
groups=(
MenuGroup(
label="Power",
items=(
MenuItem(label="Power Feeds", url="dcim:powerfeed_list",
add_url="dcim:powerfeed_add", import_url="dcim:powerfeed_import"),
MenuItem(label="Power Panels", url="dcim:powerpanel_list",
add_url="dcim:powerpanel_add", import_url="dcim:powerpanel_import"),
),
),
),
)
OTHER_MENU = Menu(
label="Other",
icon="notification-clear-all",
groups=(
MenuGroup(
label="Logging",
items=(
MenuItem(label="Change Log", url="extras:objectchange_list",
add_url=None, import_url=None),
MenuItem(label="Journal Entries",
url="extras:journalentry_list", add_url=None, import_url=None),
MenuItem(label="Webhooks", url="extras:webhook_list",
add_url="extras:webhook_add", import_url="extras:webhook_import"),
),
),
MenuGroup(
label="Customization",
items=(
MenuItem(label="Custom Fields", url="extras:customfield_list",
add_url="extras:customfield_add", import_url="extras:customfield_import"),
MenuItem(label="Custom Links", url="extras:customlink_list",
add_url="extras:customlink_add", import_url="extras:customlink_import"),
MenuItem(label="Export Templates", url="extras:exporttemplate_list",
add_url="extras:exporttemplate_add", import_url="extras:exporttemplate_import"),
),
),
MenuGroup(
label="Miscellaneous",
items=(
MenuItem(label="Config Contexts",
url="extras:configcontext_list", add_url=None, import_url=None),
MenuItem(label="Reports", url="extras:report_list",
add_url=None, import_url=None),
MenuItem(label="Scripts", url="extras:script_list",
add_url=None, import_url=None),
),
),
),
)
MENUS = (
ORGANIZATION_MENU,
DEVICES_MENU,
IPAM_MENU,
VIRTUALIZATION_MENU,
CIRCUITS_MENU,
POWER_MENU,
OTHER_MENU,
)

View File

@ -6,35 +6,41 @@
{% load static %} {% load static %}
{% block layout %} {% block layout %}
<div class="container-fluid"> <div class="container-fluid px-0">
<main class="ms-sm-auto px-0"> <main class="ms-sm-auto">
{# Sidebar #} {# Sidebar #}
<nav id="sidebar-menu" class="d-md-block sidebar collapse px-0" data-simplebar> <nav id="sidebar-menu" class="d-md-block sidebar collapse px-0" data-simplebar>
{# Sidebar content #} {# Sidebar content #}
<div class="position-sticky pt-3"> <div class="position-sticky">
{# Logo #} {# Logo #}
<a class="p-1 sidebar-logo d-none d-md-flex justify-content-center" href="{% url 'home' %}"> <div class="py-2">
<a class="sidebar-logo d-none d-md-flex justify-content-center" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" /> <img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" />
</a> </a>
</div>
{# Search bar #} <ul class="nav flex-column px-1">
<ul class="nav flex-column px-4">
{# Search bar for collapsed menu #}
<div class="d-block d-md-none mx-1 my-3 search-container"> <div class="d-block d-md-none mx-1 my-3 search-container">
{% search_options %} {% search_options %}
</div> </div>
<div class="d-flex d-md-none mx-1 my-3 justify-content-center justify-content-md-end order-last order-md-0"> <div class="d-flex d-md-none mx-1 my-3 justify-content-center justify-content-md-end order-last order-md-0">
{% include 'inc/profile_button.html' %} {% include 'inc/profile_button.html' %}
</div> </div>
{# Navigation menu #}
{% nav %} {% nav %}
</ul> </ul>
</div> </div>
{# Sidebar footer #} {# Sidebar footer #}
<div class="d-flex flex-column container-fluid mt-auto justify-content-end sidebar-bottom"> <div class="d-flex flex-column container-fluid mt-auto justify-content-end sidebar-bottom">
<nav class="nav justify-content-between mb-2 mt-4 px-2"> <nav class="nav justify-content-evenly my-2 px-2">
{# Documentation #} {# Documentation #}
<a type="button" class="nav-link" href="https://netbox.readthedocs.io/" target="_blank"> <a type="button" class="nav-link" href="https://netbox.readthedocs.io/" target="_blank">
@ -89,7 +95,7 @@
{# Page header #} {# Page header #}
{% block header %} {% block header %}
<div class="title-container px-3 py-3"> <div class="title-container px-3 pb-3">
{# Title #} {# Title #}
<div id="content-title"> <div id="content-title">

View File

@ -4,8 +4,7 @@
{% block title %}Delete {{ table.rows|length }} {{ obj_type_plural|bettertitle }}?{% endblock %} {% block title %}Delete {{ table.rows|length }} {{ obj_type_plural|bettertitle }}?{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="container-md px-0">
<div class="col col-md-12 col-lg-8 offset-lg-2">
<div class="alert alert-danger mb-3" role="alert"> <div class="alert alert-danger mb-3" role="alert">
<h4 class="alert-heading">Confirm Bulk Deletion</h4> <h4 class="alert-heading">Confirm Bulk Deletion</h4>
<hr /> <hr />
@ -15,18 +14,9 @@
{% block message_extra %}{% endblock %} {% block message_extra %}{% endblock %}
</div> </div>
</div> </div>
</div> <div class="container-xl px-0">
<div class="row">
<div class="col col-md-12 col-lg-8 offset-lg-2">
<div class="card">
<div class="card-body">
{% include 'inc/table.html' %} {% include 'inc/table.html' %}
</div>
</div>
</div>
</div>
<div class="row mt-3"> <div class="row mt-3">
<div class="col col-md-12 col-lg-8 offset-lg-2">
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
{% for field in form.hidden_fields %} {% for field in form.hidden_fields %}

View File

@ -15,12 +15,8 @@
{% endfor %} {% endfor %}
<div class="row mb-3"> <div class="row mb-3">
<div class="col col-md-8"> <div class="col col-md-8">
<div class="card">
<div class="card-body">
{% include 'inc/table.html' %} {% include 'inc/table.html' %}
</div> </div>
</div>
</div>
<div class="col col-md-4"> <div class="col col-md-4">
<div class="card"> <div class="card">
<h5 class="card-header">{% block form_title %}Attributes{% endblock %}</h5> <h5 class="card-header">{% block form_title %}Attributes{% endblock %}</h5>

View File

@ -4,27 +4,16 @@
{% block title %}Remove {{ table.rows|length }} {{ obj_type_plural|bettertitle }}?{% endblock %} {% block title %}Remove {{ table.rows|length }} {{ obj_type_plural|bettertitle }}?{% endblock %}
{% block content %} {% block content %}
<div class="row mb-3"> <div class="container-md px-0">
<div class="col col-md-6 offset-md-3">
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
<h4 class="alert-heading">Confirm Bulk Removal</h4> <h4 class="alert-heading">Confirm Bulk Removal</h4>
<p><strong>Warning:</strong> The following operation will remove {{ table.rows|length }} {{ obj_type_plural }} from {{ parent_obj }}.</p> <p><strong>Warning:</strong> The following operation will remove {{ table.rows|length }} {{ obj_type_plural }} from {{ parent_obj }}.</p>
<hr /> <hr />
<p class="mb-0">Please carefully review the {{ obj_type_plural }} to be removed and confirm below.</p> <p class="mb-0">Please carefully review the {{ obj_type_plural }} to be removed and confirm below.</p>
</div> </div>
</div>
</div> </div>
<div class="row mb-3"> <div class="container-xl px-0">
<div class="col col-md-12">
<div class="card">
<div class="card-body">
{% include 'inc/table.html' %} {% include 'inc/table.html' %}
</div>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col col-md-6 offset-md-3">
<form action="." method="post" class="form"> <form action="." method="post" class="form">
{% csrf_token %} {% csrf_token %}
{% for field in form.hidden_fields %} {% for field in form.hidden_fields %}
@ -35,6 +24,5 @@
<button type="submit" name="_confirm" class="btn btn-danger">Delete these {{ table.rows|length }} {{ obj_type_plural }}</button> <button type="submit" name="_confirm" class="btn btn-danger">Delete these {{ table.rows|length }} {{ obj_type_plural }}</button>
</div> </div>
</form> </form>
</div>
</div> </div>
{% endblock content %} {% endblock content %}

View File

@ -7,8 +7,6 @@
{% block content %} {% block content %}
<div class="row mb-3"> <div class="row mb-3">
<div class="col col-md-7"> <div class="col col-md-7">
<div class="card">
<div class="card-body">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
@ -26,8 +24,6 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
</div>
<div class="col col-md-5"> <div class="col col-md-5">
<form action="" method="post" class="form form-horizontal"> <form action="" method="post" class="form form-horizontal">
{% csrf_token %} {% csrf_token %}

View File

@ -25,16 +25,14 @@
{% block content %} {% block content %}
{% if filter_form %} {% if filter_form %}
<div class="col col-md-12 noprint">
{% include 'inc/advanced_search.html' %} {% include 'inc/advanced_search.html' %}
</div>
{% endif %} {% endif %}
{% if table.paginator.num_pages > 1 %} {% if table.paginator.num_pages > 1 %}
{% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %} {% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %}
<div class="row mb-3"> <div id="select-all-box" class="d-none card noprint">
<div class="row mb-3">
<form method="post" class="form col-md-12"> <form method="post" class="form col-md-12">
{% csrf_token %} {% csrf_token %}
<div id="select-all-box" class="d-none card noprint">
<div class="card-body d-inline-flex justify-content-between align-items-center"> <div class="card-body d-inline-flex justify-content-between align-items-center">
<div class="form-check"> <div class="form-check">
<input type="checkbox" id="select-all" name="_all" class="form-check-input" /> <input type="checkbox" id="select-all" name="_all" class="form-check-input" />
@ -55,16 +53,14 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div>
</form> </form>
</div> </div>
</div>
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{# Object list filter, table config #}
<div class="row mb-3"> <div class="row mb-3">
<div class="col col-md-12">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-4 offset-md-8 d-flex noprint table-controls"> <div class="col col-md-4 offset-md-8 d-flex noprint table-controls">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="text" class="form-control object-filter" placeholder="Filter" title="Filter text (regular expressions supported)" /> <input type="text" class="form-control object-filter" placeholder="Filter" title="Filter text (regular expressions supported)" />
@ -84,15 +80,19 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="card-body"> {# Object table #}
<div class="row">
<div class="col col-md-12">
{% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %} {% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %}
{% if permissions.change or permissions.delete %} {% if permissions.change or permissions.delete %}
<form method="post" class="form form-horizontal"> <form method="post" class="form form-horizontal">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" /> <input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" />
{% include table_template|default:'inc/responsive_table.html' %} <div class="table-responsive">
{% render_table table 'inc/table.html' %}
</div>
<div class="float-start noprint bulk-buttons"> <div class="float-start noprint bulk-buttons">
{% block bulk_buttons %}{% endblock %} {% block bulk_buttons %}{% endblock %}
{% if bulk_edit_url and permissions.change %} {% if bulk_edit_url and permissions.change %}
@ -115,8 +115,6 @@
{% endwith %} {% endwith %}
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
</div> </div>
</div>
</div>
</div> </div>
{% table_config_form table table_name="ObjectTable" %} {% table_config_form table table_name="ObjectTable" %}
{% endblock content %} {% endblock content %}

View File

@ -1,7 +1,8 @@
{% load form_helpers %} {% load form_helpers %}
{% load helpers %} {% load helpers %}
<div class="collapse" id="advanced-search-content"> <div id="advanced-search-content" class="collapse mb-3">
<div class="col col-md-12 noprint">
<form action="." method="get"> <form action="." method="get">
<div class="card"> <div class="card">
<h5 class="card-header"> <h5 class="card-header">
@ -62,4 +63,5 @@
</div> </div>
</div> </div>
</form> </form>
</div>
</div> </div>

View File

@ -1,6 +1,6 @@
{% load helpers %} {% load helpers %}
<div class="paginator float-end text-end my-3"> <div class="paginator float-end text-end">
{% if paginator.num_pages > 1 %} {% if paginator.num_pages > 1 %}
<div class="btn-group btn-group-sm mb-3" role="group" aria-label="Pages"> <div class="btn-group btn-group-sm mb-3" role="group" aria-label="Pages">
{% if page.has_previous %} {% if page.has_previous %}

View File

@ -1,7 +1,6 @@
{% load django_tables2 %} {% load django_tables2 %}
<table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}> <table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}>
<caption class="text-center small mt-3"></caption>
{% if table.show_header %} {% if table.show_header %}
<thead> <thead>
<tr> <tr>

View File

@ -21,7 +21,7 @@
{% for group in menu.groups %} {% for group in menu.groups %}
{# Within each main menu, there are groups of menu items #} {# Within each main menu, there are groups of menu items #}
<div class="flex-column nav px-2"> <div class="flex-column nav">
{% if menu.groups|length > 1 %} {% if menu.groups|length > 1 %}
<h6 class="accordion-item-title">{{ group.label }}</h6> <h6 class="accordion-item-title">{{ group.label }}</h6>
@ -32,12 +32,13 @@
<div class="nav-item d-flex justify-content-between align-items-center"> <div class="nav-item d-flex justify-content-between align-items-center">
{# Menu Link with Text #} {# Menu Link with Text #}
<a class="nav-link flex-grow-1 me-1" href="{% url item.url %}"> <a class="nav-link flex-grow-1" href="{% url item.url %}">
{{ item.label }} {{ item.label }}
</a> </a>
{# Add & Import Buttons #} {# Add & Import Buttons #}
<div class="btn-group"> {% if item.has_add or item.has_import %}
<div class="btn-group ps-1">
{% if item.has_add %} {% if item.has_add %}
<a class="btn btn-sm btn-success lh-1" href="{% url item.add_url %}" title="Add {{ item.label }}"> <a class="btn btn-sm btn-success lh-1" href="{% url item.add_url %}" title="Add {{ item.label }}">
<i class="mdi mdi-plus-thick"></i> <i class="mdi mdi-plus-thick"></i>
@ -49,6 +50,7 @@
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}

View File

@ -1,332 +1,14 @@
from dataclasses import dataclass from typing import Dict
from typing import Dict, Sequence, Optional
from django import template from django import template
from django.template import Context from django.template import Context
from django.contrib.auth.context_processors import PermWrapper from django.contrib.auth.context_processors import PermWrapper
from netbox.navigation_menu import Menu, MenuGroup, MENUS
register = template.Library() register = template.Library()
@dataclass
class MenuItem:
"""A navigation menu item link. Example: Sites, Platforms, RIRs, etc."""
label: str
url: str
disabled: bool = True
add_url: Optional[str] = None
import_url: Optional[str] = None
has_add: bool = False
has_import: bool = False
@dataclass
class MenuGroup:
"""A group of menu items within a menu."""
label: str
items: Sequence[MenuItem]
@dataclass
class Menu:
"""A top level menu group. Example: Organization, Devices, IPAM."""
label: str
icon: str
groups: Sequence[MenuGroup]
ORGANIZATION_MENU = Menu(
label="Organization",
icon="domain",
groups=(
MenuGroup(
label="Sites",
items=(
MenuItem(label="Sites", url="dcim:site_list",
add_url="dcim:site_add", import_url="dcim:site_import"),
MenuItem(label="Site Groups", url="dcim:sitegroup_list",
add_url="dcim:sitegroup_add", import_url="dcim:sitegroup_import"),
MenuItem(label="Regions", url="dcim:region_list",
add_url="dcim:region_add", import_url="dcim:region_import"),
MenuItem(label="Locations", url="dcim:location_list",
add_url="dcim:location_add", import_url="dcim:location_import"),
),
),
MenuGroup(
label="Racks",
items=(
MenuItem(label="Racks", url="dcim:rack_list",
add_url="dcim:rack_add", import_url="dcim:rack_import"),
MenuItem(label="Rack Roles", url="dcim:rackrole_list",
add_url="dcim:rackrole_add", import_url="dcim:rackrole_import"),
MenuItem(label="Elevations", url="dcim:rack_elevation_list",
add_url=None, import_url=None),
),
),
MenuGroup(
label="Tenancy",
items=(
MenuItem(label="Tenants", url="tenancy:tenant_list",
add_url="tenancy:tenant_add", import_url="tenancy:tenant_import"),
MenuItem(label="Tenant Groups",
url="tenancy:tenantgroup_list", add_url="tenancy:tenantgroup_add",
import_url="tenancy:tenantgroup_import"),
),
),
MenuGroup(
label="Tags",
items=(MenuItem(label="Tags", url="extras:tag_list",
add_url="extras:tag_add", import_url="extras:tag_import"),),
),
),
)
DEVICES_MENU = Menu(
label="Devices",
icon="server",
groups=(
MenuGroup(
label="Devices",
items=(
MenuItem(label="Devices", url="dcim:device_list",
add_url="dcim:device_add", import_url="dcim:device_import"),
MenuItem(label="Device Roles", url="dcim:devicerole_list",
add_url="dcim:devicerole_add", import_url="dcim:devicerole_import"),
MenuItem(label="Platforms", url="dcim:platform_list",
add_url="dcim:platform_add", import_url="dcim:platform_import"),
MenuItem(label="Virtual Chassis",
url="dcim:virtualchassis_list", add_url="dcim:virtualchassis_add",
import_url="dcim:virtualchassis_import"),
),
),
MenuGroup(
label="Device Types",
items=(
MenuItem(label="Device Types", url="dcim:devicetype_list",
add_url="dcim:devicetype_add", import_url="dcim:devicetype_import"),
MenuItem(label="Manufacturers", url="dcim:manufacturer_list",
add_url="dcim:manufacturer_add", import_url="dcim:manufacturer_import"),
),
),
MenuGroup(
label="Connections",
items=(
MenuItem(label="Cables", url="dcim:cable_list",
add_url=None, import_url="dcim:cable_import"),
MenuItem(
label="Console Connections", url="dcim:console_connections_list", add_url=None, import_url=None,
),
MenuItem(
label="Interface Connections", url="dcim:interface_connections_list", add_url=None, import_url=None,
),
MenuItem(label="Power Connections",
url="dcim:power_connections_list", add_url=None, import_url=None,),
),
),
MenuGroup(
label="Device Components",
items=(
MenuItem(label="Interfaces", url="dcim:interface_list",
add_url=None, import_url="dcim:interface_import"),
MenuItem(label="Front Ports", url="dcim:frontport_list",
add_url=None, import_url="dcim:frontport_import"),
MenuItem(label="Rear Ports", url="dcim:rearport_list",
add_url=None, import_url="dcim:rearport_import"),
MenuItem(label="Console Ports", url="dcim:consoleport_list",
add_url=None, import_url="dcim:consoleport_import"),
MenuItem(label="Console Server Ports", url="dcim:consoleserverport_list",
add_url=None, import_url="dcim:consoleserverport_import"),
MenuItem(label="Power Ports", url="dcim:powerport_list",
add_url=None, import_url="dcim:powerport_import"),
MenuItem(label="Power Outlets", url="dcim:poweroutlet_list",
add_url=None, import_url="dcim:poweroutlet_import"),
MenuItem(label="Device Bays", url="dcim:devicebay_list",
add_url=None, import_url="dcim:devicebay_import"),
MenuItem(label="Inventory Items",
url="dcim:inventoryitem_list", add_url=None, import_url="dcim:inventoryitem_import"),
),
),
),
)
IPAM_MENU = Menu(
label="IPAM",
icon="counter",
groups=(
MenuGroup(
label="IP Addresses",
items=(
MenuItem(label="IP Addresses", url="ipam:ipaddress_list",
add_url="ipam:ipaddress_add", import_url="ipam:ipaddress_import"),
),
),
MenuGroup(
label="Prefixes",
items=(
MenuItem(label="Prefixes", url="ipam:prefix_list",
add_url="ipam:prefix_add", import_url="ipam:prefix_import"),
MenuItem(label="Prefix & VLAN Roles", url="ipam:role_list",
add_url="ipam:role_add", import_url="ipam:role_import"),
),
),
MenuGroup(
label="Aggregates",
items=(
MenuItem(label="Aggregates", url="ipam:aggregate_list",
add_url="ipam:aggregate_add", import_url="ipam:aggregate_import"),
MenuItem(label="RIRs", url="ipam:rir_list",
add_url="ipam:rir_add", import_url="ipam:rir_import"),
),
),
MenuGroup(
label="VRFs",
items=(
MenuItem(label="VRFs", url="ipam:vrf_list",
add_url="ipam:vrf_add", import_url="ipam:vrf_import"),
MenuItem(label="Route Targets", url="ipam:routetarget_list",
add_url="ipam:routetarget_add", import_url="ipam:routetarget_import"),
),
),
MenuGroup(
label="VLANs",
items=(
MenuItem(label="VLANs", url="ipam:vlan_list",
add_url="ipam:vlan_add", import_url="ipam:vlan_import"),
MenuItem(label="VLAN Groups", url="ipam:vlangroup_list",
add_url="ipam:vlangroup_add", import_url="ipam:vlangroup_import"),
),
),
MenuGroup(
label="Services",
items=(MenuItem(label="Services", url="ipam:service_list",
add_url=None, import_url="ipam:service_import"),),
),
),
)
VIRTUALIZATION_MENU = Menu(
label="Virtualization",
icon="monitor",
groups=(
MenuGroup(
label="Virtual Machines",
items=(
MenuItem(
label="Virtual Machines",
url="virtualization:virtualmachine_list", add_url="virtualization:virtualmachine_add", import_url="virtualization:virtualmachine_import"),
MenuItem(label="Interfaces",
url="virtualization:vminterface_list", add_url="virtualization:vminterface_add", import_url="virtualization:vminterface_import"),
),
),
MenuGroup(
label="Clusters",
items=(
MenuItem(label="Clusters", url="virtualization:cluster_list",
add_url="virtualization:cluster_add", import_url="virtualization:cluster_import"),
MenuItem(label="Cluster Types",
url="virtualization:clustertype_list", add_url="virtualization:clustertype_add", import_url="virtualization:clustertype_import"),
MenuItem(
label="Cluster Groups", url="virtualization:clustergroup_list", add_url="virtualization:clustergroup_add", import_url="virtualization:clustergroup_import"),
),
),
),
)
CIRCUITS_MENU = Menu(
label="Circuits",
icon="transit-connection-variant",
groups=(
MenuGroup(
label="Circuits",
items=(
MenuItem(label="Circuits", url="circuits:circuit_list",
add_url="circuits:circuit_add", import_url="circuits:circuit_import"),
MenuItem(label="Circuit Types",
url="circuits:circuittype_list", add_url="circuits:circuittype_add", import_url="circuits:circuittype_import"),
),
),
MenuGroup(
label="Providers",
items=(
MenuItem(label="Providers", url="circuits:provider_list",
add_url="circuits:provider_add", import_url="circuits:provider_import"),
MenuItem(
label="Provider Networks", url="circuits:providernetwork_list", add_url="circuits:providernetwork_add", import_url="circuits:providernetwork_import"
),
),
),
),
)
POWER_MENU = Menu(
label="Power",
icon="flash",
groups=(
MenuGroup(
label="Power",
items=(
MenuItem(label="Power Feeds", url="dcim:powerfeed_list",
add_url="dcim:powerfeed_add", import_url="dcim:powerfeed_import"),
MenuItem(label="Power Panels", url="dcim:powerpanel_list",
add_url="dcim:powerpanel_add", import_url="dcim:powerpanel_import"),
),
),
),
)
OTHER_MENU = Menu(
label="Other",
icon="notification-clear-all",
groups=(
MenuGroup(
label="Logging",
items=(
MenuItem(label="Change Log", url="extras:objectchange_list",
add_url=None, import_url=None),
MenuItem(label="Journal Entries",
url="extras:journalentry_list", add_url=None, import_url=None),
MenuItem(label="Webhooks", url="extras:webhook_list",
add_url="extras:webhook_add", import_url="extras:webhook_import"),
),
),
MenuGroup(
label="Customization",
items=(
MenuItem(label="Custom Fields", url="extras:customfield_list",
add_url="extras:customfield_add", import_url="extras:customfield_import"),
MenuItem(label="Custom Links", url="extras:customlink_list",
add_url="extras:customlink_add", import_url="extras:customlink_import"),
MenuItem(label="Export Templates", url="extras:exporttemplate_list",
add_url="extras:exporttemplate_add", import_url="extras:exporttemplate_import"),
),
),
MenuGroup(
label="Miscellaneous",
items=(
MenuItem(label="Config Contexts",
url="extras:configcontext_list", add_url=None, import_url=None),
MenuItem(label="Reports", url="extras:report_list",
add_url=None, import_url=None),
MenuItem(label="Scripts", url="extras:script_list",
add_url=None, import_url=None),
),
),
),
)
MENUS = (
ORGANIZATION_MENU,
DEVICES_MENU,
IPAM_MENU,
VIRTUALIZATION_MENU,
CIRCUITS_MENU,
POWER_MENU,
OTHER_MENU,
)
def process_menu(menu: Menu, perms: PermWrapper) -> MenuGroup: def process_menu(menu: Menu, perms: PermWrapper) -> MenuGroup:
"""Enable a menu item if view permissions exist for the user.""" """Enable a menu item if view permissions exist for the user."""
for group in menu.groups: for group in menu.groups: