mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-19 10:08:44 -06:00
Add panels for common inclusion templates
This commit is contained in:
@@ -18,6 +18,7 @@ from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN
|
|||||||
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
||||||
from netbox.object_actions import *
|
from netbox.object_actions import *
|
||||||
from netbox.ui import layout
|
from netbox.ui import layout
|
||||||
|
from netbox.ui.panels import CommentsPanel, CustomFieldsPanel, ImageAttachmentsPanel, RelatedObjectsPanel, TagsPanel
|
||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
from utilities.forms import ConfirmationForm
|
from utilities.forms import ConfirmationForm
|
||||||
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||||
@@ -468,14 +469,20 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView):
|
|||||||
layout = layout.Layout(
|
layout = layout.Layout(
|
||||||
layout.Row(
|
layout.Row(
|
||||||
layout.Column(
|
layout.Column(
|
||||||
panels.SitePanel(_('Site'))
|
panels.SitePanel(_('Site')),
|
||||||
|
CustomFieldsPanel(),
|
||||||
|
TagsPanel(),
|
||||||
|
CommentsPanel(),
|
||||||
|
),
|
||||||
|
layout.Column(
|
||||||
|
RelatedObjectsPanel(),
|
||||||
|
ImageAttachmentsPanel(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
return {
|
return {
|
||||||
# 'site_panel': panels.SitePanel(instance, _('Site')),
|
|
||||||
'related_models': self.get_related_models(
|
'related_models': self.get_related_models(
|
||||||
request,
|
request,
|
||||||
instance,
|
instance,
|
||||||
|
|||||||
@@ -8,14 +8,23 @@ from netbox.ui.attrs import Attr
|
|||||||
from utilities.string import title
|
from utilities.string import title
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'CommentsPanel',
|
||||||
|
'CustomFieldsPanel',
|
||||||
|
'ImageAttachmentsPanel',
|
||||||
'NestedGroupObjectPanel',
|
'NestedGroupObjectPanel',
|
||||||
'ObjectPanel',
|
'ObjectPanel',
|
||||||
|
'RelatedObjectsPanel',
|
||||||
'Panel',
|
'Panel',
|
||||||
|
'TagsPanel',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Panel(ABC):
|
class Panel(ABC):
|
||||||
|
|
||||||
|
def __init__(self, title=None):
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def render(self, obj):
|
def render(self, obj):
|
||||||
pass
|
pass
|
||||||
@@ -51,9 +60,6 @@ class ObjectPanelMeta(ABCMeta):
|
|||||||
class ObjectPanel(Panel, metaclass=ObjectPanelMeta):
|
class ObjectPanel(Panel, metaclass=ObjectPanelMeta):
|
||||||
template_name = 'ui/panels/object.html'
|
template_name = 'ui/panels/object.html'
|
||||||
|
|
||||||
def __init__(self, title=None):
|
|
||||||
self.title = title
|
|
||||||
|
|
||||||
def get_attributes(self, obj):
|
def get_attributes(self, obj):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -74,3 +80,65 @@ class NestedGroupObjectPanel(ObjectPanel, metaclass=ObjectPanelMeta):
|
|||||||
name = attrs.TextAttr('name', label=_('Name'))
|
name = attrs.TextAttr('name', label=_('Name'))
|
||||||
description = attrs.TextAttr('description', label=_('Description'))
|
description = attrs.TextAttr('description', label=_('Description'))
|
||||||
parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)
|
parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomFieldsPanel(Panel):
|
||||||
|
template_name = 'ui/panels/custom_fields.html'
|
||||||
|
title = _('Custom Fields')
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
obj = context.get('object')
|
||||||
|
custom_fields = obj.get_custom_fields_by_group()
|
||||||
|
if not custom_fields:
|
||||||
|
return ''
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
'title': self.title,
|
||||||
|
'custom_fields': custom_fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class TagsPanel(Panel):
|
||||||
|
template_name = 'ui/panels/tags.html'
|
||||||
|
title = _('Tags')
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
'title': self.title,
|
||||||
|
'object': context.get('object'),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class CommentsPanel(Panel):
|
||||||
|
template_name = 'ui/panels/comments.html'
|
||||||
|
title = _('Comments')
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
obj = context.get('object')
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
'title': self.title,
|
||||||
|
'comments': obj.comments,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedObjectsPanel(Panel):
|
||||||
|
template_name = 'ui/panels/related_objects.html'
|
||||||
|
title = _('Related Objects')
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
'title': self.title,
|
||||||
|
'object': context.get('object'),
|
||||||
|
'related_models': context.get('related_models'),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class ImageAttachmentsPanel(Panel):
|
||||||
|
template_name = 'ui/panels/image_attachments.html'
|
||||||
|
title = _('Image Attachments')
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
'title': self.title,
|
||||||
|
'request': context.get('request'),
|
||||||
|
'object': context.get('object'),
|
||||||
|
})
|
||||||
|
|||||||
4
netbox/templates/ui/panels/_base.html
Normal file
4
netbox/templates/ui/panels/_base.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<div class="card">
|
||||||
|
<h2 class="card-header">{{ title }}</h2>
|
||||||
|
{% block panel_content %}{% endblock %}
|
||||||
|
</div>
|
||||||
12
netbox/templates/ui/panels/comments.html
Normal file
12
netbox/templates/ui/panels/comments.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{% extends "ui/panels/_base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block panel_content %}
|
||||||
|
<div class="card-body">
|
||||||
|
{% if comments %}
|
||||||
|
{{ comments|markdown }}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">{% trans "None" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock panel_content %}
|
||||||
31
netbox/templates/ui/panels/custom_fields.html
Normal file
31
netbox/templates/ui/panels/custom_fields.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{% extends "ui/panels/_base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block panel_content %}
|
||||||
|
<table class="table table-hover attr-table">
|
||||||
|
{% for group_name, fields in custom_fields.items %}
|
||||||
|
{% if group_name %}
|
||||||
|
<tr>
|
||||||
|
<th scope="row" colspan="2" class="fw-bold">{{ group_name }}</th>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% for field, value in fields.items %}
|
||||||
|
<tr>
|
||||||
|
<th scope="row"{% if group_name %} class="ps-3"{% endif %}>{{ field }}
|
||||||
|
{% if field.description %}
|
||||||
|
<i
|
||||||
|
class="mdi mdi-information text-primary"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-placement="right"
|
||||||
|
title="{{ field.description|escape }}"
|
||||||
|
></i>
|
||||||
|
{% endif %}
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
{% customfield_value field value %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endblock panel_content %}
|
||||||
7
netbox/templates/ui/panels/image_attachments.html
Normal file
7
netbox/templates/ui/panels/image_attachments.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{% extends "ui/panels/_base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{# TODO: Add "attach an image" button in panel header #}
|
||||||
|
{% block panel_content %}
|
||||||
|
{% htmx_table 'extras:imageattachment_list' object_type_id=object|content_type_id object_id=object.pk %}
|
||||||
|
{% endblock panel_content %}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<div class="card">
|
{% extends "ui/panels/_base.html" %}
|
||||||
<h2 class="card-header">{{ title }}</h2>
|
|
||||||
|
{% block panel_content %}
|
||||||
<table class="table table-hover attr-table">
|
<table class="table table-hover attr-table">
|
||||||
{% for attr in attrs %}
|
{% for attr in attrs %}
|
||||||
<tr>
|
<tr>
|
||||||
@@ -10,4 +11,4 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
{% endblock panel_content %}
|
||||||
|
|||||||
25
netbox/templates/ui/panels/related_objects.html
Normal file
25
netbox/templates/ui/panels/related_objects.html
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{% extends "ui/panels/_base.html" %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block panel_content %}
|
||||||
|
<ul class="list-group list-group-flush" role="presentation">
|
||||||
|
{% for related_object_count in related_models %}
|
||||||
|
{% action_url related_object_count.queryset.model 'list' as list_url %}
|
||||||
|
{% if list_url %}
|
||||||
|
<a href="{{ list_url }}?{{ related_object_count.filter_param }}={{ object.pk }}" class="list-group-item list-group-item-action d-flex justify-content-between">
|
||||||
|
{{ related_object_count.name }}
|
||||||
|
{% with count=related_object_count.queryset.count %}
|
||||||
|
{% if count %}
|
||||||
|
<span class="badge text-bg-primary rounded-pill">{{ count }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge text-bg-light rounded-pill">—</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% empty %}
|
||||||
|
<span class="list-group-item text-muted">{% trans "None" %}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endblock panel_content %}
|
||||||
15
netbox/templates/ui/panels/tags.html
Normal file
15
netbox/templates/ui/panels/tags.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{% extends "ui/panels/_base.html" %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block panel_content %}
|
||||||
|
<div class="card-body">
|
||||||
|
{% with url=object|validated_viewname:"list" %}
|
||||||
|
{% for tag in object.tags.all %}
|
||||||
|
{% tag tag url %}
|
||||||
|
{% empty %}
|
||||||
|
<span class="text-muted">{% trans "No tags assigned" %}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
{% endblock panel_content %}
|
||||||
Reference in New Issue
Block a user