From c1127148e28a3a70da19da5c1e5f8ca6ce594896 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 9 Apr 2019 16:49:04 -0400 Subject: [PATCH] Provide individual views for each type of cable connection --- netbox/circuits/urls.py | 2 +- netbox/dcim/forms.py | 124 +++++++++++++----- netbox/dcim/urls.py | 14 +- netbox/dcim/views.py | 41 +++--- netbox/templates/dcim/cable_connect.html | 29 ++-- netbox/templates/dcim/inc/consoleport.html | 13 +- .../templates/dcim/inc/consoleserverport.html | 13 +- netbox/templates/dcim/inc/frontport.html | 14 +- netbox/templates/dcim/inc/interface.html | 14 +- netbox/templates/dcim/inc/poweroutlet.html | 2 +- netbox/templates/dcim/inc/powerport.html | 4 +- netbox/templates/dcim/inc/rearport.html | 14 +- 12 files changed, 198 insertions(+), 86 deletions(-) diff --git a/netbox/circuits/urls.py b/netbox/circuits/urls.py index be1106308..5b818a945 100644 --- a/netbox/circuits/urls.py +++ b/netbox/circuits/urls.py @@ -43,7 +43,7 @@ urlpatterns = [ url(r'^circuits/(?P\d+)/terminations/add/$', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'), url(r'^circuit-terminations/(?P\d+)/edit/$', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'), url(r'^circuit-terminations/(?P\d+)/delete/$', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'), - url(r'^circuit-terminations/(?P\d+)/connect/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}), + url(r'^circuit-terminations/(?P\d+)/connect/(?P[\w-]+)/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}), url(r'^circuit-terminations/(?P\d+)/trace/$', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}), ] diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index f28a35997..661ec217b 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -2522,6 +2522,9 @@ class RearPortBulkDisconnectForm(ConfirmationForm): # class ConnectCableToDeviceForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm): + """ + Base form for connecting a Cable to a Device component + """ termination_b_site = forms.ModelChoiceField( queryset=Site.objects.all(), label='Site', @@ -2567,42 +2570,89 @@ class ConnectCableToDeviceForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelFo } ) ) - termination_b_type = forms.ModelChoiceField( - queryset=ContentType.objects.all(), - label='Type', - widget=ContentTypeSelect() - ) - termination_b_id = forms.IntegerField( - label='Name', - widget=APISelect( - api_url='/api/dcim/{{termination_b_type}}s/', - disabled_indicator='cable', - conditional_query_params={ - 'termination_b_type__interface': 'type=physical', - } - ) - ) class Meta: model = Cable fields = [ - 'termination_b_site', 'termination_b_rack', 'termination_b_device', 'termination_b_type', - 'termination_b_id', 'type', 'status', 'label', 'color', 'length', 'length_unit', + 'termination_b_site', 'termination_b_rack', 'termination_b_device', 'termination_b_id', 'type', 'status', + 'label', 'color', 'length', 'length_unit', ] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # Define available types for endpoint B based on the type of endpoint A - termination_a_type = self.instance.termination_a._meta.model_name - self.fields['termination_b_type'].queryset = ContentType.objects.filter( - model__in=COMPATIBLE_TERMINATION_TYPES.get(termination_a_type) - ).exclude( - model='circuittermination' +class ConnectCableToConsolePortForm(ConnectCableToDeviceForm): + termination_b_id = forms.IntegerField( + label='Name', + widget=APISelect( + api_url='/api/dcim/console-ports/', + disabled_indicator='cable', ) + ) -class ConnectCableToCircuitForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm): +class ConnectCableToConsoleServerPortForm(ConnectCableToDeviceForm): + termination_b_id = forms.IntegerField( + label='Name', + widget=APISelect( + api_url='/api/dcim/console-server-ports/', + disabled_indicator='cable', + ) + ) + + +class ConnectCableToPowerPortForm(ConnectCableToDeviceForm): + termination_b_id = forms.IntegerField( + label='Name', + widget=APISelect( + api_url='/api/dcim/power-ports/', + disabled_indicator='cable', + ) + ) + + +class ConnectCableToPowerOutletForm(ConnectCableToDeviceForm): + termination_b_id = forms.IntegerField( + label='Name', + widget=APISelect( + api_url='/api/dcim/power-outlets/', + disabled_indicator='cable', + ) + ) + + +class ConnectCableToInterfaceForm(ConnectCableToDeviceForm): + termination_b_id = forms.IntegerField( + label='Name', + widget=APISelect( + api_url='/api/dcim/interfaces/', + disabled_indicator='cable', + additional_query_params={ + 'type': 'physical', + } + ) + ) + + +class ConnectCableToFrontPortForm(ConnectCableToDeviceForm): + termination_b_id = forms.IntegerField( + label='Name', + widget=APISelect( + api_url='/api/dcim/front-ports/', + disabled_indicator='cable', + ) + ) + + +class ConnectCableToRearPortForm(ConnectCableToDeviceForm): + termination_b_id = forms.IntegerField( + label='Name', + widget=APISelect( + api_url='/api/dcim/rear-ports/', + disabled_indicator='cable', + ) + ) + + +class ConnectCableToCircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm): termination_b_provider = forms.ModelChoiceField( queryset=Provider.objects.all(), label='Provider', @@ -2613,6 +2663,17 @@ class ConnectCableToCircuitForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelF } ) ) + termination_b_site = forms.ModelChoiceField( + queryset=Site.objects.all(), + label='Site', + required=False, + widget=APISelect( + api_url='/api/dcim/sites/', + filter_for={ + 'termination_b_circuit': 'site_id', + } + ) + ) termination_b_circuit = ChainedModelChoiceField( queryset=Circuit.objects.all(), chains=( @@ -2628,18 +2689,19 @@ class ConnectCableToCircuitForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelF ) ) termination_b_id = forms.IntegerField( - label='Termination', + label='Side', widget=APISelect( api_url='/api/circuits/circuit-terminations/', - disabled_indicator='cable' + disabled_indicator='cable', + display_field='term_side' ) ) class Meta: model = Cable fields = [ - 'termination_b_provider', 'termination_b_circuit', 'termination_b_id', 'type', 'status', 'label', 'color', - 'length', 'length_unit', + 'termination_b_provider', 'termination_b_site', 'termination_b_circuit', 'termination_b_id', 'type', + 'status', 'label', 'color', 'length', 'length_unit', ] @@ -2686,7 +2748,7 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, ChainedFieldsMixin, forms.Mode ) ) termination_b_id = forms.IntegerField( - label='Power Feed', + label='Name', widget=APISelect( api_url='/api/dcim/power-feeds/', ) diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index f6ed55ff3..82e32dad2 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -162,7 +162,7 @@ urlpatterns = [ url(r'^devices/console-ports/add/$', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'), url(r'^devices/(?P\d+)/console-ports/add/$', views.ConsolePortCreateView.as_view(), name='consoleport_add'), url(r'^devices/(?P\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), - url(r'^console-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}), + url(r'^console-ports/(?P\d+)/connect/(?P[\w-]+)/$', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}), url(r'^console-ports/(?P\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'), url(r'^console-ports/(?P\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'), url(r'^console-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}), @@ -171,7 +171,7 @@ urlpatterns = [ url(r'^devices/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'), url(r'^devices/(?P\d+)/console-server-ports/add/$', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'), url(r'^devices/(?P\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'), - url(r'^console-server-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}), + url(r'^console-server-ports/(?P\d+)/connect/(?P[\w-]+)/$', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}), url(r'^console-server-ports/(?P\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'), url(r'^console-server-ports/(?P\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'), url(r'^console-server-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}), @@ -182,7 +182,7 @@ urlpatterns = [ url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'), url(r'^devices/(?P\d+)/power-ports/add/$', views.PowerPortCreateView.as_view(), name='powerport_add'), url(r'^devices/(?P\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), - url(r'^power-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}), + url(r'^power-ports/(?P\d+)/connect/(?P[\w-]+)/$', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}), url(r'^power-ports/(?P\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'), url(r'^power-ports/(?P\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'), url(r'^power-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}), @@ -191,7 +191,7 @@ urlpatterns = [ url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'), url(r'^devices/(?P\d+)/power-outlets/add/$', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'), url(r'^devices/(?P\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), - url(r'^power-outlets/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}), + url(r'^power-outlets/(?P\d+)/connect/(?P[\w-]+)/$', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}), url(r'^power-outlets/(?P\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'), url(r'^power-outlets/(?P\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'), url(r'^power-outlets/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}), @@ -203,7 +203,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/interfaces/add/$', views.InterfaceCreateView.as_view(), name='interface_add'), url(r'^devices/(?P\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), url(r'^devices/(?P\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), - url(r'^interfaces/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}), + url(r'^interfaces/(?P\d+)/connect/(?P[\w-]+)/$', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}), url(r'^interfaces/(?P\d+)/$', views.InterfaceView.as_view(), name='interface'), url(r'^interfaces/(?P\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'), url(r'^interfaces/(?P\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'), @@ -218,7 +218,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/front-ports/add/$', views.FrontPortCreateView.as_view(), name='frontport_add'), url(r'^devices/(?P\d+)/front-ports/edit/$', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'), url(r'^devices/(?P\d+)/front-ports/delete/$', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'), - url(r'^front-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}), + url(r'^front-ports/(?P\d+)/connect/(?P[\w-]+)/$', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}), url(r'^front-ports/(?P\d+)/edit/$', views.FrontPortEditView.as_view(), name='frontport_edit'), url(r'^front-ports/(?P\d+)/delete/$', views.FrontPortDeleteView.as_view(), name='frontport_delete'), url(r'^front-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}), @@ -230,7 +230,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/rear-ports/add/$', views.RearPortCreateView.as_view(), name='rearport_add'), url(r'^devices/(?P\d+)/rear-ports/edit/$', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'), url(r'^devices/(?P\d+)/rear-ports/delete/$', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'), - url(r'^rear-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}), + url(r'^rear-ports/(?P\d+)/connect/(?P[\w-]+)/$', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}), url(r'^rear-ports/(?P\d+)/edit/$', views.RearPortEditView.as_view(), name='rearport_edit'), url(r'^rear-ports/(?P\d+)/delete/$', views.RearPortDeleteView.as_view(), name='rearport_delete'), url(r'^rear-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 37ed42dc0..b1403745d 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -15,7 +15,7 @@ from django.utils.http import is_safe_url from django.utils.safestring import mark_safe from django.views.generic import View -from circuits.models import Circuit, CircuitTermination +from circuits.models import Circuit from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE from extras.views import ObjectConfigContextView from ipam.models import Prefix, VLAN @@ -1679,27 +1679,28 @@ class CableCreateView(PermissionRequiredMixin, GetReturnURLMixin, View): permission_required = 'dcim.add_cable' template_name = 'dcim/cable_connect.html' - def _get_form_class(self): - if self.termination_b_type == 'circuit': - return forms.ConnectCableToCircuitForm - if self.termination_b_type == 'powerfeed': - return forms.ConnectCableToPowerFeedForm - return forms.ConnectCableToDeviceForm - def dispatch(self, request, *args, **kwargs): - # Retrieve endpoint A based on the given type and PK termination_a_type = kwargs.get('termination_a_type') termination_a_id = kwargs.get('termination_a_id') - self.obj = Cable( - termination_a=termination_a_type.objects.get(pk=termination_a_id) - ) - self.termination_b_type = request.GET.get('type') - if self.termination_b_type == 'circuit': - self.obj.termination_b_type = ContentType.objects.get_for_model(CircuitTermination) - elif self.termination_b_type == 'powerfeed': - self.obj.termination_b_type = ContentType.objects.get_for_model(PowerFeed) + termination_b_type_name = kwargs.get('termination_b_type') + self.termination_b_type = ContentType.objects.get(model=termination_b_type_name.replace('-', '')) + + self.obj = Cable( + termination_a=termination_a_type.objects.get(pk=termination_a_id), + termination_b_type=self.termination_b_type + ) + self.form_class = { + 'console-port': forms.ConnectCableToConsolePortForm, + 'console-server-port': forms.ConnectCableToConsoleServerPortForm, + 'power-port': forms.ConnectCableToPowerPortForm, + 'power-outlet': forms.ConnectCableToPowerOutletForm, + 'interface': forms.ConnectCableToInterfaceForm, + 'front-port': forms.ConnectCableToFrontPortForm, + 'power-feed': forms.ConnectCableToPowerFeedForm, + 'circuit-termination': forms.ConnectCableToCircuitTerminationForm, + }[termination_b_type_name] return super().dispatch(request, *args, **kwargs) @@ -1708,18 +1709,19 @@ class CableCreateView(PermissionRequiredMixin, GetReturnURLMixin, View): # Parse initial data manually to avoid setting field values as lists initial_data = {k: request.GET[k] for k in request.GET} - form = self._get_form_class()(instance=self.obj, initial=initial_data) + form = self.form_class(instance=self.obj, initial=initial_data) return render(request, self.template_name, { 'obj': self.obj, 'obj_type': Cable._meta.verbose_name, + 'termination_b_type': self.termination_b_type.name, 'form': form, 'return_url': self.get_return_url(request, self.obj), }) def post(self, request, *args, **kwargs): - form = self._get_form_class()(request.POST, request.FILES, instance=self.obj) + form = self.form_class(request.POST, request.FILES, instance=self.obj) if form.is_valid(): obj = form.save() @@ -1742,6 +1744,7 @@ class CableCreateView(PermissionRequiredMixin, GetReturnURLMixin, View): return render(request, self.template_name, { 'obj': self.obj, 'obj_type': Cable._meta.verbose_name, + 'termination_b_type': self.termination_b_type.name, 'form': form, 'return_url': self.get_return_url(request, self.obj), }) diff --git a/netbox/templates/dcim/cable_connect.html b/netbox/templates/dcim/cable_connect.html index 52c1444d4..b1609f578 100644 --- a/netbox/templates/dcim/cable_connect.html +++ b/netbox/templates/dcim/cable_connect.html @@ -22,7 +22,7 @@ {% endif %} {% with termination_a=form.instance.termination_a %} -

{% block title %}Connect {{ termination_a.device }} {{ termination_a }}{% endblock %}

+

{% block title %}Connect {{ termination_a.device }} {{ termination_a }} to {{ termination_b_type|bettertitle }}{% endblock %}

@@ -101,7 +101,16 @@ B Side
- {# TODO: Clean this up #} + {% if tabs %} + + {% endif %} + {% if 'termination_b_provider' in form.fields %} + {% render_field form.termination_b_provider %} + {% endif %} {% if 'termination_b_site' in form.fields %} {% render_field form.termination_b_site %} {% endif %} @@ -117,18 +126,18 @@ {% if 'termination_b_type' in form.fields %} {% render_field form.termination_b_type %} {% endif %} - {% if 'termination_b_provider' in form.fields %} - {% render_field form.termination_b_provider %} + {% if 'termination_b_powerpanel' in form.fields %} + {% render_field form.termination_b_powerpanel %} {% endif %} {% if 'termination_b_circuit' in form.fields %} {% render_field form.termination_b_circuit %} {% endif %} - {% if 'termination_b_powerpanel' in form.fields %} - {% render_field form.termination_b_powerpanel %} - {% endif %} - {% if 'termination_b_powerfeed' in form.fields %} - {% render_field form.termination_b_powerfeed %} - {% endif %} +
+ +
+

{{ termination_b_type|capfirst }}

+
+
{% render_field form.termination_b_id %}
diff --git a/netbox/templates/dcim/inc/consoleport.html b/netbox/templates/dcim/inc/consoleport.html index eea23db93..be531ef1b 100644 --- a/netbox/templates/dcim/inc/consoleport.html +++ b/netbox/templates/dcim/inc/consoleport.html @@ -38,9 +38,16 @@ {% if cp.cable %} {% include 'dcim/inc/cable_toggle_buttons.html' with cable=cp.cable %} {% elif perms.dcim.add_cable %} - - - + + + + {% endif %} {% if perms.dcim.change_consoleport %} diff --git a/netbox/templates/dcim/inc/consoleserverport.html b/netbox/templates/dcim/inc/consoleserverport.html index 2ce7818b6..d1dce00d3 100644 --- a/netbox/templates/dcim/inc/consoleserverport.html +++ b/netbox/templates/dcim/inc/consoleserverport.html @@ -47,9 +47,16 @@ {% if csp.cable %} {% include 'dcim/inc/cable_toggle_buttons.html' with cable=csp.cable %} {% elif perms.dcim.add_cable %} - - - + + + + {% endif %} {% if perms.dcim.change_consoleserverport %} diff --git a/netbox/templates/dcim/inc/frontport.html b/netbox/templates/dcim/inc/frontport.html index 2b468725d..ae86a36b4 100644 --- a/netbox/templates/dcim/inc/frontport.html +++ b/netbox/templates/dcim/inc/frontport.html @@ -58,9 +58,17 @@ {% if frontport.cable %} {% include 'dcim/inc/cable_toggle_buttons.html' with cable=frontport.cable %} {% elif perms.dcim.add_cable %} - - - + + + + {% endif %} {% if perms.dcim.change_frontport %} diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html index 584d1754d..4aa4e377d 100644 --- a/netbox/templates/dcim/inc/interface.html +++ b/netbox/templates/dcim/inc/interface.html @@ -151,9 +151,17 @@ {% if iface.cable %} {% include 'dcim/inc/cable_toggle_buttons.html' with cable=iface.cable %} {% elif iface.is_connectable and perms.dcim.add_cable %} - - - + + + + {% endif %} diff --git a/netbox/templates/dcim/inc/poweroutlet.html b/netbox/templates/dcim/inc/poweroutlet.html index 74f946495..042c9142a 100644 --- a/netbox/templates/dcim/inc/poweroutlet.html +++ b/netbox/templates/dcim/inc/poweroutlet.html @@ -47,7 +47,7 @@ {% if po.cable %} {% include 'dcim/inc/cable_toggle_buttons.html' with cable=po.cable %} {% elif perms.dcim.add_cable %} - + {% endif %} diff --git a/netbox/templates/dcim/inc/powerport.html b/netbox/templates/dcim/inc/powerport.html index c3a83ad69..e74e4f368 100644 --- a/netbox/templates/dcim/inc/powerport.html +++ b/netbox/templates/dcim/inc/powerport.html @@ -47,8 +47,8 @@ {% endif %} diff --git a/netbox/templates/dcim/inc/rearport.html b/netbox/templates/dcim/inc/rearport.html index 63aae1127..27609e726 100644 --- a/netbox/templates/dcim/inc/rearport.html +++ b/netbox/templates/dcim/inc/rearport.html @@ -57,9 +57,17 @@ {% if rearport.cable %} {% include 'dcim/inc/cable_toggle_buttons.html' with cable=rearport.cable %} {% elif perms.dcim.add_cable %} - - - + + + + {% endif %} {% if perms.dcim.change_rearport %}