mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-14 04:19:36 -06:00
Add layouts for DeviceType & ModuleTypeProfile
This commit is contained in:
parent
d5cec3723e
commit
1de41b4964
@ -112,3 +112,24 @@ class DeviceManagementPanel(panels.ObjectPanel):
|
|||||||
label=_('Out-of-band IP'),
|
label=_('Out-of-band IP'),
|
||||||
template_name='dcim/device/attrs/ipaddress.html',
|
template_name='dcim/device/attrs/ipaddress.html',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceTypePanel(panels.ObjectPanel):
|
||||||
|
manufacturer = attrs.ObjectAttr('manufacturer', label=_('Manufacturer'), linkify=True)
|
||||||
|
model = attrs.TextAttr('model', label=_('Model'))
|
||||||
|
part_number = attrs.TextAttr('part_number', label=_('Part number'))
|
||||||
|
default_platform = attrs.ObjectAttr('default_platform', label=_('Default platform'), linkify=True)
|
||||||
|
description = attrs.TextAttr('description', label=_('Description'))
|
||||||
|
u_height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height'))
|
||||||
|
exclude_from_utilization = attrs.BooleanAttr('exclude_from_utilization', label=_('Exclude from utilization'))
|
||||||
|
full_depth = attrs.BooleanAttr('is_full_depth', label=_('Full depth'))
|
||||||
|
weight = attrs.NumericAttr('weight', unit_accessor='get_weight_unit_display', label=_('Weight'))
|
||||||
|
subdevice_role = attrs.ChoiceAttr('subdevice_role', label=_('Parent/child'))
|
||||||
|
airflow = attrs.ChoiceAttr('airflow', label=_('Airflow'))
|
||||||
|
front_image = attrs.ImageAttr('front_image', label=_('Front image'))
|
||||||
|
rear_image = attrs.ImageAttr('rear_image', label=_('Rear image'))
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleTypeProfilePanel(panels.ObjectPanel):
|
||||||
|
name = attrs.TextAttr('name', label=_('Name'))
|
||||||
|
description = attrs.TextAttr('description', label=_('Description'))
|
||||||
|
|||||||
@ -21,7 +21,7 @@ from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
|||||||
from netbox.object_actions import *
|
from netbox.object_actions import *
|
||||||
from netbox.ui import actions, layout
|
from netbox.ui import actions, layout
|
||||||
from netbox.ui.panels import (
|
from netbox.ui.panels import (
|
||||||
CommentsPanel, NestedGroupObjectPanel, ObjectsTablePanel, OrganizationalObjectPanel, RelatedObjectsPanel,
|
CommentsPanel, JSONPanel, NestedGroupObjectPanel, ObjectsTablePanel, OrganizationalObjectPanel, RelatedObjectsPanel,
|
||||||
TemplatePanel,
|
TemplatePanel,
|
||||||
)
|
)
|
||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
@ -1308,6 +1308,18 @@ class DeviceTypeListView(generic.ObjectListView):
|
|||||||
@register_model_view(DeviceType)
|
@register_model_view(DeviceType)
|
||||||
class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = DeviceType.objects.all()
|
queryset = DeviceType.objects.all()
|
||||||
|
layout = layout.SimpleLayout(
|
||||||
|
left_panels=[
|
||||||
|
panels.DeviceTypePanel(),
|
||||||
|
TagsPanel(),
|
||||||
|
],
|
||||||
|
right_panels=[
|
||||||
|
RelatedObjectsPanel(),
|
||||||
|
CustomFieldsPanel(),
|
||||||
|
CommentsPanel(),
|
||||||
|
ImageAttachmentsPanel(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
return {
|
return {
|
||||||
@ -1559,6 +1571,34 @@ class ModuleTypeProfileListView(generic.ObjectListView):
|
|||||||
@register_model_view(ModuleTypeProfile)
|
@register_model_view(ModuleTypeProfile)
|
||||||
class ModuleTypeProfileView(GetRelatedModelsMixin, generic.ObjectView):
|
class ModuleTypeProfileView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = ModuleTypeProfile.objects.all()
|
queryset = ModuleTypeProfile.objects.all()
|
||||||
|
layout = layout.SimpleLayout(
|
||||||
|
left_panels=[
|
||||||
|
panels.ModuleTypeProfilePanel(),
|
||||||
|
TagsPanel(),
|
||||||
|
CommentsPanel(),
|
||||||
|
],
|
||||||
|
right_panels=[
|
||||||
|
JSONPanel(field_name='schema', title=_('Schema')),
|
||||||
|
CustomFieldsPanel(),
|
||||||
|
],
|
||||||
|
bottom_panels=[
|
||||||
|
ObjectsTablePanel(
|
||||||
|
model='dcim.ModuleType',
|
||||||
|
title=_('Module Types'),
|
||||||
|
filters={
|
||||||
|
'profile_id': lambda ctx: ctx['object'].pk,
|
||||||
|
},
|
||||||
|
actions=[
|
||||||
|
actions.AddObject(
|
||||||
|
'dcim.ModuleType',
|
||||||
|
url_params={
|
||||||
|
'profile': lambda ctx: ctx['object'].pk,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register_model_view(ModuleTypeProfile, 'add', detail=False)
|
@register_model_view(ModuleTypeProfile, 'add', detail=False)
|
||||||
|
|||||||
@ -24,11 +24,12 @@ class PanelAction:
|
|||||||
button_class: Bootstrap CSS class for the button
|
button_class: Bootstrap CSS class for the button
|
||||||
button_icon: Name of the button's MDI icon
|
button_icon: Name of the button's MDI icon
|
||||||
"""
|
"""
|
||||||
template_name = 'ui/action.html'
|
template_name = 'ui/actions/link.html'
|
||||||
label = None
|
label = None
|
||||||
button_class = 'primary'
|
button_class = 'primary'
|
||||||
button_icon = None
|
button_icon = None
|
||||||
|
|
||||||
|
# TODO: Refactor URL parameters to AddObject
|
||||||
def __init__(self, view_name, view_kwargs=None, url_params=None, permissions=None, label=None):
|
def __init__(self, view_name, view_kwargs=None, url_params=None, permissions=None, label=None):
|
||||||
"""
|
"""
|
||||||
Initialize a new PanelAction.
|
Initialize a new PanelAction.
|
||||||
@ -114,3 +115,30 @@ class AddObject(PanelAction):
|
|||||||
|
|
||||||
# Require "add" permission on the model
|
# Require "add" permission on the model
|
||||||
self.permissions = [get_permission_for_model(model, 'add')]
|
self.permissions = [get_permission_for_model(model, 'add')]
|
||||||
|
|
||||||
|
|
||||||
|
class CopyContent:
|
||||||
|
"""
|
||||||
|
An action to copy the contents of a panel to the clipboard.
|
||||||
|
"""
|
||||||
|
template_name = 'ui/actions/copy_content.html'
|
||||||
|
label = _('Copy')
|
||||||
|
button_class = 'primary'
|
||||||
|
button_icon = 'content-copy'
|
||||||
|
|
||||||
|
def __init__(self, target_id):
|
||||||
|
self.target_id = target_id
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
"""
|
||||||
|
Render the action as HTML.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
context: The template context
|
||||||
|
"""
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
'target_id': self.target_id,
|
||||||
|
'label': self.label,
|
||||||
|
'button_class': self.button_class,
|
||||||
|
'button_icon': self.button_icon,
|
||||||
|
})
|
||||||
|
|||||||
@ -135,6 +135,20 @@ class ColorAttr(Attr):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class ImageAttr(Attr):
|
||||||
|
template_name = 'ui/attrs/image.html'
|
||||||
|
|
||||||
|
def render(self, obj, context=None):
|
||||||
|
context = context or {}
|
||||||
|
value = self._resolve_attr(obj, self.accessor)
|
||||||
|
if value in (None, ''):
|
||||||
|
return self.placeholder
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
**context,
|
||||||
|
'value': value,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class ObjectAttr(Attr):
|
class ObjectAttr(Attr):
|
||||||
template_name = 'ui/attrs/object.html'
|
template_name = 'ui/attrs/object.html'
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from django.template.loader import render_to_string
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from netbox.ui import attrs
|
from netbox.ui import attrs
|
||||||
|
from netbox.ui.actions import CopyContent
|
||||||
from utilities.querydict import dict_to_querydict
|
from utilities.querydict import dict_to_querydict
|
||||||
from utilities.string import title
|
from utilities.string import title
|
||||||
from utilities.templatetags.plugins import _get_registered_content
|
from utilities.templatetags.plugins import _get_registered_content
|
||||||
@ -12,6 +13,7 @@ from utilities.views import get_viewname
|
|||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'CommentsPanel',
|
'CommentsPanel',
|
||||||
|
'JSONPanel',
|
||||||
'NestedGroupObjectPanel',
|
'NestedGroupObjectPanel',
|
||||||
'ObjectPanel',
|
'ObjectPanel',
|
||||||
'ObjectsTablePanel',
|
'ObjectsTablePanel',
|
||||||
@ -34,7 +36,7 @@ class Panel(ABC):
|
|||||||
"""
|
"""
|
||||||
template_name = None
|
template_name = None
|
||||||
title = None
|
title = None
|
||||||
actions = []
|
actions = None
|
||||||
|
|
||||||
def __init__(self, title=None, actions=None):
|
def __init__(self, title=None, actions=None):
|
||||||
"""
|
"""
|
||||||
@ -46,8 +48,7 @@ class Panel(ABC):
|
|||||||
"""
|
"""
|
||||||
if title is not None:
|
if title is not None:
|
||||||
self.title = title
|
self.title = title
|
||||||
if actions is not None:
|
self.actions = actions or []
|
||||||
self.actions = actions
|
|
||||||
|
|
||||||
def get_context(self, context):
|
def get_context(self, context):
|
||||||
"""
|
"""
|
||||||
@ -251,6 +252,42 @@ class ObjectsTablePanel(Panel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class JSONPanel(Panel):
|
||||||
|
"""
|
||||||
|
A panel which renders formatted JSON data.
|
||||||
|
"""
|
||||||
|
template_name = 'ui/panels/json.html'
|
||||||
|
|
||||||
|
def __init__(self, field_name, copy_button=True, **kwargs):
|
||||||
|
"""
|
||||||
|
Instantiate a new JSONPanel.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
field_name: The name of the JSON field on the object
|
||||||
|
copy_button: Set to True (default) to include a copy-to-clipboard button
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.field_name = field_name
|
||||||
|
|
||||||
|
if copy_button:
|
||||||
|
self.actions.append(
|
||||||
|
CopyContent(f'panel_{field_name}'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_context(self, context):
|
||||||
|
"""
|
||||||
|
Return the context data to be used when rendering the panel.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
context: The template context
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
**super().get_context(context),
|
||||||
|
'data': getattr(context['object'], self.field_name),
|
||||||
|
'field_name': self.field_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TemplatePanel(Panel):
|
class TemplatePanel(Panel):
|
||||||
"""
|
"""
|
||||||
A panel which renders content using an HTML template.
|
A panel which renders content using an HTML template.
|
||||||
|
|||||||
7
netbox/templates/ui/actions/copy_content.html
Normal file
7
netbox/templates/ui/actions/copy_content.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<a class="btn btn-ghost-{{ button_class }} btn-sm copy-content" data-clipboard-target="#{{ target_id }}" title="{% trans "Copy to clipboard" %}">
|
||||||
|
{% if button_icon %}
|
||||||
|
<i class="mdi mdi-{{ button_icon }}" aria-hidden="true"></i>
|
||||||
|
{% endif %}
|
||||||
|
{{ label }}
|
||||||
|
</a>
|
||||||
@ -1,4 +1,4 @@
|
|||||||
<a href="{{ url }}" class="btn btn-ghost-{{ button_class }} btn-sm">
|
<a {% if url %}href="{{ url }}" {% endif %}class="btn btn-ghost-{{ button_class }} btn-sm">
|
||||||
{% if button_icon %}
|
{% if button_icon %}
|
||||||
<i class="mdi mdi-{{ button_icon }}" aria-hidden="true"></i>
|
<i class="mdi mdi-{{ button_icon }}" aria-hidden="true"></i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -1 +1 @@
|
|||||||
{% checkmark object.desc_units %}
|
{% checkmark value %}
|
||||||
|
|||||||
3
netbox/templates/ui/attrs/image.html
Normal file
3
netbox/templates/ui/attrs/image.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<a href="{{ value.url }}">
|
||||||
|
<img src="{{ value.url }}" alt="{{ value.name }}" class="img-fluid" />
|
||||||
|
</a>
|
||||||
5
netbox/templates/ui/panels/json.html
Normal file
5
netbox/templates/ui/panels/json.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends "ui/panels/_base.html" %}
|
||||||
|
|
||||||
|
{% block panel_content %}
|
||||||
|
<pre id="panel_{{ field_name }}">{{ data|json }}</pre>
|
||||||
|
{% endblock panel_content %}
|
||||||
Loading…
Reference in New Issue
Block a user