From 79bebf7c9b9043a31ab32d0e2d2e39f5c7cb33fa Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 3 Jan 2022 11:18:46 -0500 Subject: [PATCH 01/20] PRVB --- docs/release-notes/version-3.1.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 670cc4cce..29213a4c5 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -1,5 +1,9 @@ # NetBox v3.1 +## v3.1.5 (FUTURE) + +--- + ## v3.1.4 (2022-01-03) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index c22443275..ef0470038 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -19,7 +19,7 @@ from netbox.config import PARAMS # Environment setup # -VERSION = '3.1.4' +VERSION = '3.1.5-dev' # Hostname HOSTNAME = platform.node() From e11e8a5d6436f770e8157bdbd20123e2878a5c7b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 4 Jan 2022 09:15:25 -0500 Subject: [PATCH 02/20] Fixes #8213: Fix ValueError exception under prefix IP addresses view --- docs/release-notes/version-3.1.md | 4 ++++ netbox/ipam/views.py | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 29213a4c5..b523ab8c7 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -2,6 +2,10 @@ ## v3.1.5 (FUTURE) +### Bug Fixes + +* [#8213](https://github.com/netbox-community/netbox/issues/8213) - Fix ValueError exception under prefix IP addresses view + --- ## v3.1.4 (2022-01-03) diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 55ac284d1..c79a58dd6 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -505,9 +505,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView): template_name = 'ipam/prefix/ip_addresses.html' def get_children(self, request, parent): - return parent.get_child_ips().restrict(request.user, 'view').prefetch_related( - 'vrf', 'role', 'tenant', - ) + return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant') def prep_table_data(self, request, queryset, parent): show_available = bool(request.GET.get('show_available', 'true') == 'true') From 2fe02ddb1f88a93cba82b04bbd3c2caa0425e5b6 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 4 Jan 2022 09:32:41 -0500 Subject: [PATCH 03/20] Add tests for IPAM object children views --- netbox/ipam/tests/test_views.py | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index 022ea13c3..4f0f9a214 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -1,5 +1,7 @@ import datetime +from django.test import override_settings +from django.urls import reverse from netaddr import IPNetwork from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site @@ -222,6 +224,21 @@ class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'description': 'New description', } + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_aggregate_prefixes(self): + rir = RIR.objects.first() + aggregate = Aggregate.objects.create(prefix=IPNetwork('192.168.0.0/16'), rir=rir) + prefixes = ( + Prefix(prefix=IPNetwork('192.168.1.0/24')), + Prefix(prefix=IPNetwork('192.168.2.0/24')), + Prefix(prefix=IPNetwork('192.168.3.0/24')), + ) + Prefix.objects.bulk_create(prefixes) + self.assertEqual(aggregate.get_child_prefixes().count(), 3) + + url = reverse('ipam:aggregate_prefixes', kwargs={'pk': aggregate.pk}) + self.assertHttpStatus(self.client.get(url), 200) + class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase): model = Role @@ -319,6 +336,48 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'description': 'New description', } + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_prefix_prefixes(self): + prefixes = ( + Prefix(prefix=IPNetwork('192.168.0.0/16')), + Prefix(prefix=IPNetwork('192.168.1.0/24')), + Prefix(prefix=IPNetwork('192.168.2.0/24')), + Prefix(prefix=IPNetwork('192.168.3.0/24')), + ) + Prefix.objects.bulk_create(prefixes) + self.assertEqual(prefixes[0].get_child_prefixes().count(), 3) + + url = reverse('ipam:prefix_prefixes', kwargs={'pk': prefixes[0].pk}) + self.assertHttpStatus(self.client.get(url), 200) + + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_prefix_ipranges(self): + prefix = Prefix.objects.create(prefix=IPNetwork('192.168.0.0/16')) + ip_ranges = ( + IPRange(start_address='192.168.0.1/24', end_address='192.168.0.100/24', size=99), + IPRange(start_address='192.168.1.1/24', end_address='192.168.1.100/24', size=99), + IPRange(start_address='192.168.2.1/24', end_address='192.168.2.100/24', size=99), + ) + IPRange.objects.bulk_create(ip_ranges) + self.assertEqual(prefix.get_child_ranges().count(), 3) + + url = reverse('ipam:prefix_ipranges', kwargs={'pk': prefix.pk}) + self.assertHttpStatus(self.client.get(url), 200) + + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_prefix_ipaddresses(self): + prefix = Prefix.objects.create(prefix=IPNetwork('192.168.0.0/16')) + ip_addresses = ( + IPAddress(address=IPNetwork('192.168.0.1/16')), + IPAddress(address=IPNetwork('192.168.0.2/16')), + IPAddress(address=IPNetwork('192.168.0.3/16')), + ) + IPAddress.objects.bulk_create(ip_addresses) + self.assertEqual(prefix.get_child_ips().count(), 3) + + url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk}) + self.assertHttpStatus(self.client.get(url), 200) + class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = IPRange @@ -377,6 +436,24 @@ class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'description': 'New description', } + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_iprange_ipaddresses(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('192.168.0.1/24'), + end_address=IPNetwork('192.168.0.100/24'), + size=99 + ) + ip_addresses = ( + IPAddress(address=IPNetwork('192.168.0.1/24')), + IPAddress(address=IPNetwork('192.168.0.2/24')), + IPAddress(address=IPNetwork('192.168.0.3/24')), + ) + IPAddress.objects.bulk_create(ip_addresses) + self.assertEqual(iprange.get_child_ips().count(), 3) + + url = reverse('ipam:iprange_ipaddresses', kwargs={'pk': iprange.pk}) + self.assertHttpStatus(self.client.get(url), 200) + class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = IPAddress From 8c8774cd2fd5e826c6787b415af2136429b4eecb Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 4 Jan 2022 13:24:15 -0500 Subject: [PATCH 04/20] Fixes #8226: Honor return URL after populating a device bay --- docs/release-notes/version-3.1.md | 1 + netbox/dcim/views.py | 3 ++- netbox/templates/dcim/devicebay_populate.html | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index b523ab8c7..963aaad23 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -5,6 +5,7 @@ ### Bug Fixes * [#8213](https://github.com/netbox-community/netbox/issues/8213) - Fix ValueError exception under prefix IP addresses view +* [#8226](https://github.com/netbox-community/netbox/issues/8226) - Honor return URL after populating a device bay --- diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 7048ae63e..cee516f5c 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -2035,8 +2035,9 @@ class DeviceBayPopulateView(generic.ObjectEditView): device_bay.installed_device = form.cleaned_data['installed_device'] device_bay.save() messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay)) + return_url = self.get_return_url(request) - return redirect('dcim:device', pk=device_bay.device.pk) + return redirect(return_url) return render(request, 'dcim/devicebay_populate.html', { 'device_bay': device_bay, diff --git a/netbox/templates/dcim/devicebay_populate.html b/netbox/templates/dcim/devicebay_populate.html index d0f47921a..237227277 100644 --- a/netbox/templates/dcim/devicebay_populate.html +++ b/netbox/templates/dcim/devicebay_populate.html @@ -4,7 +4,7 @@ {% render_errors form %} {% block content %} -
+ {% csrf_token %}
From ea961ba8f219e9a52b8a10c6a832faed1ed323c3 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 4 Jan 2022 13:49:07 -0500 Subject: [PATCH 05/20] Fixes #8224: Fix KeyError exception when creating FHRP group with IP address and protocol "other" --- docs/release-notes/version-3.1.md | 1 + netbox/ipam/constants.py | 1 + netbox/ipam/forms/models.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 963aaad23..4e8446b41 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -5,6 +5,7 @@ ### Bug Fixes * [#8213](https://github.com/netbox-community/netbox/issues/8213) - Fix ValueError exception under prefix IP addresses view +* [#8224](https://github.com/netbox-community/netbox/issues/8224) - Fix KeyError exception when creating FHRP group with IP address and protocol "other" * [#8226](https://github.com/netbox-community/netbox/issues/8226) - Honor return URL after populating a device bay --- diff --git a/netbox/ipam/constants.py b/netbox/ipam/constants.py index b19d4061b..ab88dfc1a 100644 --- a/netbox/ipam/constants.py +++ b/netbox/ipam/constants.py @@ -65,6 +65,7 @@ FHRP_PROTOCOL_ROLE_MAPPINGS = { FHRPGroupProtocolChoices.PROTOCOL_HSRP: IPAddressRoleChoices.ROLE_HSRP, FHRPGroupProtocolChoices.PROTOCOL_GLBP: IPAddressRoleChoices.ROLE_GLBP, FHRPGroupProtocolChoices.PROTOCOL_CARP: IPAddressRoleChoices.ROLE_CARP, + FHRPGroupProtocolChoices.PROTOCOL_OTHER: IPAddressRoleChoices.ROLE_VIP, } diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index c5e3146e9..0f85a95b1 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -580,7 +580,7 @@ class FHRPGroupForm(CustomFieldModelForm): vrf=self.cleaned_data['ip_vrf'], address=self.cleaned_data['ip_address'], status=self.cleaned_data['ip_status'], - role=FHRP_PROTOCOL_ROLE_MAPPINGS[self.cleaned_data['protocol']], + role=FHRP_PROTOCOL_ROLE_MAPPINGS.get(self.cleaned_data['protocol'], IPAddressRoleChoices.ROLE_VIP), assigned_object=instance ) ipaddress.save() From 662cafe416b7dd0ed2a23735453131a3361d931d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 4 Jan 2022 15:01:16 -0500 Subject: [PATCH 06/20] Form widgets & style cleanup --- netbox/extras/forms/models.py | 18 ++++++++++++++---- netbox/ipam/forms/models.py | 3 +-- netbox/project-static/dist/netbox-light.css | Bin 493807 -> 493807 bytes netbox/project-static/dist/netbox-print.css | Bin 1624275 -> 1624275 bytes netbox/project-static/styles/theme-light.scss | 2 -- netbox/utilities/forms/fields.py | 4 ++-- netbox/utilities/forms/widgets.py | 10 ---------- .../templates/widgets/select_contenttype.html | 1 - 8 files changed, 17 insertions(+), 21 deletions(-) delete mode 100644 netbox/utilities/templates/widgets/select_contenttype.html diff --git a/netbox/extras/forms/models.py b/netbox/extras/forms/models.py index 1e619ebec..89ab7aa19 100644 --- a/netbox/extras/forms/models.py +++ b/netbox/extras/forms/models.py @@ -7,8 +7,8 @@ from extras.models import * from extras.utils import FeatureQuery from tenancy.models import Tenant, TenantGroup from utilities.forms import ( - add_blank_choice, BootstrapMixin, CommentField, ContentTypeChoiceField, - ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect, + add_blank_choice, BootstrapMixin, CommentField, ContentTypeChoiceField, ContentTypeMultipleChoiceField, + DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect, ) from virtualization.models import Cluster, ClusterGroup @@ -41,6 +41,10 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm): ('Values', ('default', 'choices')), ('Validation', ('validation_minimum', 'validation_maximum', 'validation_regex')), ) + widgets = { + 'type': StaticSelect(), + 'filter_logic': StaticSelect(), + } class CustomLinkForm(BootstrapMixin, forms.ModelForm): @@ -57,6 +61,7 @@ class CustomLinkForm(BootstrapMixin, forms.ModelForm): ('Templates', ('link_text', 'link_url')), ) widgets = { + 'button_class': StaticSelect(), 'link_text': forms.Textarea(attrs={'class': 'font-monospace'}), 'link_url': forms.Textarea(attrs={'class': 'font-monospace'}), } @@ -96,8 +101,7 @@ class WebhookForm(BootstrapMixin, forms.ModelForm): model = Webhook fields = '__all__' fieldsets = ( - ('Webhook', ('name', 'enabled')), - ('Assigned Models', ('content_types',)), + ('Webhook', ('name', 'content_types', 'enabled')), ('Events', ('type_create', 'type_update', 'type_delete')), ('HTTP Request', ( 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template', 'secret', @@ -105,7 +109,13 @@ class WebhookForm(BootstrapMixin, forms.ModelForm): ('Conditions', ('conditions',)), ('SSL', ('ssl_verification', 'ca_file_path')), ) + labels = { + 'type_create': 'Creations', + 'type_update': 'Updates', + 'type_delete': 'Deletions', + } widgets = { + 'http_method': StaticSelect(), 'additional_headers': forms.Textarea(attrs={'class': 'font-monospace'}), 'body_template': forms.Textarea(attrs={'class': 'font-monospace'}), } diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index 0f85a95b1..4ed8aa267 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -628,8 +628,7 @@ class FHRPGroupAssignmentForm(BootstrapMixin, forms.ModelForm): class VLANGroupForm(CustomFieldModelForm): scope_type = ContentTypeChoiceField( queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES), - required=False, - widget=StaticSelect + required=False ) region = DynamicModelChoiceField( queryset=Region.objects.all(), diff --git a/netbox/project-static/dist/netbox-light.css b/netbox/project-static/dist/netbox-light.css index 23dc8d3821e58e7264fe4bd3109dea298dc227bf..215f986bb1045d51eac763de5aac1e0e23d59cad 100644 GIT binary patch delta 216 zcmaDqQSSXjxeaCU(-l^;vNzYpx7Wrq0x=U1GXpWp_S$$>{a}zR+jPZQtg_qBZ(`la z0pV}h#j3D<-ELM9Sunr-#0}Q%CvLFuWlf*2#>75d-kn`zngt{8IO;>eh z&j6{EoBm-3Gu!ly&g`=%C){V7yx|#~ezHT?Gh%8j3{lpE{?I&)q@nubyRbXPDF7M8+G0lRJck-Pu-pLc5vrSiZ zX3v1Covb)ZY`VY^W{&9_o!MtkE_li|IpG=4Ous$nGgAqiZrAn19hVSGEoqP#9W4+Fn1+^l>(*E>Z1V|Sfv3G&(rzdza(bU2voPJ zE{=BSZvL++nr44FRi`M7EYu@Xb#^cYMpjW^q3)qjSmZKHncRZh4-132s6`K}eAdYY z6==OP6>2JvLiN;Z!1uUhkkY!Hw2N^5!M4Dhp;2f~>qN|avK5Kg6L#8Ohi02a;P#Se z;_{0|toA-0$X(|GtRJEO_bSOHlQC2s9MFBwjWCbny#O CaFav; delta 438 zcmZ9`JxD@P7zSX}+qrtZH?JP;9PEcef()XX`f;#=gixp^8wE4kifC$TiN*?vcZ)kU zH53$QNt%o3hn5HeA)F#;h=zWw&!~uoXZXGsUf!#X{MAN&dEYgZWQBo3zA&?zO1g-f zc!);o2qQZ25+5;$pP0lV^&~)S5+os&O7b1?U5d#Z=O{Px09vPbK*h6stnnvno90nC z^W0Qh3w$w)5gu$`?K|NlOd_N~^__^7Zk)H;41*Ojj9gl3NZ2xhi4PHmo%XkEI&!o) z5!6wB76x_=*#c+JW8uBmWg(Z<84g~=EY@$#COF*ks^M2L>B8K#o8h7=qBt!vzbd_n z`>$tQT~@`D_M4Am_sA1MPm!6bUz5ig)~?;YVl2l0jLB7njU_``*w$nx&JF2Bmm!;r wJs}30$#^YnuU)jZ180Wx)$-fgJ{^gQ`L8U3hdP;oconsole server port - This attribute can be used to reference the relevant API endpoint for a particular ContentType. - """ - option_template_name = 'widgets/select_contenttype.html' - - class SelectSpeedWidget(forms.NumberInput): """ Speed field with dropdown selections for convenience. diff --git a/netbox/utilities/templates/widgets/select_contenttype.html b/netbox/utilities/templates/widgets/select_contenttype.html deleted file mode 100644 index 04c42c371..000000000 --- a/netbox/utilities/templates/widgets/select_contenttype.html +++ /dev/null @@ -1 +0,0 @@ - From 0a22b3990fe93c27f19dfe1797de7ce07ffd109f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 4 Jan 2022 20:42:44 -0500 Subject: [PATCH 07/20] #7450: Clean up footer and navbar styles --- netbox/project-static/dist/netbox-dark.css | Bin 789153 -> 789142 bytes netbox/project-static/dist/netbox-light.css | Bin 493807 -> 493753 bytes netbox/project-static/dist/netbox-print.css | Bin 1624275 -> 1624333 bytes netbox/project-static/styles/netbox.scss | 18 +++--- netbox/project-static/styles/theme-dark.scss | 2 +- netbox/project-static/styles/theme-light.scss | 2 + netbox/templates/base/layout.html | 2 +- netbox/templates/extras/report.html | 55 +++++++++++------- netbox/templates/extras/report_result.html | 2 +- 9 files changed, 46 insertions(+), 35 deletions(-) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index e711685bf40042d2b0918218882f0acba69a0d49..4cb2f191d5dcdfc2729ee80dd254d9d59853a8bd 100644 GIT binary patch delta 296 zcmZ2D)nM9GgN7Ey7N!>F7M3lnk?qr2vKh6eCzP|YPY-Bg<(ux%!OA<`wwKiqiQCc3 z%D4T0J8J~vboDew(dqxUazeE%=w{`gzNVA4gU!&~Fv&1w`u%>^>C=ItLX6eZ|7I~7 zLQDnxVl$&Sz)kWNr)8*%I zvu>|n#QlVKI?Dr2q3H@`%2E)==}cGH#Vxk|$!=~=x9J)nMUNgN7Ey7N!>F7M3lnk?qr8hOuf*Pbg<)pB~W0$~WDigOzu>Z7-`K61St5 zm2dn1cGd{S=@G4rqSOCx<%DWk(9Oy}eN8882U}uFl4(-P^!xp+)29PPg{D6YV>N=9 z3*?Hl8+5Y*F&hxG12G2>a{@8fc7ty24`S0Bd^x$M^Yw9ZOx|R#Jbl3mZl39<7I8~% z&tJs-l6QOI2JUo@=@kX6BHPV&a<5`yOUp^Euri!{aH+`jx1ZQ_rccHnE{xVEol;t^z=zLuRwXu5nEGyiloQFcLgWfMzN15@+q4wE$1F5mGl^Lc!tzdH7MNZc4dhzIGxJWDh-T((e;CIM z#4JF}y8U4so7Us$4$bWBjJ4AXoY+;jJ3F!Sb4_=NWfPv9u$ygrkPrJs7RKu7AM#lw zrmx<_%DG)Oko`B~^he)VB-pLfa#Aa-45u5MXO-J-6U@F`fBMFBHc`g3=~3>?Qqw;- Nu^-P& delta 215 zcmdlvQSSXjxrP?T7N#xCYoj>~z`!DTdSf)R%J%Eg%p8meu>vb*(e1xtm|c;?7^9iR z+hgLGftUq|S+~c;v8{YO`GG&%^y*WroGeM{nzhs8o!C{k8#%G_b8V0EVZX?-Jvfm4 zE93UWVD{7c(^K4;1*XR|vmc*6r;S}`dcGVJ|MU|ZSR{mN9dc7sG7}XFiZb&`s`W|| nlXRy$1hY%R4QB=#KE0lkRf5$jFF!AJdPNJn;&#gx_68OJIbTLo diff --git a/netbox/project-static/dist/netbox-print.css b/netbox/project-static/dist/netbox-print.css index 2d702b89a02932ba5f1efb8d489493e8cb39eaab..f292f7bd8cf58bac5bf3d25763017d89b222f6d3 100644 GIT binary patch delta 299 zcmcaSF}ZhAazhJa3sVbo3rh=Y3tJ0&3r7oQ3)dFzm?zV}d$IFQZ-2-wy1n5E_ZF7v ztby#J+r3|MM>9>&a^~lqe*HDKbo+(Z+(66&#JoVv2gLk9EC9rUKr95r!aytn#G*hf zw*A6uaewaV4o4-}r{A6^A~HR}TY_b})(`Oo+h2YcHpe6Z?{YEO<%WNf^E9OGH$S;T+`1jz!(Es*jGWz|*1*>QrlkDLF9}I=x4a)?-Vf#"); $navbar-light-toggler-border-color: $gray-700; diff --git a/netbox/project-static/styles/theme-light.scss b/netbox/project-static/styles/theme-light.scss index 22cc48108..0ca85319b 100644 --- a/netbox/project-static/styles/theme-light.scss +++ b/netbox/project-static/styles/theme-light.scss @@ -22,6 +22,8 @@ $theme-colors: map-merge($theme-colors, $theme-color-addons); $light: $gray-200; +$navbar-light-color: $gray-100; + $card-cap-color: $gray-800; $accordion-bg: transparent; diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index 7b1597bf0..1959ef38d 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -20,7 +20,7 @@
{# Top bar #} -
- {% table_config_form table %} {% endblock %} + +{% block modals %} + {% table_config_form table %} +{% endblock modals %} diff --git a/netbox/templates/ipam/iprange/ip_addresses.html b/netbox/templates/ipam/iprange/ip_addresses.html index 210cff812..8663c158f 100644 --- a/netbox/templates/ipam/iprange/ip_addresses.html +++ b/netbox/templates/ipam/iprange/ip_addresses.html @@ -35,5 +35,8 @@ - {% table_config_form table %} {% endblock %} + +{% block modals %} + {% table_config_form table %} +{% endblock modals %} diff --git a/netbox/templates/ipam/prefix/ip_addresses.html b/netbox/templates/ipam/prefix/ip_addresses.html index e2f77756c..ae5d3cf74 100644 --- a/netbox/templates/ipam/prefix/ip_addresses.html +++ b/netbox/templates/ipam/prefix/ip_addresses.html @@ -35,5 +35,8 @@ - {% table_config_form table %} {% endblock %} + +{% block modals %} + {% table_config_form table %} +{% endblock modals %} diff --git a/netbox/templates/ipam/prefix/ip_ranges.html b/netbox/templates/ipam/prefix/ip_ranges.html index af80578a0..3d5e0c4c0 100644 --- a/netbox/templates/ipam/prefix/ip_ranges.html +++ b/netbox/templates/ipam/prefix/ip_ranges.html @@ -35,5 +35,8 @@ - {% table_config_form table %} {% endblock %} + +{% block modals %} + {% table_config_form table %} +{% endblock modals %} diff --git a/netbox/templates/ipam/prefix/prefixes.html b/netbox/templates/ipam/prefix/prefixes.html index 18fcbb569..21756a36a 100644 --- a/netbox/templates/ipam/prefix/prefixes.html +++ b/netbox/templates/ipam/prefix/prefixes.html @@ -37,5 +37,8 @@ - {% table_config_form table %} {% endblock %} + +{% block modals %} + {% table_config_form table %} +{% endblock modals %} diff --git a/netbox/templates/ipam/vlan/interfaces.html b/netbox/templates/ipam/vlan/interfaces.html index acba983aa..3ce00631f 100644 --- a/netbox/templates/ipam/vlan/interfaces.html +++ b/netbox/templates/ipam/vlan/interfaces.html @@ -5,13 +5,14 @@
{% csrf_token %} {% include 'inc/table_controls_htmx.html' with table_modal="VLANDevicesTable_config" %} -
{% include 'htmx/table.html' %}
-
+{% endblock content %} + +{% block modals %} {% table_config_form table %} -{% endblock %} +{% endblock modals %} diff --git a/netbox/templates/ipam/vlan/vminterfaces.html b/netbox/templates/ipam/vlan/vminterfaces.html index aff559393..fcd207894 100644 --- a/netbox/templates/ipam/vlan/vminterfaces.html +++ b/netbox/templates/ipam/vlan/vminterfaces.html @@ -5,13 +5,14 @@
{% csrf_token %} {% include 'inc/table_controls_htmx.html' with table_modal="VLANVirtualMachinesTable_config" %} -
{% include 'htmx/table.html' %}
-
+{% endblock content %} + +{% block modals %} {% table_config_form table %} -{% endblock %} +{% endblock modals %} diff --git a/netbox/templates/virtualization/cluster/devices.html b/netbox/templates/virtualization/cluster/devices.html index ab774a29c..700006196 100644 --- a/netbox/templates/virtualization/cluster/devices.html +++ b/netbox/templates/virtualization/cluster/devices.html @@ -6,13 +6,11 @@
{% csrf_token %} {% include 'inc/table_controls_htmx.html' with table_modal="DeviceTable_config" %} -
{% include 'htmx/table.html' %}
-
{% if perms.virtualization.change_cluster %} @@ -23,5 +21,8 @@
+{% endblock content %} + +{% block modals %} {% table_config_form table %} -{% endblock %} +{% endblock modals %} diff --git a/netbox/templates/virtualization/cluster/virtual_machines.html b/netbox/templates/virtualization/cluster/virtual_machines.html index 7681e3413..5b0359e07 100644 --- a/netbox/templates/virtualization/cluster/virtual_machines.html +++ b/netbox/templates/virtualization/cluster/virtual_machines.html @@ -6,13 +6,11 @@
{% csrf_token %} {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineTable_config" %} -
{% include 'htmx/table.html' %}
-
{% if perms.virtualization.change_virtualmachine %} @@ -28,5 +26,8 @@
+{% endblock content %} + +{% block modals %} {% table_config_form table %} -{% endblock %} +{% endblock modals %} diff --git a/netbox/templates/virtualization/virtualmachine/interfaces.html b/netbox/templates/virtualization/virtualmachine/interfaces.html index 6b3e70c7f..5f6ab52ad 100644 --- a/netbox/templates/virtualization/virtualmachine/interfaces.html +++ b/netbox/templates/virtualization/virtualmachine/interfaces.html @@ -37,5 +37,8 @@
+{% endblock content %} + +{% block modals %} {% table_config_form table %} -{% endblock %} +{% endblock modals %} From 511aedd5dba206a781897d70751c32df7571fcdf Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 5 Jan 2022 11:39:58 -0500 Subject: [PATCH 11/20] Omit table configuration form from rack elevations view --- netbox/templates/dcim/rack_elevation_list.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/templates/dcim/rack_elevation_list.html b/netbox/templates/dcim/rack_elevation_list.html index 312b543a6..87a047900 100644 --- a/netbox/templates/dcim/rack_elevation_list.html +++ b/netbox/templates/dcim/rack_elevation_list.html @@ -73,3 +73,5 @@ {% endblock content-wrapper %} + +{% block modals %}{% endblock %} From 443b4ccc573f07f582f0c9ca8485f15d3517c71d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 5 Jan 2022 11:23:11 -0500 Subject: [PATCH 12/20] Initial work on #8231 --- netbox/ipam/views.py | 1 - netbox/netbox/views/generic.py | 20 +++++++++++++++---- netbox/templates/generic/object.html | 6 +++++- netbox/templates/generic/object_delete.html | 19 ++++++++++++------ netbox/templates/htmx/delete_form.html | 20 +++++++++++++++++++ netbox/templates/inc/htmx_modal.html | 7 +++++++ netbox/templates/ipam/prefix_delete.html | 5 ----- .../utilities/templates/buttons/delete.html | 10 ++++++++-- 8 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 netbox/templates/htmx/delete_form.html create mode 100644 netbox/templates/inc/htmx_modal.html delete mode 100644 netbox/templates/ipam/prefix_delete.html diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index c79a58dd6..38b30e9cc 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -529,7 +529,6 @@ class PrefixEditView(generic.ObjectEditView): class PrefixDeleteView(generic.ObjectDeleteView): queryset = Prefix.objects.all() - template_name = 'ipam/prefix_delete.html' class PrefixBulkImportView(generic.BulkImportView): diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index feff2ca39..74f8f325b 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -10,6 +10,7 @@ from django.db.models import ManyToManyField, ProtectedError from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput, Textarea from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse from django.utils.html import escape from django.utils.http import is_safe_url from django.utils.safestring import mark_safe @@ -430,10 +431,21 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): obj = self.get_object(kwargs) form = ConfirmationForm(initial=request.GET) + # If this is an HTMX request, return only the rendered deletion form as modal content + if is_htmx(request): + viewname = f'{self.queryset.model._meta.app_label}:{self.queryset.model._meta.model_name}_delete' + form_url = reverse(viewname, kwargs={'pk': obj.pk}) + return render(request, 'htmx/delete_form.html', { + 'object': obj, + 'object_type': self.queryset.model._meta.verbose_name, + 'form': form, + 'form_url': form_url, + }) + return render(request, self.template_name, { - 'obj': obj, + 'object': obj, + 'object_type': self.queryset.model._meta.verbose_name, 'form': form, - 'obj_type': self.queryset.model._meta.verbose_name, 'return_url': self.get_return_url(request, obj), }) @@ -466,9 +478,9 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): logger.debug("Form validation failed") return render(request, self.template_name, { - 'obj': obj, + 'object': obj, + 'object_type': self.queryset.model._meta.verbose_name, 'form': form, - 'obj_type': self.queryset.model._meta.verbose_name, 'return_url': self.get_return_url(request, obj), }) diff --git a/netbox/templates/generic/object.html b/netbox/templates/generic/object.html index 40c0e09ce..4d616f944 100644 --- a/netbox/templates/generic/object.html +++ b/netbox/templates/generic/object.html @@ -100,4 +100,8 @@
{% block content %}{% endblock %}
-{% endblock %} +{% endblock content-wrapper %} + +{% block modals %} + {% include 'inc/htmx_modal.html' %} +{% endblock modals %} diff --git a/netbox/templates/generic/object_delete.html b/netbox/templates/generic/object_delete.html index 85cedd29c..d0603ace0 100644 --- a/netbox/templates/generic/object_delete.html +++ b/netbox/templates/generic/object_delete.html @@ -1,9 +1,16 @@ -{% extends 'generic/confirmation_form.html' %} +{% extends 'base/layout.html' %} {% load form_helpers %} -{% block title %}Delete {{ obj_type }}?{% endblock %} +{% block title %}Delete {{ object_type }}?{% endblock %} -{% block message %} -

Are you sure you want to delete {{ obj_type }} {{ obj }}?

- {% block message_extra %}{% endblock %} -{% endblock message %} +{% block header %}{% endblock %} + +{% block content %} + +{% endblock %} diff --git a/netbox/templates/htmx/delete_form.html b/netbox/templates/htmx/delete_form.html new file mode 100644 index 000000000..fc1cbe0a0 --- /dev/null +++ b/netbox/templates/htmx/delete_form.html @@ -0,0 +1,20 @@ +{% load form_helpers %} + +
+ {% csrf_token %} + + + +
diff --git a/netbox/templates/inc/htmx_modal.html b/netbox/templates/inc/htmx_modal.html new file mode 100644 index 000000000..771f5d595 --- /dev/null +++ b/netbox/templates/inc/htmx_modal.html @@ -0,0 +1,7 @@ + diff --git a/netbox/templates/ipam/prefix_delete.html b/netbox/templates/ipam/prefix_delete.html deleted file mode 100644 index eb7a22d3c..000000000 --- a/netbox/templates/ipam/prefix_delete.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends 'generic/object_delete.html' %} - -{% block message_extra %} -

Note: This will not delete any child prefixes or IP addresses.

-{% endblock %} diff --git a/netbox/utilities/templates/buttons/delete.html b/netbox/utilities/templates/buttons/delete.html index 6fe3fe7d8..a027edeec 100644 --- a/netbox/utilities/templates/buttons/delete.html +++ b/netbox/utilities/templates/buttons/delete.html @@ -1,3 +1,9 @@ - -  Delete + +  Delete From ccda73494f5f4b104f280865b5c99eb43014cb0e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 5 Jan 2022 14:57:56 -0500 Subject: [PATCH 13/20] Center modal dialog vertically --- netbox/templates/inc/htmx_modal.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/inc/htmx_modal.html b/netbox/templates/inc/htmx_modal.html index 771f5d595..d15e5b799 100644 --- a/netbox/templates/inc/htmx_modal.html +++ b/netbox/templates/inc/htmx_modal.html @@ -1,5 +1,5 @@