mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-01 23:23:24 +02:00
Compare commits
3 Commits
21770-embe
...
20924-plug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
faf8554d2c | ||
|
|
623ab55d5b | ||
|
|
c9073aca3c |
@@ -85,26 +85,6 @@ NetBox also includes a set of panels suite for specific uses, such as display ob
|
||||
|
||||
::: netbox.ui.panels.ObjectAttributesPanel
|
||||
|
||||
#### Object Attributes
|
||||
|
||||
The following classes are available to represent object attributes within an ObjectAttributesPanel. Additionally, plugins can subclass `netbox.ui.attrs.ObjectAttribute` to create custom classes.
|
||||
|
||||
| Class | Description |
|
||||
|--------------------------------------|--------------------------------------------------|
|
||||
| `netbox.ui.attrs.AddressAttr` | A physical or mailing address. |
|
||||
| `netbox.ui.attrs.BooleanAttr` | A boolean value |
|
||||
| `netbox.ui.attrs.ColorAttr` | A color expressed in RGB |
|
||||
| `netbox.ui.attrs.ChoiceAttr` | A selection from a set of choices |
|
||||
| `netbox.ui.attrs.GPSCoordinatesAttr` | GPS coordinates (latitude and longitude) |
|
||||
| `netbox.ui.attrs.ImageAttr` | An attached image (displays the image) |
|
||||
| `netbox.ui.attrs.NestedObjectAttr` | A related nested object |
|
||||
| `netbox.ui.attrs.NumericAttr` | An integer or float value |
|
||||
| `netbox.ui.attrs.RelatedObjectAttr` | A related object |
|
||||
| `netbox.ui.attrs.TemplatedAttr` | Renders an attribute using a custom template |
|
||||
| `netbox.ui.attrs.TextAttr` | A string (text) value |
|
||||
| `netbox.ui.attrs.TimezoneAttr` | A timezone with annotated offset |
|
||||
| `netbox.ui.attrs.UtilizationAttr` | A numeric value expressed as a utilization graph |
|
||||
|
||||
::: netbox.ui.panels.OrganizationalObjectPanel
|
||||
|
||||
::: netbox.ui.panels.NestedGroupObjectPanel
|
||||
@@ -119,9 +99,13 @@ The following classes are available to represent object attributes within an Obj
|
||||
|
||||
::: netbox.ui.panels.TemplatePanel
|
||||
|
||||
::: netbox.ui.panels.TextCodePanel
|
||||
|
||||
::: netbox.ui.panels.ContextTablePanel
|
||||
|
||||
::: netbox.ui.panels.PluginContentPanel
|
||||
|
||||
## Panel Actions
|
||||
### Panel Actions
|
||||
|
||||
Each panel may have actions associated with it. These render as links or buttons within the panel header, opposite the panel's title. For example, a common use case is to include an "Add" action on a panel which displays a list of objects. Below is an example of this.
|
||||
|
||||
@@ -146,3 +130,60 @@ panels.ObjectsTablePanel(
|
||||
::: netbox.ui.actions.AddObject
|
||||
|
||||
::: netbox.ui.actions.CopyContent
|
||||
|
||||
## Object Attributes
|
||||
|
||||
The following classes are available to represent object attributes within an ObjectAttributesPanel. Additionally, plugins can subclass `netbox.ui.attrs.ObjectAttribute` to create custom classes.
|
||||
|
||||
| Class | Description |
|
||||
|------------------------------------------|--------------------------------------------------|
|
||||
| `netbox.ui.attrs.AddressAttr` | A physical or mailing address. |
|
||||
| `netbox.ui.attrs.BooleanAttr` | A boolean value |
|
||||
| `netbox.ui.attrs.ChoiceAttr` | A selection from a set of choices |
|
||||
| `netbox.ui.attrs.ColorAttr` | A color expressed in RGB |
|
||||
| `netbox.ui.attrs.DateTimeAttr` | A date or datetime value |
|
||||
| `netbox.ui.attrs.GenericForeignKeyAttr` | A related object via a generic foreign key |
|
||||
| `netbox.ui.attrs.GPSCoordinatesAttr` | GPS coordinates (latitude and longitude) |
|
||||
| `netbox.ui.attrs.ImageAttr` | An attached image (displays the image) |
|
||||
| `netbox.ui.attrs.NestedObjectAttr` | A related nested object (includes ancestors) |
|
||||
| `netbox.ui.attrs.NumericAttr` | An integer or float value |
|
||||
| `netbox.ui.attrs.RelatedObjectAttr` | A related object |
|
||||
| `netbox.ui.attrs.RelatedObjectListAttr` | A list of related objects |
|
||||
| `netbox.ui.attrs.TemplatedAttr` | Renders an attribute using a custom template |
|
||||
| `netbox.ui.attrs.TextAttr` | A string (text) value |
|
||||
| `netbox.ui.attrs.TimezoneAttr` | A timezone with annotated offset |
|
||||
| `netbox.ui.attrs.UtilizationAttr` | A numeric value expressed as a utilization graph |
|
||||
|
||||
::: netbox.ui.attrs.ObjectAttribute
|
||||
|
||||
::: netbox.ui.attrs.AddressAttr
|
||||
|
||||
::: netbox.ui.attrs.BooleanAttr
|
||||
|
||||
::: netbox.ui.attrs.ChoiceAttr
|
||||
|
||||
::: netbox.ui.attrs.ColorAttr
|
||||
|
||||
::: netbox.ui.attrs.DateTimeAttr
|
||||
|
||||
::: netbox.ui.attrs.GenericForeignKeyAttr
|
||||
|
||||
::: netbox.ui.attrs.GPSCoordinatesAttr
|
||||
|
||||
::: netbox.ui.attrs.ImageAttr
|
||||
|
||||
::: netbox.ui.attrs.NestedObjectAttr
|
||||
|
||||
::: netbox.ui.attrs.NumericAttr
|
||||
|
||||
::: netbox.ui.attrs.RelatedObjectAttr
|
||||
|
||||
::: netbox.ui.attrs.RelatedObjectListAttr
|
||||
|
||||
::: netbox.ui.attrs.TemplatedAttr
|
||||
|
||||
::: netbox.ui.attrs.TextAttr
|
||||
|
||||
::: netbox.ui.attrs.TimezoneAttr
|
||||
|
||||
::: netbox.ui.attrs.UtilizationAttr
|
||||
|
||||
@@ -58,6 +58,26 @@ class CircuitGroupAssignmentsPanel(panels.ObjectsTablePanel):
|
||||
)
|
||||
|
||||
|
||||
class CircuitTerminationPanel(panels.ObjectAttributesPanel):
|
||||
title = _('Circuit Termination')
|
||||
circuit = attrs.RelatedObjectAttr('circuit', linkify=True)
|
||||
provider = attrs.RelatedObjectAttr('circuit.provider', linkify=True)
|
||||
termination = attrs.GenericForeignKeyAttr('termination', linkify=True, label=_('Termination point'))
|
||||
connection = attrs.TemplatedAttr(
|
||||
'pk',
|
||||
template_name='circuits/circuit_termination/attrs/connection.html',
|
||||
label=_('Connection'),
|
||||
)
|
||||
speed = attrs.TemplatedAttr(
|
||||
'port_speed',
|
||||
template_name='circuits/circuit_termination/attrs/speed.html',
|
||||
label=_('Speed'),
|
||||
)
|
||||
xconnect_id = attrs.TextAttr('xconnect_id', label=_('Cross-Connect'), style='font-monospace')
|
||||
pp_info = attrs.TextAttr('pp_info', label=_('Patch Panel/Port'))
|
||||
description = attrs.TextAttr('description')
|
||||
|
||||
|
||||
class CircuitGroupPanel(panels.OrganizationalObjectPanel):
|
||||
tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ from netbox.ui import actions, layout
|
||||
from netbox.ui.panels import (
|
||||
CommentsPanel,
|
||||
ObjectsTablePanel,
|
||||
Panel,
|
||||
RelatedObjectsPanel,
|
||||
)
|
||||
from netbox.views import generic
|
||||
@@ -508,10 +507,7 @@ class CircuitTerminationView(generic.ObjectView):
|
||||
queryset = CircuitTermination.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
Panel(
|
||||
template_name='circuits/panels/circuit_termination.html',
|
||||
title=_('Circuit Termination'),
|
||||
)
|
||||
panels.CircuitTerminationPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
CustomFieldsPanel(),
|
||||
|
||||
@@ -59,7 +59,7 @@ class PanelAction:
|
||||
"""
|
||||
# Enforce permissions
|
||||
user = context['request'].user
|
||||
if not user.has_perms(self.permissions):
|
||||
if self.permissions and not user.has_perms(self.permissions):
|
||||
return ''
|
||||
|
||||
return render_to_string(self.template_name, self.get_context(context))
|
||||
|
||||
@@ -506,6 +506,7 @@ class TemplatedAttr(ObjectAttribute):
|
||||
|
||||
def get_context(self, obj, context):
|
||||
return {
|
||||
**context,
|
||||
**self.context,
|
||||
'object': obj,
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class SimpleLayout(Layout):
|
||||
"""
|
||||
A layout with one row of two columns and a second row with one column.
|
||||
|
||||
Plugin content registered for `left_page`, `right_page`, or `full_width_path` is included automatically. Most object
|
||||
Plugin content registered for `left_page`, `right_page`, or `full_width_page` is included automatically. Most object
|
||||
views in NetBox utilize this layout.
|
||||
|
||||
```
|
||||
|
||||
@@ -187,7 +187,7 @@ class ObjectAttributesPanel(ObjectPanel, metaclass=ObjectAttributesPanelMeta):
|
||||
'attrs': [
|
||||
{
|
||||
'label': attr.label or self._name_to_label(name),
|
||||
'value': attr.render(ctx['object'], {'name': name}),
|
||||
'value': attr.render(ctx['object'], {'name': name, 'perms': ctx['perms']}),
|
||||
} for name, attr in self._attrs.items() if name in attr_names
|
||||
],
|
||||
}
|
||||
@@ -225,9 +225,10 @@ class CommentsPanel(ObjectPanel):
|
||||
self.field_name = field_name
|
||||
|
||||
def get_context(self, context):
|
||||
ctx = super().get_context(context)
|
||||
return {
|
||||
**super().get_context(context),
|
||||
'comments': getattr(context['object'], self.field_name),
|
||||
**ctx,
|
||||
'comments': getattr(ctx['object'], self.field_name),
|
||||
}
|
||||
|
||||
|
||||
@@ -249,9 +250,10 @@ class JSONPanel(ObjectPanel):
|
||||
self.actions.append(CopyContent(f'panel_{field_name}'))
|
||||
|
||||
def get_context(self, context):
|
||||
ctx = super().get_context(context)
|
||||
return {
|
||||
**super().get_context(context),
|
||||
'data': getattr(context['object'], self.field_name),
|
||||
**ctx,
|
||||
'data': getattr(ctx['object'], self.field_name),
|
||||
'field_name': self.field_name,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{% load helpers i18n %}
|
||||
{% if object.mark_connected %}
|
||||
<div>
|
||||
<span class="text-success"><i class="mdi mdi-check-bold"></i></span>
|
||||
<span class="text-muted">{% trans "Marked as connected" %}</span>
|
||||
</div>
|
||||
{% elif object.cable %}
|
||||
<div>
|
||||
<a class="d-block d-md-inline" href="{{ object.cable.get_absolute_url }}">{{ object.cable }}</a> {% trans "to" %}
|
||||
{% for peer in object.link_peers %}
|
||||
{% if peer.device %}
|
||||
{{ peer.device|linkify }}<br/>
|
||||
{% elif peer.circuit %}
|
||||
{{ peer.circuit|linkify }}<br/>
|
||||
{% endif %}
|
||||
{{ peer|linkify }}{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<a href="{% url 'circuits:circuittermination_trace' pk=object.pk %}" class="btn btn-sm btn-primary" title="{% trans "Trace" %}">
|
||||
<i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i> {% trans "Trace" %}
|
||||
</a>
|
||||
{% if perms.dcim.change_cable %}
|
||||
<a href="{% url 'dcim:cable_edit' pk=object.cable.pk %}?return_url={{ object.circuit.get_absolute_url }}" title="{% trans "Edit cable" %}" class="btn btn-sm btn-warning">
|
||||
<i class="mdi mdi-ethernet-cable" aria-hidden="true"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_cable %}
|
||||
<a href="{% url 'dcim:cable_delete' pk=object.cable.pk %}?return_url={{ object.circuit.get_absolute_url }}" title="{% trans "Remove cable" %}" class="btn btn-sm btn-danger">
|
||||
<i class="mdi mdi-ethernet-cable-off" aria-hidden="true"></i> {% trans "Disconnect" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elif perms.dcim.add_cable %}
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> {% trans "Connect" %}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=circuits.circuittermination&a_terminations={{ object.pk }}&b_terminations_type=dcim.interface&return_url={{ object.get_absolute_url }}">{% trans "Interface" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=circuits.circuittermination&a_terminations={{ object.pk }}&b_terminations_type=dcim.frontport&return_url={{ object.get_absolute_url }}">{% trans "Front Port" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=circuits.circuittermination&a_terminations={{ object.pk }}&b_terminations_type=dcim.rearport&return_url={{ object.get_absolute_url }}">{% trans "Rear Port" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'dcim:cable_add' %}?a_terminations_type=circuits.circuittermination&a_terminations={{ object.pk }}&b_terminations_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">{% trans "Circuit Termination" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ ''|placeholder }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% load helpers i18n %}
|
||||
{% if object.upstream_speed %}
|
||||
<i class="mdi mdi-arrow-down-bold" title="{% trans "Downstream" %}"></i> {{ object.port_speed|humanize_speed }}
|
||||
<i class="mdi mdi-slash-forward"></i>
|
||||
<i class="mdi mdi-arrow-up-bold" title="{% trans "Upstream" %}"></i> {{ object.upstream_speed|humanize_speed }}
|
||||
{% else %}
|
||||
{{ object.port_speed|humanize_speed }}
|
||||
{% endif %}
|
||||
@@ -1,16 +0,0 @@
|
||||
{% extends "ui/panels/_base.html" %}
|
||||
{% load helpers i18n %}
|
||||
|
||||
{% block panel_content %}
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Circuit" %}</th>
|
||||
<td>{{ object.circuit|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Provider" %}</th>
|
||||
<td>{{ object.circuit.provider|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
{% include 'circuits/inc/circuit_termination_fields.html' with termination=object %}
|
||||
</table>
|
||||
{% endblock panel_content %}
|
||||
Reference in New Issue
Block a user