mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-13 20:09:37 -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'),
|
||||
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.ui import actions, layout
|
||||
from netbox.ui.panels import (
|
||||
CommentsPanel, NestedGroupObjectPanel, ObjectsTablePanel, OrganizationalObjectPanel, RelatedObjectsPanel,
|
||||
CommentsPanel, JSONPanel, NestedGroupObjectPanel, ObjectsTablePanel, OrganizationalObjectPanel, RelatedObjectsPanel,
|
||||
TemplatePanel,
|
||||
)
|
||||
from netbox.views import generic
|
||||
@ -1308,6 +1308,18 @@ class DeviceTypeListView(generic.ObjectListView):
|
||||
@register_model_view(DeviceType)
|
||||
class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
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):
|
||||
return {
|
||||
@ -1559,6 +1571,34 @@ class ModuleTypeProfileListView(generic.ObjectListView):
|
||||
@register_model_view(ModuleTypeProfile)
|
||||
class ModuleTypeProfileView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
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)
|
||||
|
||||
@ -24,11 +24,12 @@ class PanelAction:
|
||||
button_class: Bootstrap CSS class for the button
|
||||
button_icon: Name of the button's MDI icon
|
||||
"""
|
||||
template_name = 'ui/action.html'
|
||||
template_name = 'ui/actions/link.html'
|
||||
label = None
|
||||
button_class = 'primary'
|
||||
button_icon = None
|
||||
|
||||
# TODO: Refactor URL parameters to AddObject
|
||||
def __init__(self, view_name, view_kwargs=None, url_params=None, permissions=None, label=None):
|
||||
"""
|
||||
Initialize a new PanelAction.
|
||||
@ -114,3 +115,30 @@ class AddObject(PanelAction):
|
||||
|
||||
# Require "add" permission on the model
|
||||
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):
|
||||
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 netbox.ui import attrs
|
||||
from netbox.ui.actions import CopyContent
|
||||
from utilities.querydict import dict_to_querydict
|
||||
from utilities.string import title
|
||||
from utilities.templatetags.plugins import _get_registered_content
|
||||
@ -12,6 +13,7 @@ from utilities.views import get_viewname
|
||||
|
||||
__all__ = (
|
||||
'CommentsPanel',
|
||||
'JSONPanel',
|
||||
'NestedGroupObjectPanel',
|
||||
'ObjectPanel',
|
||||
'ObjectsTablePanel',
|
||||
@ -34,7 +36,7 @@ class Panel(ABC):
|
||||
"""
|
||||
template_name = None
|
||||
title = None
|
||||
actions = []
|
||||
actions = None
|
||||
|
||||
def __init__(self, title=None, actions=None):
|
||||
"""
|
||||
@ -46,8 +48,7 @@ class Panel(ABC):
|
||||
"""
|
||||
if title is not None:
|
||||
self.title = title
|
||||
if actions is not None:
|
||||
self.actions = actions
|
||||
self.actions = actions or []
|
||||
|
||||
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):
|
||||
"""
|
||||
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 %}
|
||||
<i class="mdi mdi-{{ button_icon }}" aria-hidden="true"></i>
|
||||
{% 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