mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Fixes #10682: Correct home view links to connection lists
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
* [#10643](https://github.com/netbox-community/netbox/issues/10643) - Ensure consistent display of custom fields for all model forms
|
* [#10643](https://github.com/netbox-community/netbox/issues/10643) - Ensure consistent display of custom fields for all model forms
|
||||||
* [#10646](https://github.com/netbox-community/netbox/issues/10646) - Fix filtering of power feed by power panel when connecting a cable
|
* [#10646](https://github.com/netbox-community/netbox/issues/10646) - Fix filtering of power feed by power panel when connecting a cable
|
||||||
* [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables
|
* [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables
|
||||||
|
* [#10682](https://github.com/netbox-community/netbox/issues/10682) - Correct home view links to connection lists
|
||||||
* [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+
|
* [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+
|
||||||
* [#10716](https://github.com/netbox-community/netbox/issues/10716) - Add left/right page plugin content embeds for tag view
|
* [#10716](https://github.com/netbox-community/netbox/issues/10716) - Add left/right page plugin content embeds for tag view
|
||||||
* [#10719](https://github.com/netbox-community/netbox/issues/10719) - Prevent user without sufficient permission from creating an IP address via FHRP group creation
|
* [#10719](https://github.com/netbox-community/netbox/issues/10719) - Prevent user without sufficient permission from creating an IP address via FHRP group creation
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
@@ -8,6 +9,7 @@ from django.shortcuts import redirect, render
|
|||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.template.exceptions import TemplateDoesNotExist
|
from django.template.exceptions import TemplateDoesNotExist
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django.views.decorators.csrf import requires_csrf_token
|
from django.views.decorators.csrf import requires_csrf_token
|
||||||
from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found
|
from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
@@ -24,100 +26,90 @@ from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF
|
|||||||
from netbox.constants import SEARCH_MAX_RESULTS
|
from netbox.constants import SEARCH_MAX_RESULTS
|
||||||
from netbox.forms import SearchForm
|
from netbox.forms import SearchForm
|
||||||
from netbox.search import SEARCH_TYPES
|
from netbox.search import SEARCH_TYPES
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Contact, Tenant
|
||||||
from virtualization.models import Cluster, VirtualMachine
|
from virtualization.models import Cluster, VirtualMachine
|
||||||
from wireless.models import WirelessLAN, WirelessLink
|
from wireless.models import WirelessLAN, WirelessLink
|
||||||
|
|
||||||
|
|
||||||
|
Link = namedtuple('Link', ('label', 'viewname', 'permission', 'count'))
|
||||||
|
|
||||||
|
|
||||||
class HomeView(View):
|
class HomeView(View):
|
||||||
template_name = 'home.html'
|
template_name = 'home.html'
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
|
if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
|
||||||
return redirect("login")
|
return redirect('login')
|
||||||
|
|
||||||
connected_consoleports = ConsolePort.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
|
console_connections = ConsolePort.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
|
||||||
_path__is_complete=True
|
_path__is_complete=True
|
||||||
)
|
).count
|
||||||
connected_powerports = PowerPort.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
|
power_connections = PowerPort.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
|
||||||
_path__is_complete=True
|
_path__is_complete=True
|
||||||
)
|
).count
|
||||||
connected_interfaces = Interface.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
|
interface_connections = Interface.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
|
||||||
_path__is_complete=True
|
_path__is_complete=True
|
||||||
)
|
).count
|
||||||
|
|
||||||
|
def get_count_queryset(model):
|
||||||
|
return model.objects.restrict(request.user, 'view').count
|
||||||
|
|
||||||
def build_stats():
|
def build_stats():
|
||||||
org = (
|
org = (
|
||||||
("dcim.view_site", "Sites", Site.objects.restrict(request.user, 'view').count),
|
Link(_('Sites'), 'dcim:site_list', 'dcim.view_site', get_count_queryset(Site)),
|
||||||
("tenancy.view_tenant", "Tenants", Tenant.objects.restrict(request.user, 'view').count),
|
Link(_('Tenants'), 'tenancy:tenant_list', 'tenancy.view_tenant', get_count_queryset(Tenant)),
|
||||||
|
Link(_('Contacts'), 'tenancy:contact_list', 'tenancy.view_contact', get_count_queryset(Contact)),
|
||||||
)
|
)
|
||||||
dcim = (
|
dcim = (
|
||||||
("dcim.view_rack", "Racks", Rack.objects.restrict(request.user, 'view').count),
|
Link(_('Racks'), 'dcim:rack_list', 'dcim.view_rack', get_count_queryset(Rack)),
|
||||||
("dcim.view_devicetype", "Device Types", DeviceType.objects.restrict(request.user, 'view').count),
|
Link(_('Device Types'), 'dcim:devicetype_list', 'dcim.view_devicetype', get_count_queryset(DeviceType)),
|
||||||
("dcim.view_device", "Devices", Device.objects.restrict(request.user, 'view').count),
|
Link(_('Devices'), 'dcim:device_list', 'dcim.view_device', get_count_queryset(Device)),
|
||||||
)
|
)
|
||||||
ipam = (
|
ipam = (
|
||||||
("ipam.view_vrf", "VRFs", VRF.objects.restrict(request.user, 'view').count),
|
Link(_('VRFs'), 'ipam:vrf_list', 'ipam.view_vrf', get_count_queryset(VRF)),
|
||||||
("ipam.view_aggregate", "Aggregates", Aggregate.objects.restrict(request.user, 'view').count),
|
Link(_('Aggregates'), 'ipam:aggregate_list', 'ipam.view_aggregate', get_count_queryset(Aggregate)),
|
||||||
("ipam.view_prefix", "Prefixes", Prefix.objects.restrict(request.user, 'view').count),
|
Link(_('Prefixes'), 'ipam:prefix_list', 'ipam.view_prefix', get_count_queryset(Prefix)),
|
||||||
("ipam.view_iprange", "IP Ranges", IPRange.objects.restrict(request.user, 'view').count),
|
Link(_('IP Ranges'), 'ipam:iprange_list', 'ipam.view_iprange', get_count_queryset(IPRange)),
|
||||||
("ipam.view_ipaddress", "IP Addresses", IPAddress.objects.restrict(request.user, 'view').count),
|
Link(_('IP Addresses'), 'ipam:ipaddress_list', 'ipam.view_ipaddress', get_count_queryset(IPAddress)),
|
||||||
("ipam.view_vlan", "VLANs", VLAN.objects.restrict(request.user, 'view').count)
|
Link(_('VLANs'), 'ipam:vlan_list', 'ipam.view_vlan', get_count_queryset(VLAN)),
|
||||||
|
|
||||||
)
|
)
|
||||||
circuits = (
|
circuits = (
|
||||||
("circuits.view_provider", "Providers", Provider.objects.restrict(request.user, 'view').count),
|
Link(_('Providers'), 'circuits:provider_list', 'circuits.view_provider', get_count_queryset(Provider)),
|
||||||
("circuits.view_circuit", "Circuits", Circuit.objects.restrict(request.user, 'view').count),
|
Link(_('Circuits'), 'circuits:circuit_list', 'circuits.view_circuit', get_count_queryset(Circuit))
|
||||||
)
|
)
|
||||||
virtualization = (
|
virtualization = (
|
||||||
("virtualization.view_cluster", "Clusters", Cluster.objects.restrict(request.user, 'view').count),
|
Link(_('Clusters'), 'virtualization:cluster_list', 'virtualization.view_cluster',
|
||||||
("virtualization.view_virtualmachine", "Virtual Machines", VirtualMachine.objects.restrict(request.user, 'view').count),
|
get_count_queryset(Cluster)),
|
||||||
|
Link(_('Virtual Machines'), 'virtualization:virtualmachine_list', 'virtualization.view_virtualmachine',
|
||||||
|
get_count_queryset(VirtualMachine)),
|
||||||
)
|
)
|
||||||
connections = (
|
connections = (
|
||||||
("dcim.view_cable", "Cables", Cable.objects.restrict(request.user, 'view').count),
|
Link(_('Cables'), 'dcim:cable_list', 'dcim.view_cable', get_count_queryset(Cable)),
|
||||||
("dcim.view_consoleport", "Console", connected_consoleports.count),
|
Link(_('Interfaces'), 'dcim:interface_connections_list', 'dcim.view_interface', interface_connections),
|
||||||
("dcim.view_interface", "Interfaces", connected_interfaces.count),
|
Link(_('Console'), 'dcim:console_connections_list', 'dcim.view_consoleport', console_connections),
|
||||||
("dcim.view_powerport", "Power Connections", connected_powerports.count),
|
Link(_('Power'), 'dcim:power_connections_list', 'dcim.view_powerport', power_connections),
|
||||||
)
|
)
|
||||||
power = (
|
power = (
|
||||||
("dcim.view_powerpanel", "Power Panels", PowerPanel.objects.restrict(request.user, 'view').count),
|
Link(_('Power Panels'), 'dcim:powerpanel_list', 'dcim.view_powerpanel', get_count_queryset(PowerPanel)),
|
||||||
("dcim.view_powerfeed", "Power Feeds", PowerFeed.objects.restrict(request.user, 'view').count),
|
Link(_('Power Feeds'), 'dcim:powerfeed_list', 'dcim.view_powerfeed', get_count_queryset(PowerFeed)),
|
||||||
)
|
)
|
||||||
wireless = (
|
wireless = (
|
||||||
("wireless.view_wirelesslan", "Wireless LANs", WirelessLAN.objects.restrict(request.user, 'view').count),
|
Link(_('Wireless LANs'), 'wireless:wirelesslan_list', 'wireless.view_wirelesslan',
|
||||||
("wireless.view_wirelesslink", "Wireless Links", WirelessLink.objects.restrict(request.user, 'view').count),
|
get_count_queryset(WirelessLAN)),
|
||||||
|
Link(_('Wireless Links'), 'wireless:wirelesslink_list', 'wireless.view_wirelesslink',
|
||||||
|
get_count_queryset(WirelessLink)),
|
||||||
)
|
)
|
||||||
sections = (
|
stats = (
|
||||||
("Organization", org, "domain"),
|
(_('Organization'), org, 'domain'),
|
||||||
("IPAM", ipam, "counter"),
|
(_('IPAM'), ipam, 'counter'),
|
||||||
("Virtualization", virtualization, "monitor"),
|
(_('Virtualization'), virtualization, 'monitor'),
|
||||||
("Inventory", dcim, "server"),
|
(_('Inventory'), dcim, 'server'),
|
||||||
("Circuits", circuits, "transit-connection-variant"),
|
(_('Circuits'), circuits, 'transit-connection-variant'),
|
||||||
("Connections", connections, "cable-data"),
|
(_('Connections'), connections, 'cable-data'),
|
||||||
("Power", power, "flash"),
|
(_('Power'), power, 'flash'),
|
||||||
("Wireless", wireless, "wifi"),
|
(_('Wireless'), wireless, 'wifi'),
|
||||||
)
|
)
|
||||||
|
|
||||||
stats = []
|
|
||||||
for section_label, section_items, icon_class in sections:
|
|
||||||
items = []
|
|
||||||
for perm, item_label, get_count in section_items:
|
|
||||||
app, scope = perm.split(".")
|
|
||||||
url = ":".join((app, scope.replace("view_", "") + "_list"))
|
|
||||||
item = {
|
|
||||||
"label": item_label,
|
|
||||||
"count": None,
|
|
||||||
"url": url,
|
|
||||||
"disabled": True,
|
|
||||||
"icon": icon_class,
|
|
||||||
}
|
|
||||||
if request.user.has_perm(perm):
|
|
||||||
item["count"] = get_count()
|
|
||||||
item["disabled"] = False
|
|
||||||
items.append(item)
|
|
||||||
stats.append((section_label, items, icon_class))
|
|
||||||
|
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
# Compile changelog table
|
# Compile changelog table
|
||||||
|
@@ -36,8 +36,8 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="list-group list-group-flush">
|
<div class="list-group list-group-flush">
|
||||||
{% for item in items %}
|
{% for item in items %}
|
||||||
{% if not item.disabled %}
|
{% if item.permission in perms %}
|
||||||
<a href="{% url item.url %}" class="list-group-item list-group-item-action">
|
<a href="{% url item.viewname %}" class="list-group-item list-group-item-action">
|
||||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
<h4 class="mb-1">{{ item.count }}</h4>
|
<h4 class="mb-1">{{ item.count }}</h4>
|
||||||
|
Reference in New Issue
Block a user