diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 3d0672019..09a085ff7 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.core.paginator import EmptyPage, PageNotAnInteger @@ -19,7 +20,8 @@ from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from netbox.object_actions import * from netbox.ui import layout from netbox.ui.panels import ( - CommentsPanel, CustomFieldsPanel, ImageAttachmentsPanel, PluginContentPanel, RelatedObjectsPanel, TagsPanel, + CommentsPanel, CustomFieldsPanel, EmbeddedTablePanel, ImageAttachmentsPanel, PluginContentPanel, + RelatedObjectsPanel, TagsPanel, ) from netbox.views import generic from utilities.forms import ConfirmationForm @@ -485,6 +487,20 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView): ), layout.Row( layout.Column( + EmbeddedTablePanel( + 'dcim:location_list', + url_params={'site_id': lambda x: x.pk}, + title=_('Locations') + ), + EmbeddedTablePanel( + 'dcim:device_list', + url_params={ + 'site_id': lambda x: x.pk, + 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE, + 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE, + }, + title=_('Non-Racked Devices') + ), PluginContentPanel('full_width_page'), ), ), diff --git a/netbox/netbox/ui/panels.py b/netbox/netbox/ui/panels.py index 3948f0de4..a602eaa33 100644 --- a/netbox/netbox/ui/panels.py +++ b/netbox/netbox/ui/panels.py @@ -5,12 +5,14 @@ from django.utils.translation import gettext_lazy as _ from netbox.ui import attrs from netbox.ui.attrs import Attr +from utilities.querydict import dict_to_querydict from utilities.string import title from utilities.templatetags.plugins import _get_registered_content __all__ = ( 'CommentsPanel', 'CustomFieldsPanel', + 'EmbeddedTablePanel', 'ImageAttachmentsPanel', 'NestedGroupObjectPanel', 'ObjectPanel', @@ -146,6 +148,28 @@ class ImageAttachmentsPanel(Panel): }) +class EmbeddedTablePanel(Panel): + template_name = 'ui/panels/embedded_table.html' + title = None + + def __init__(self, viewname, url_params=None, **kwargs): + super().__init__(**kwargs) + self.viewname = viewname + self.url_params = url_params or {} + + def render(self, context): + obj = context.get('object') + url_params = { + k: v(obj) if callable(v) else v for k, v in self.url_params.items() + } + # url_params['return_url'] = return_url or context['request'].path + return render_to_string(self.template_name, { + 'title': self.title, + 'viewname': self.viewname, + 'url_params': dict_to_querydict(url_params), + }) + + class PluginContentPanel(Panel): def __init__(self, method, **kwargs): diff --git a/netbox/templates/ui/panels/embedded_table.html b/netbox/templates/ui/panels/embedded_table.html new file mode 100644 index 000000000..64579705f --- /dev/null +++ b/netbox/templates/ui/panels/embedded_table.html @@ -0,0 +1,5 @@ +{% extends "ui/panels/_base.html" %} + +{% block panel_content %} + {% include 'builtins/htmx_table.html' %} +{% endblock panel_content %}