mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-14 20:39:35 -06:00
Add rack role & type layouts
This commit is contained in:
parent
21bb734dcb
commit
17cffd7860
@ -23,6 +23,26 @@ class LocationPanel(panels.NestedGroupObjectPanel):
|
|||||||
facility = attrs.TextAttr('facility', label=_('Facility'))
|
facility = attrs.TextAttr('facility', label=_('Facility'))
|
||||||
|
|
||||||
|
|
||||||
|
class RackDimensionsPanel(panels.ObjectPanel):
|
||||||
|
form_factor = attrs.ChoiceAttr('form_factor', label=_('Form factor'))
|
||||||
|
width = attrs.ChoiceAttr('width', label=_('Width'))
|
||||||
|
u_height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height'))
|
||||||
|
outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display', label=_('Outer width'))
|
||||||
|
outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display', label=_('Outer height'))
|
||||||
|
outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display', label=_('Outer depth'))
|
||||||
|
mounting_depth = attrs.TextAttr('mounting_depth', format_string='{}mm', label=_('Mounting depth'))
|
||||||
|
|
||||||
|
|
||||||
|
class RackNumberingPanel(panels.ObjectPanel):
|
||||||
|
starting_unit = attrs.TextAttr('starting_unit', label=_('Starting unit'))
|
||||||
|
desc_units = attrs.BooleanAttr('desc_units', label=_('Descending units'))
|
||||||
|
|
||||||
|
|
||||||
|
class RackWeightPanel(panels.ObjectPanel):
|
||||||
|
weight = attrs.NumericAttr('weight', unit_accessor='get_weight_unit_display', label=_('Weight'))
|
||||||
|
max_weight = attrs.NumericAttr('max_weight', unit_accessor='get_weight_unit_display', label=_('Maximum weight'))
|
||||||
|
|
||||||
|
|
||||||
class RackPanel(panels.ObjectPanel):
|
class RackPanel(panels.ObjectPanel):
|
||||||
region = attrs.NestedObjectAttr('site.region', label=_('Region'), linkify=True)
|
region = attrs.NestedObjectAttr('site.region', label=_('Region'), linkify=True)
|
||||||
site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
|
site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
|
||||||
@ -40,6 +60,17 @@ class RackPanel(panels.ObjectPanel):
|
|||||||
power_utilization = attrs.UtilizationAttr('get_power_utilization', label=_('Power utilization'))
|
power_utilization = attrs.UtilizationAttr('get_power_utilization', label=_('Power utilization'))
|
||||||
|
|
||||||
|
|
||||||
|
class RackRolePanel(panels.OrganizationalObjectPanel):
|
||||||
|
color = attrs.ColorAttr('color')
|
||||||
|
|
||||||
|
|
||||||
|
class RackTypePanel(panels.ObjectPanel):
|
||||||
|
manufacturer = attrs.ObjectAttr('manufacturer', label=_('Manufacturer'), linkify=True)
|
||||||
|
model = attrs.TextAttr('model', label=_('Model'))
|
||||||
|
description = attrs.TextAttr('description', label=_('Description'))
|
||||||
|
airflow = attrs.ChoiceAttr('airflow', label=_('Airflow'))
|
||||||
|
|
||||||
|
|
||||||
class DevicePanel(panels.ObjectPanel):
|
class DevicePanel(panels.ObjectPanel):
|
||||||
region = attrs.NestedObjectAttr('site.region', label=_('Region'), linkify=True)
|
region = attrs.NestedObjectAttr('site.region', label=_('Region'), linkify=True)
|
||||||
site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
|
site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
|
||||||
|
|||||||
@ -527,7 +527,7 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView):
|
|||||||
layout = layout.Layout(
|
layout = layout.Layout(
|
||||||
layout.Row(
|
layout.Row(
|
||||||
layout.Column(
|
layout.Column(
|
||||||
panels.SitePanel(_('Site')),
|
panels.SitePanel(),
|
||||||
CustomFieldsPanel(),
|
CustomFieldsPanel(),
|
||||||
TagsPanel(),
|
TagsPanel(),
|
||||||
CommentsPanel(),
|
CommentsPanel(),
|
||||||
@ -817,6 +817,25 @@ class RackRoleListView(generic.ObjectListView):
|
|||||||
@register_model_view(RackRole)
|
@register_model_view(RackRole)
|
||||||
class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
|
class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = RackRole.objects.all()
|
queryset = RackRole.objects.all()
|
||||||
|
layout = layout.Layout(
|
||||||
|
layout.Row(
|
||||||
|
layout.Column(
|
||||||
|
panels.RackRolePanel(),
|
||||||
|
TagsPanel(),
|
||||||
|
PluginContentPanel('left_page'),
|
||||||
|
),
|
||||||
|
layout.Column(
|
||||||
|
RelatedObjectsPanel(),
|
||||||
|
CustomFieldsPanel(),
|
||||||
|
PluginContentPanel('right_page'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
layout.Row(
|
||||||
|
layout.Column(
|
||||||
|
PluginContentPanel('full_width_page'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
return {
|
return {
|
||||||
@ -884,6 +903,29 @@ class RackTypeListView(generic.ObjectListView):
|
|||||||
@register_model_view(RackType)
|
@register_model_view(RackType)
|
||||||
class RackTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
class RackTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = RackType.objects.all()
|
queryset = RackType.objects.all()
|
||||||
|
layout = layout.Layout(
|
||||||
|
layout.Row(
|
||||||
|
layout.Column(
|
||||||
|
panels.RackTypePanel(),
|
||||||
|
panels.RackDimensionsPanel(_('Dimensions')),
|
||||||
|
TagsPanel(),
|
||||||
|
CommentsPanel(),
|
||||||
|
PluginContentPanel('left_page'),
|
||||||
|
),
|
||||||
|
layout.Column(
|
||||||
|
panels.RackNumberingPanel(_('Numbering')),
|
||||||
|
panels.RackWeightPanel(_('Weight')),
|
||||||
|
CustomFieldsPanel(),
|
||||||
|
RelatedObjectsPanel(),
|
||||||
|
PluginContentPanel('right_page'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
layout.Row(
|
||||||
|
layout.Column(
|
||||||
|
PluginContentPanel('full_width_page'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -13,12 +13,14 @@ from netbox.config import get_config
|
|||||||
|
|
||||||
class Attr(ABC):
|
class Attr(ABC):
|
||||||
template_name = None
|
template_name = None
|
||||||
|
label = None
|
||||||
placeholder = mark_safe('<span class="text-muted">—</span>')
|
placeholder = mark_safe('<span class="text-muted">—</span>')
|
||||||
|
|
||||||
def __init__(self, accessor, label=None, template_name=None):
|
def __init__(self, accessor, label=None, template_name=None):
|
||||||
self.accessor = accessor
|
self.accessor = accessor
|
||||||
self.label = label
|
|
||||||
self.template_name = template_name or self.template_name
|
self.template_name = template_name or self.template_name
|
||||||
|
if label is not None:
|
||||||
|
self.label = label
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def render(self, obj, context=None):
|
def render(self, obj, context=None):
|
||||||
@ -37,9 +39,10 @@ class Attr(ABC):
|
|||||||
class TextAttr(Attr):
|
class TextAttr(Attr):
|
||||||
template_name = 'ui/attrs/text.html'
|
template_name = 'ui/attrs/text.html'
|
||||||
|
|
||||||
def __init__(self, *args, style=None, copy_button=False, **kwargs):
|
def __init__(self, *args, style=None, format_string=None, copy_button=False, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.style = style
|
self.style = style
|
||||||
|
self.format_string = format_string
|
||||||
self.copy_button = copy_button
|
self.copy_button = copy_button
|
||||||
|
|
||||||
def render(self, obj, context=None):
|
def render(self, obj, context=None):
|
||||||
@ -47,6 +50,8 @@ class TextAttr(Attr):
|
|||||||
value = self._resolve_attr(obj, self.accessor)
|
value = self._resolve_attr(obj, self.accessor)
|
||||||
if value in (None, ''):
|
if value in (None, ''):
|
||||||
return self.placeholder
|
return self.placeholder
|
||||||
|
if self.format_string:
|
||||||
|
value = self.format_string.format(value)
|
||||||
return render_to_string(self.template_name, {
|
return render_to_string(self.template_name, {
|
||||||
**context,
|
**context,
|
||||||
'value': value,
|
'value': value,
|
||||||
@ -55,6 +60,28 @@ class TextAttr(Attr):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class NumericAttr(Attr):
|
||||||
|
template_name = 'ui/attrs/numeric.html'
|
||||||
|
|
||||||
|
def __init__(self, *args, unit_accessor=None, copy_button=False, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.unit_accessor = unit_accessor
|
||||||
|
self.copy_button = copy_button
|
||||||
|
|
||||||
|
def render(self, obj, context=None):
|
||||||
|
context = context or {}
|
||||||
|
value = self._resolve_attr(obj, self.accessor)
|
||||||
|
if value in (None, ''):
|
||||||
|
return self.placeholder
|
||||||
|
unit = self._resolve_attr(obj, self.unit_accessor) if self.unit_accessor else None
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
**context,
|
||||||
|
'value': value,
|
||||||
|
'unit': unit,
|
||||||
|
'copy_button': self.copy_button,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class ChoiceAttr(Attr):
|
class ChoiceAttr(Attr):
|
||||||
template_name = 'ui/attrs/choice.html'
|
template_name = 'ui/attrs/choice.html'
|
||||||
|
|
||||||
@ -77,6 +104,37 @@ class ChoiceAttr(Attr):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class BooleanAttr(Attr):
|
||||||
|
template_name = 'ui/attrs/boolean.html'
|
||||||
|
|
||||||
|
def __init__(self, *args, display_false=True, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.display_false = display_false
|
||||||
|
|
||||||
|
def render(self, obj, context=None):
|
||||||
|
context = context or {}
|
||||||
|
value = self._resolve_attr(obj, self.accessor)
|
||||||
|
if value in (None, '') and not self.display_false:
|
||||||
|
return self.placeholder
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
**context,
|
||||||
|
'value': value,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class ColorAttr(Attr):
|
||||||
|
template_name = 'ui/attrs/color.html'
|
||||||
|
label = _('Color')
|
||||||
|
|
||||||
|
def render(self, obj, context=None):
|
||||||
|
context = context or {}
|
||||||
|
value = self._resolve_attr(obj, self.accessor)
|
||||||
|
return render_to_string(self.template_name, {
|
||||||
|
**context,
|
||||||
|
'color': value,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class ObjectAttr(Attr):
|
class ObjectAttr(Attr):
|
||||||
template_name = 'ui/attrs/object.html'
|
template_name = 'ui/attrs/object.html'
|
||||||
|
|
||||||
@ -149,9 +207,9 @@ class AddressAttr(Attr):
|
|||||||
|
|
||||||
class GPSCoordinatesAttr(Attr):
|
class GPSCoordinatesAttr(Attr):
|
||||||
template_name = 'ui/attrs/gps_coordinates.html'
|
template_name = 'ui/attrs/gps_coordinates.html'
|
||||||
|
label = _('GPS Coordinates')
|
||||||
|
|
||||||
def __init__(self, latitude_attr='latitude', longitude_attr='longitude', map_url=True, **kwargs):
|
def __init__(self, latitude_attr='latitude', longitude_attr='longitude', map_url=True, **kwargs):
|
||||||
kwargs.setdefault('label', _('GPS Coordinates'))
|
|
||||||
super().__init__(accessor=None, **kwargs)
|
super().__init__(accessor=None, **kwargs)
|
||||||
self.latitude_attr = latitude_attr
|
self.latitude_attr = latitude_attr
|
||||||
self.longitude_attr = longitude_attr
|
self.longitude_attr = longitude_attr
|
||||||
|
|||||||
@ -19,6 +19,7 @@ __all__ = (
|
|||||||
'NestedGroupObjectPanel',
|
'NestedGroupObjectPanel',
|
||||||
'ObjectPanel',
|
'ObjectPanel',
|
||||||
'ObjectsTablePanel',
|
'ObjectsTablePanel',
|
||||||
|
'OrganizationalObjectPanel',
|
||||||
'RelatedObjectsPanel',
|
'RelatedObjectsPanel',
|
||||||
'Panel',
|
'Panel',
|
||||||
'PluginContentPanel',
|
'PluginContentPanel',
|
||||||
@ -45,7 +46,7 @@ class Panel(ABC):
|
|||||||
return render_to_string(self.template_name, {
|
return render_to_string(self.template_name, {
|
||||||
'request': context.get('request'),
|
'request': context.get('request'),
|
||||||
'object': obj,
|
'object': obj,
|
||||||
'title': self.title,
|
'title': self.title or title(obj._meta.verbose_name),
|
||||||
'actions': [action.get_context(obj) for action in self.actions],
|
'actions': [action.get_context(obj) for action in self.actions],
|
||||||
**self.get_context(obj),
|
**self.get_context(obj),
|
||||||
})
|
})
|
||||||
@ -93,9 +94,12 @@ class ObjectPanel(Panel, metaclass=ObjectPanelMeta):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NestedGroupObjectPanel(ObjectPanel, metaclass=ObjectPanelMeta):
|
class OrganizationalObjectPanel(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'))
|
||||||
|
|
||||||
|
|
||||||
|
class NestedGroupObjectPanel(OrganizationalObjectPanel, metaclass=ObjectPanelMeta):
|
||||||
parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)
|
parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
netbox/templates/ui/attrs/boolean.html
Normal file
1
netbox/templates/ui/attrs/boolean.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% checkmark object.desc_units %}
|
||||||
1
netbox/templates/ui/attrs/color.html
Normal file
1
netbox/templates/ui/attrs/color.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<span class="badge color-label" style="background-color: #{{ color }}"> </span>
|
||||||
12
netbox/templates/ui/attrs/numeric.html
Normal file
12
netbox/templates/ui/attrs/numeric.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<span{% if style %} class="{{ style }}"{% endif %}>
|
||||||
|
<span{% if name %} id="attr_{{ name }}"{% endif %}>{{ value }}</span>
|
||||||
|
{% if unit %}
|
||||||
|
{{ unit|lower }}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
{% if copy_button %}
|
||||||
|
<a class="btn btn-sm btn-primary copy-content" data-clipboard-target="#attr_{{ name }}" title="{% trans "Copy to clipboard" %}">
|
||||||
|
<i class="mdi mdi-content-copy"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
Loading…
Reference in New Issue
Block a user