From e7bd0e53d78809f430c8dafaacded4a52d6c7438 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 16 Oct 2024 11:56:55 -0400 Subject: [PATCH 01/18] Closes #17776: Add support for different HTTP methods to HTMXSelect --- netbox/utilities/forms/widgets/select.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/netbox/utilities/forms/widgets/select.py b/netbox/utilities/forms/widgets/select.py index 9108951b7..8115e2449 100644 --- a/netbox/utilities/forms/widgets/select.py +++ b/netbox/utilities/forms/widgets/select.py @@ -43,9 +43,12 @@ class HTMXSelect(forms.Select): """ Selection widget that will re-generate the HTML form upon the selection of a new option. """ - def __init__(self, hx_url='.', hx_target_id='form_fields', attrs=None, **kwargs): + def __init__(self, method='get', hx_url='.', hx_target_id='form_fields', attrs=None, **kwargs): + method = method.lower() + if method not in ('delete', 'get', 'patch', 'post', 'put'): + raise ValueError(f"Unsupported HTTP method: {method}") _attrs = { - 'hx-get': hx_url, + f'hx-{method}': hx_url, 'hx-include': f'#{hx_target_id}', 'hx-target': f'#{hx_target_id}', } From 35307d213f98a254442a843726e0610c13db5e3f Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 16 Oct 2024 09:00:51 -0700 Subject: [PATCH 02/18] 17468 add warning to documentation about overriding custom script properties --- docs/customization/custom-scripts.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/customization/custom-scripts.md b/docs/customization/custom-scripts.md index 3fa6491d2..1051b31f6 100644 --- a/docs/customization/custom-scripts.md +++ b/docs/customization/custom-scripts.md @@ -72,6 +72,9 @@ script_order = (MyCustomScript, AnotherCustomScript) Script attributes are defined under a class named `Meta` within the script. These are optional, but encouraged. +!!! warning + These are also defined and used as properties on the base custom script class, so don't use the same names as variables or override them in your custom script. + ### `name` This is the human-friendly names of your script. If omitted, the class name will be used. From aa3f4cb5f5ba18f2876db92976b76acaa9582502 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Tue, 15 Oct 2024 16:01:12 -0700 Subject: [PATCH 03/18] 17710 remove cached fields from CableTermination GraphQL --- netbox/dcim/graphql/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 24ba5cca4..8adfed82e 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -112,7 +112,7 @@ class ModularComponentTemplateType(ComponentTemplateType): @strawberry_django.type( models.CableTermination, - exclude=('termination_type', 'termination_id'), + exclude=('termination_type', 'termination_id', '_device', '_rack', '_location', '_site'), filters=CableTerminationFilter ) class CableTerminationType(NetBoxObjectType): From e8e95f5e97a684fd26d04289f86e3dbce8ac29cc Mon Sep 17 00:00:00 2001 From: Artem Kotik Date: Wed, 16 Oct 2024 11:39:23 +0200 Subject: [PATCH 04/18] Add job timeout handling in JobRunner for periodic jobs --- netbox/netbox/jobs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/netbox/jobs.py b/netbox/netbox/jobs.py index 087c24896..ae8f2f109 100644 --- a/netbox/netbox/jobs.py +++ b/netbox/netbox/jobs.py @@ -68,6 +68,8 @@ class JobRunner(ABC): finally: if job.interval: new_scheduled_time = (job.scheduled or job.started) + timedelta(minutes=job.interval) + if job.object and getattr(job.object, "python_class", None): + kwargs["job_timeout"] = job.object.python_class.job_timeout cls.enqueue( instance=job.object, user=job.user, From 532dbabbab8352188c07b041a27e32bc035c2f1b Mon Sep 17 00:00:00 2001 From: corubba Date: Tue, 15 Oct 2024 22:02:14 +0200 Subject: [PATCH 05/18] Fixes #17749: Add missing graphql fields --- netbox/dcim/graphql/types.py | 1 + netbox/tenancy/graphql/types.py | 2 ++ netbox/wireless/graphql/types.py | 1 + 3 files changed, 4 insertions(+) diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 8adfed82e..bce6f06ac 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -243,6 +243,7 @@ class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBo consoleserverports: List[Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]] poweroutlets: List[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]] frontports: List[Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]] + devicebays: List[Annotated["DeviceBayType", strawberry.lazy('dcim.graphql.types')]] modulebays: List[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]] services: List[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]] inventoryitems: List[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]] diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index 120b5c71b..7baa136b3 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -66,6 +66,7 @@ class TenantGroupType(OrganizationalObjectType): parent: Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')] | None tenants: List[TenantType] + children: List[Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')]] # @@ -99,6 +100,7 @@ class ContactGroupType(OrganizationalObjectType): parent: Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')] | None contacts: List[ContactType] + children: List[Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')]] @strawberry_django.type( diff --git a/netbox/wireless/graphql/types.py b/netbox/wireless/graphql/types.py index e542fd9c9..b24525fbe 100644 --- a/netbox/wireless/graphql/types.py +++ b/netbox/wireless/graphql/types.py @@ -23,6 +23,7 @@ class WirelessLANGroupType(OrganizationalObjectType): parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]] + children: List[Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')]] @strawberry_django.type( From 82de559317645bea76dd44d50449e27c6f6c2ed2 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 16 Oct 2024 11:15:36 -0700 Subject: [PATCH 06/18] 17754 fix per-page on version history (#17766) * 17754 fix per-page on version history * 17754 remove htmx table * Use non-HTMX template for static tables --------- Co-authored-by: Jeremy Stretch --- netbox/templates/core/plugin.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/netbox/templates/core/plugin.html b/netbox/templates/core/plugin.html index 34eaf81ee..b833db037 100644 --- a/netbox/templates/core/plugin.html +++ b/netbox/templates/core/plugin.html @@ -2,6 +2,7 @@ {% load helpers %} {% load form_helpers %} {% load i18n %} +{% load render_table from django_tables2 %} {% block title %}{{ plugin.title_long }}{% endblock %} @@ -93,8 +94,8 @@

{% trans "Version History" %}

-
- {% include 'htmx/table.html' %} +
+ {% render_table table 'inc/table.html' %}
From 81108e405fb2e44c6f57ef8c44bf5f3458915282 Mon Sep 17 00:00:00 2001 From: Brian Tiemann Date: Wed, 16 Oct 2024 15:06:21 -0400 Subject: [PATCH 07/18] Add webp to the list of acceptable extensions for handling filenames in image_upload --- netbox/extras/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/extras/utils.py b/netbox/extras/utils.py index 28d2e13f0..efe7ada5b 100644 --- a/netbox/extras/utils.py +++ b/netbox/extras/utils.py @@ -33,7 +33,7 @@ def image_upload(instance, filename): # Rename the file to the provided name, if any. Attempt to preserve the file extension. extension = filename.rsplit('.')[-1].lower() - if instance.name and extension in ['bmp', 'gif', 'jpeg', 'jpg', 'png']: + if instance.name and extension in ['bmp', 'gif', 'jpeg', 'jpg', 'png', 'webp']: filename = '.'.join([instance.name, extension]) elif instance.name: filename = instance.name From 27a39339df8442c18b8b282dae18c9fbd3b1a761 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 16 Oct 2024 13:53:21 -0700 Subject: [PATCH 08/18] 17464 fix margins for custom-field markdown description (#17775) * 17464 fix margins for custom-field markdown description * 17464 fix margins for custom-field markdown description * 17464 review changes * 17464 update comments --- netbox/project-static/dist/netbox.css | Bin 554655 -> 554652 bytes .../styles/custom/_markdown.scss | 13 ++++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css index fbad302ff47fecfc13969279dcd861519cf68e64..081c9aa6326820ac4c82d6f60f7b869b85a59d4e 100644 GIT binary patch delta 41 xcmbRLR&mZ-#fBEf7N!>F7M2#)7Pc1lEgYYVrt24T7)}3N#9_HTv6#b-830xQ4>bS) delta 45 zcmbR9R&oAY#fBEf7N!>F7M2#)7Pc1lEgYYVrmGfnSTdKS*iHXj#9_2Ou9(A)832%a B5J3O{ diff --git a/netbox/project-static/styles/custom/_markdown.scss b/netbox/project-static/styles/custom/_markdown.scss index 32ef7a09c..75ada3bc6 100644 --- a/netbox/project-static/styles/custom/_markdown.scss +++ b/netbox/project-static/styles/custom/_markdown.scss @@ -28,16 +28,19 @@ } -// Remove the bottom margin of

elements inside a table cell -td > .rendered-markdown { - max-height: 200px; - overflow-y: scroll; - +// Remove the bottom margin of the last

elements in markdown +.rendered-markdown { p:last-of-type { margin-bottom: 0; } } +// fix layout of rendered markdown inside a table cell +td > .rendered-markdown { + max-height: 200px; + overflow-y: scroll; +} + // Markdown preview .markdown-widget { .preview { From 33bc1320c4a0e798f20cd9fb4465465c9d46f0c2 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 16 Oct 2024 16:57:10 -0400 Subject: [PATCH 09/18] Changelog for #177109, #17740, #17749, #17754, #17759 --- docs/release-notes/version-4.1.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/release-notes/version-4.1.md b/docs/release-notes/version-4.1.md index d48bb899f..c29fb3aab 100644 --- a/docs/release-notes/version-4.1.md +++ b/docs/release-notes/version-4.1.md @@ -1,5 +1,17 @@ # NetBox v4.1 +## v4.1.5 (FUTURE) + +### Bug Fixes + +* [#17710](https://github.com/netbox-community/netbox/issues/17710) - Remove cached fields on CableTermination model from GraphQL API +* [#17740](https://github.com/netbox-community/netbox/issues/17740) - Ensure support for image attachments with a `.webp` file extension +* [#17749](https://github.com/netbox-community/netbox/issues/17749) - Restore missing `devicetypes` and `children` fields for several objects in GraphQL API +* [#17754](https://github.com/netbox-community/netbox/issues/17754) - Remove paginator from version history table under plugin view +* [#17759](https://github.com/netbox-community/netbox/issues/17759) - Retain `job_timeout` value when scheduling a recurring custom script + +--- + ## v4.1.4 (2024-10-15) ### Enhancements From 9f7743e5da12774ab3fe3fbfe46fa3dfb0508638 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 05:03:07 +0000 Subject: [PATCH 10/18] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 34 ++++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index 44b7b82a0..09797fd1b 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-16 05:01+0000\n" +"POT-Creation-Date: 2024-10-17 05:02+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -430,7 +430,7 @@ msgstr "" #: netbox/templates/circuits/provider.html:33 #: netbox/templates/circuits/providernetwork.html:32 #: netbox/templates/core/datasource.html:54 -#: netbox/templates/core/plugin.html:79 netbox/templates/dcim/cable.html:36 +#: netbox/templates/core/plugin.html:80 netbox/templates/dcim/cable.html:36 #: netbox/templates/dcim/consoleport.html:44 #: netbox/templates/dcim/consoleserverport.html:44 #: netbox/templates/dcim/device.html:94 netbox/templates/dcim/devicebay.html:32 @@ -1168,7 +1168,7 @@ msgstr "" msgid "status" msgstr "" -#: netbox/circuits/models/circuits.py:84 netbox/templates/core/plugin.html:19 +#: netbox/circuits/models/circuits.py:84 netbox/templates/core/plugin.html:20 msgid "installed" msgstr "" @@ -1396,7 +1396,7 @@ msgstr "" #: netbox/templates/circuits/provideraccount.html:28 #: netbox/templates/circuits/providernetwork.html:24 #: netbox/templates/core/datasource.html:34 netbox/templates/core/job.html:44 -#: netbox/templates/core/plugin.html:53 netbox/templates/core/rq_worker.html:43 +#: netbox/templates/core/plugin.html:54 netbox/templates/core/rq_worker.html:43 #: netbox/templates/dcim/consoleport.html:28 #: netbox/templates/dcim/consoleserverport.html:28 #: netbox/templates/dcim/devicebay.html:24 @@ -1654,7 +1654,7 @@ msgid "Cancelled" msgstr "" #: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 -#: netbox/templates/core/plugin.html:87 +#: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:216 msgid "Local" msgstr "" @@ -2072,7 +2072,7 @@ msgstr "" #: netbox/core/models/data.py:49 netbox/extras/choices.py:37 #: netbox/extras/models/models.py:164 netbox/extras/tables/tables.py:656 #: netbox/templates/core/datasource.html:58 -#: netbox/templates/core/plugin.html:65 +#: netbox/templates/core/plugin.html:66 msgid "URL" msgstr "" @@ -2320,7 +2320,7 @@ msgstr "" msgid "No plugin data found" msgstr "" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:61 +#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "" @@ -2328,7 +2328,7 @@ msgstr "" msgid "Installed" msgstr "" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:83 +#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "" @@ -10849,7 +10849,7 @@ msgstr "" #: netbox/netbox/navigation/menu.py:454 netbox/netbox/navigation/menu.py:502 #: netbox/templates/500.html:35 netbox/templates/account/preferences.html:22 -#: netbox/templates/core/plugin.html:12 +#: netbox/templates/core/plugin.html:13 #: netbox/templates/core/plugin_list.html:7 #: netbox/templates/core/plugin_list.html:12 msgid "Plugins" @@ -11769,35 +11769,35 @@ msgstr "" msgid "Indefinite" msgstr "" -#: netbox/templates/core/plugin.html:21 +#: netbox/templates/core/plugin.html:22 msgid "Not installed" msgstr "" -#: netbox/templates/core/plugin.html:32 +#: netbox/templates/core/plugin.html:33 msgid "Overview" msgstr "" -#: netbox/templates/core/plugin.html:38 +#: netbox/templates/core/plugin.html:39 msgid "Install" msgstr "" -#: netbox/templates/core/plugin.html:50 +#: netbox/templates/core/plugin.html:51 msgid "Plugin Details" msgstr "" -#: netbox/templates/core/plugin.html:57 +#: netbox/templates/core/plugin.html:58 msgid "Summary" msgstr "" -#: netbox/templates/core/plugin.html:75 +#: netbox/templates/core/plugin.html:76 msgid "License" msgstr "" -#: netbox/templates/core/plugin.html:95 +#: netbox/templates/core/plugin.html:96 msgid "Version History" msgstr "" -#: netbox/templates/core/plugin.html:106 +#: netbox/templates/core/plugin.html:107 msgid "Local Installation Instructions" msgstr "" From 6a316df787915141f8038507c531cdcfaffc2b39 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 16 Oct 2024 16:05:01 -0400 Subject: [PATCH 11/18] Closes #17789: Use a single scope field for VLANGroup bulk edit --- netbox/ipam/forms/bulk_edit.py | 97 +++++------------- netbox/netbox/views/generic/bulk_views.py | 9 +- netbox/templates/generic/bulk_edit.html | 116 +++++++++++----------- 3 files changed, 90 insertions(+), 132 deletions(-) diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py index f4a7eabb7..79debd0ed 100644 --- a/netbox/ipam/forms/bulk_edit.py +++ b/netbox/ipam/forms/bulk_edit.py @@ -1,22 +1,23 @@ from django import forms from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import gettext_lazy as _ -from dcim.models import Location, Rack, Region, Site, SiteGroup +from dcim.models import Region, Site, SiteGroup from ipam.choices import * from ipam.constants import * from ipam.models import * from ipam.models import ASN from netbox.forms import NetBoxModelBulkEditForm from tenancy.models import Tenant -from utilities.forms import add_blank_choice +from utilities.forms import add_blank_choice, get_field_value from utilities.forms.fields import ( CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField, NumericRangeArrayField, ) from utilities.forms.rendering import FieldSet -from utilities.forms.widgets import BulkEditNullBooleanSelect -from virtualization.models import Cluster, ClusterGroup +from utilities.forms.widgets import BulkEditNullBooleanSelect, HTMXSelect +from utilities.templatetags.builtins.filters import bettertitle __all__ = ( 'AggregateBulkEditForm', @@ -429,62 +430,17 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): required=False ) scope_type = ContentTypeChoiceField( - label=_('Scope type'), queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES), - required=False - ) - scope_id = forms.IntegerField( + widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}), required=False, - widget=forms.HiddenInput() + label=_('Scope type') ) - region = DynamicModelChoiceField( - label=_('Region'), - queryset=Region.objects.all(), - required=False - ) - sitegroup = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), + scope = DynamicModelChoiceField( + label=_('Scope'), + queryset=Site.objects.none(), # Initial queryset required=False, - label=_('Site group') - ) - site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False, - query_params={ - 'region_id': '$region', - 'group_id': '$sitegroup', - } - ) - location = DynamicModelChoiceField( - label=_('Location'), - queryset=Location.objects.all(), - required=False, - query_params={ - 'site_id': '$site', - } - ) - rack = DynamicModelChoiceField( - label=_('Rack'), - queryset=Rack.objects.all(), - required=False, - query_params={ - 'site_id': '$site', - 'location_id': '$location', - } - ) - clustergroup = DynamicModelChoiceField( - queryset=ClusterGroup.objects.all(), - required=False, - label=_('Cluster group') - ) - cluster = DynamicModelChoiceField( - label=_('Cluster'), - queryset=Cluster.objects.all(), - required=False, - query_params={ - 'group_id': '$clustergroup', - } + disabled=True, + selector=True ) vid_ranges = NumericRangeArrayField( label=_('VLAN ID ranges'), @@ -494,24 +450,23 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): model = VLANGroup fieldsets = ( FieldSet('site', 'vid_ranges', 'description'), - FieldSet( - 'scope_type', 'region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster', name=_('Scope') - ), + FieldSet('scope_type', 'scope', name=_('Scope')), ) - nullable_fields = ('description',) + nullable_fields = ('description', 'scope') - def clean(self): - super().clean() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) - # Assign scope based on scope_type - if self.cleaned_data.get('scope_type'): - scope_field = self.cleaned_data['scope_type'].model - if scope_obj := self.cleaned_data.get(scope_field): - self.cleaned_data['scope_id'] = scope_obj.pk - self.changed_data.append('scope_id') - else: - self.cleaned_data.pop('scope_type') - self.changed_data.remove('scope_type') + if scope_type_id := get_field_value(self, 'scope_type'): + try: + scope_type = ContentType.objects.get(pk=scope_type_id) + model = scope_type.model_class() + self.fields['scope'].queryset = model.objects.all() + self.fields['scope'].widget.attrs['selector'] = model._meta.label_lower + self.fields['scope'].disabled = False + self.fields['scope'].label = _(bettertitle(model._meta.verbose_name)) + except ObjectDoesNotExist: + pass class VLANBulkEditForm(NetBoxModelBulkEditForm): diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index d7d28b95f..d8115726c 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -3,7 +3,7 @@ import re from copy import deepcopy from django.contrib import messages -from django.contrib.contenttypes.fields import GenericRel +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError from django.db import transaction, IntegrityError @@ -576,7 +576,10 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): for name, model_field in model_fields.items(): # Handle nullification if name in form.nullable_fields and name in nullified_fields: - setattr(obj, name, None if model_field.null else '') + if type(model_field) is GenericForeignKey: + setattr(obj, name, None) + else: + setattr(obj, name, None if model_field.null else '') # Normal fields elif name in form.changed_data: setattr(obj, name, form.cleaned_data[name]) @@ -688,7 +691,7 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): logger.debug("Form validation failed") else: - form = self.form(initial=initial_data) + form = self.form(request.POST, initial=initial_data) restrict_form_fields(form, request.user) # Retrieve objects being edited diff --git a/netbox/templates/generic/bulk_edit.html b/netbox/templates/generic/bulk_edit.html index 4b4d5aece..8c4d305ec 100644 --- a/netbox/templates/generic/bulk_edit.html +++ b/netbox/templates/generic/bulk_edit.html @@ -42,71 +42,71 @@ Context: {# Edit form #}

- - {% csrf_token %} - {% if request.POST.return_url %} - - {% endif %} - {% for field in form.hidden_fields %} - {{ field }} - {% endfor %} - - {% if form.fieldsets %} - - {# Render grouped fields according to declared fieldsets #} - {% for fieldset in form.fieldsets %} - {% render_fieldset form fieldset %} +
+ {% csrf_token %} + {% if request.POST.return_url %} + + {% endif %} + {% for field in form.hidden_fields %} + {{ field }} {% endfor %} - {# Render tag add/remove fields #} - {% if form.add_tags and form.remove_tags %} -
-
-

{% trans "Tags" %}

+ {% if form.fieldsets %} + + {# Render grouped fields according to declared fieldsets #} + {% for fieldset in form.fieldsets %} + {% render_fieldset form fieldset %} + {% endfor %} + + {# Render tag add/remove fields #} + {% if form.add_tags and form.remove_tags %} +
+
+

{% trans "Tags" %}

+
+ {% render_field form.add_tags %} + {% render_field form.remove_tags %}
- {% render_field form.add_tags %} - {% render_field form.remove_tags %} -
- {% endif %} - - {# Render custom fields #} - {% if form.custom_fields %} -
-
-

{% trans "Custom Fields" %}

-
- {% render_custom_fields form %} -
- {% endif %} - - {# Render comments #} - {% if form.comments %} -
-
-

{% trans "Comments" %}

-
- {% render_field form.comments bulk_nullable=True %} -
- {% endif %} - - {% else %} - - {# Render all fields #} - {% for field in form.visible_fields %} - {% if field.name in form.nullable_fields %} - {% render_field field bulk_nullable=True %} - {% else %} - {% render_field field %} {% endif %} - {% endfor %} - {% endif %} + {# Render custom fields #} + {% if form.custom_fields %} +
+
+

{% trans "Custom Fields" %}

+
+ {% render_custom_fields form %} +
+ {% endif %} -
- {% trans "Cancel" %} - + {# Render comments #} + {% if form.comments %} +
+
+

{% trans "Comments" %}

+
+ {% render_field form.comments bulk_nullable=True %} +
+ {% endif %} + + {% else %} + + {# Render all fields #} + {% for field in form.visible_fields %} + {% if field.name in form.nullable_fields %} + {% render_field field bulk_nullable=True %} + {% else %} + {% render_field field %} + {% endif %} + {% endfor %} + + {% endif %} + +
+ {% trans "Cancel" %} + +
-
From 110b2b3d978948a3bd0d0b14c02ba312b569fdff Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Oct 2024 05:02:11 +0000 Subject: [PATCH 12/18] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 163 +++++++++---------- 1 file changed, 80 insertions(+), 83 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index 09797fd1b..4748feb46 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-17 05:02+0000\n" +"POT-Creation-Date: 2024-10-18 05:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -212,14 +212,13 @@ msgstr "" #: netbox/dcim/tables/power.py:26 netbox/dcim/tables/power.py:93 #: netbox/dcim/tables/racks.py:122 netbox/dcim/tables/racks.py:207 #: netbox/dcim/tables/sites.py:134 netbox/extras/filtersets.py:525 -#: netbox/ipam/forms/bulk_edit.py:217 netbox/ipam/forms/bulk_edit.py:284 -#: netbox/ipam/forms/bulk_edit.py:451 netbox/ipam/forms/bulk_edit.py:529 -#: netbox/ipam/forms/bulk_import.py:171 netbox/ipam/forms/bulk_import.py:429 -#: netbox/ipam/forms/filtersets.py:153 netbox/ipam/forms/filtersets.py:231 -#: netbox/ipam/forms/filtersets.py:432 netbox/ipam/forms/filtersets.py:489 -#: netbox/ipam/forms/model_forms.py:205 netbox/ipam/forms/model_forms.py:636 -#: netbox/ipam/tables/ip.py:245 netbox/ipam/tables/vlans.py:118 -#: netbox/ipam/tables/vlans.py:221 +#: netbox/ipam/forms/bulk_edit.py:218 netbox/ipam/forms/bulk_edit.py:285 +#: netbox/ipam/forms/bulk_edit.py:484 netbox/ipam/forms/bulk_import.py:171 +#: netbox/ipam/forms/bulk_import.py:429 netbox/ipam/forms/filtersets.py:153 +#: netbox/ipam/forms/filtersets.py:231 netbox/ipam/forms/filtersets.py:432 +#: netbox/ipam/forms/filtersets.py:489 netbox/ipam/forms/model_forms.py:205 +#: netbox/ipam/forms/model_forms.py:636 netbox/ipam/tables/ip.py:245 +#: netbox/ipam/tables/vlans.py:118 netbox/ipam/tables/vlans.py:221 #: netbox/templates/circuits/inc/circuit_termination_fields.html:6 #: netbox/templates/dcim/device.html:22 #: netbox/templates/dcim/inc/cable_termination.html:8 @@ -415,13 +414,13 @@ msgstr "" #: netbox/extras/forms/bulk_edit.py:256 netbox/extras/forms/bulk_edit.py:274 #: netbox/extras/forms/bulk_edit.py:298 netbox/extras/forms/bulk_edit.py:312 #: netbox/extras/forms/bulk_edit.py:339 netbox/extras/tables/tables.py:79 -#: netbox/ipam/forms/bulk_edit.py:52 netbox/ipam/forms/bulk_edit.py:72 -#: netbox/ipam/forms/bulk_edit.py:92 netbox/ipam/forms/bulk_edit.py:116 -#: netbox/ipam/forms/bulk_edit.py:145 netbox/ipam/forms/bulk_edit.py:174 -#: netbox/ipam/forms/bulk_edit.py:193 netbox/ipam/forms/bulk_edit.py:275 -#: netbox/ipam/forms/bulk_edit.py:320 netbox/ipam/forms/bulk_edit.py:368 -#: netbox/ipam/forms/bulk_edit.py:411 netbox/ipam/forms/bulk_edit.py:427 -#: netbox/ipam/forms/bulk_edit.py:561 netbox/ipam/forms/bulk_edit.py:592 +#: netbox/ipam/forms/bulk_edit.py:53 netbox/ipam/forms/bulk_edit.py:73 +#: netbox/ipam/forms/bulk_edit.py:93 netbox/ipam/forms/bulk_edit.py:117 +#: netbox/ipam/forms/bulk_edit.py:146 netbox/ipam/forms/bulk_edit.py:175 +#: netbox/ipam/forms/bulk_edit.py:194 netbox/ipam/forms/bulk_edit.py:276 +#: netbox/ipam/forms/bulk_edit.py:321 netbox/ipam/forms/bulk_edit.py:369 +#: netbox/ipam/forms/bulk_edit.py:412 netbox/ipam/forms/bulk_edit.py:428 +#: netbox/ipam/forms/bulk_edit.py:516 netbox/ipam/forms/bulk_edit.py:547 #: netbox/templates/account/token.html:35 #: netbox/templates/circuits/circuit.html:59 #: netbox/templates/circuits/circuitgroup.html:32 @@ -654,8 +653,8 @@ msgstr "" #: netbox/dcim/tables/devices.py:1063 netbox/dcim/tables/modules.py:69 #: netbox/dcim/tables/power.py:74 netbox/dcim/tables/racks.py:126 #: netbox/dcim/tables/sites.py:82 netbox/dcim/tables/sites.py:138 -#: netbox/ipam/forms/bulk_edit.py:255 netbox/ipam/forms/bulk_edit.py:305 -#: netbox/ipam/forms/bulk_edit.py:353 netbox/ipam/forms/bulk_edit.py:551 +#: netbox/ipam/forms/bulk_edit.py:256 netbox/ipam/forms/bulk_edit.py:306 +#: netbox/ipam/forms/bulk_edit.py:354 netbox/ipam/forms/bulk_edit.py:506 #: netbox/ipam/forms/bulk_import.py:192 netbox/ipam/forms/bulk_import.py:257 #: netbox/ipam/forms/bulk_import.py:293 netbox/ipam/forms/bulk_import.py:450 #: netbox/ipam/forms/filtersets.py:210 netbox/ipam/forms/filtersets.py:281 @@ -720,11 +719,11 @@ msgstr "" #: netbox/dcim/forms/filtersets.py:978 netbox/dcim/forms/filtersets.py:1008 #: netbox/dcim/forms/filtersets.py:1130 netbox/dcim/tables/power.py:88 #: netbox/extras/filtersets.py:612 netbox/extras/forms/filtersets.py:323 -#: netbox/extras/forms/filtersets.py:396 netbox/ipam/forms/bulk_edit.py:42 -#: netbox/ipam/forms/bulk_edit.py:67 netbox/ipam/forms/bulk_edit.py:111 -#: netbox/ipam/forms/bulk_edit.py:140 netbox/ipam/forms/bulk_edit.py:165 -#: netbox/ipam/forms/bulk_edit.py:250 netbox/ipam/forms/bulk_edit.py:300 -#: netbox/ipam/forms/bulk_edit.py:348 netbox/ipam/forms/bulk_edit.py:546 +#: netbox/extras/forms/filtersets.py:396 netbox/ipam/forms/bulk_edit.py:43 +#: netbox/ipam/forms/bulk_edit.py:68 netbox/ipam/forms/bulk_edit.py:112 +#: netbox/ipam/forms/bulk_edit.py:141 netbox/ipam/forms/bulk_edit.py:166 +#: netbox/ipam/forms/bulk_edit.py:251 netbox/ipam/forms/bulk_edit.py:301 +#: netbox/ipam/forms/bulk_edit.py:349 netbox/ipam/forms/bulk_edit.py:501 #: netbox/ipam/forms/bulk_import.py:38 netbox/ipam/forms/bulk_import.py:67 #: netbox/ipam/forms/bulk_import.py:95 netbox/ipam/forms/bulk_import.py:115 #: netbox/ipam/forms/bulk_import.py:135 netbox/ipam/forms/bulk_import.py:164 @@ -950,9 +949,9 @@ msgstr "" #: netbox/dcim/tables/devices.py:157 netbox/dcim/tables/power.py:30 #: netbox/dcim/tables/racks.py:118 netbox/dcim/tables/racks.py:212 #: netbox/extras/filtersets.py:536 netbox/extras/forms/filtersets.py:320 -#: netbox/ipam/forms/bulk_edit.py:460 netbox/ipam/forms/filtersets.py:173 -#: netbox/ipam/forms/filtersets.py:414 netbox/ipam/forms/filtersets.py:437 -#: netbox/ipam/forms/filtersets.py:467 netbox/templates/dcim/device.html:26 +#: netbox/ipam/forms/filtersets.py:173 netbox/ipam/forms/filtersets.py:414 +#: netbox/ipam/forms/filtersets.py:437 netbox/ipam/forms/filtersets.py:467 +#: netbox/templates/dcim/device.html:26 #: netbox/templates/dcim/device_edit.html:30 #: netbox/templates/dcim/inc/cable_termination.html:12 #: netbox/templates/dcim/location.html:26 @@ -992,11 +991,10 @@ msgstr "" #: netbox/dcim/forms/filtersets.py:1600 netbox/dcim/forms/filtersets.py:1624 #: netbox/dcim/forms/model_forms.py:112 netbox/dcim/forms/object_create.py:375 #: netbox/dcim/tables/devices.py:143 netbox/dcim/tables/sites.py:85 -#: netbox/extras/filtersets.py:503 netbox/ipam/forms/bulk_edit.py:207 -#: netbox/ipam/forms/bulk_edit.py:441 netbox/ipam/forms/bulk_edit.py:519 -#: netbox/ipam/forms/filtersets.py:217 netbox/ipam/forms/filtersets.py:422 -#: netbox/ipam/forms/filtersets.py:475 netbox/templates/dcim/device.html:18 -#: netbox/templates/dcim/rack.html:16 +#: netbox/extras/filtersets.py:503 netbox/ipam/forms/bulk_edit.py:208 +#: netbox/ipam/forms/bulk_edit.py:474 netbox/ipam/forms/filtersets.py:217 +#: netbox/ipam/forms/filtersets.py:422 netbox/ipam/forms/filtersets.py:475 +#: netbox/templates/dcim/device.html:18 netbox/templates/dcim/rack.html:16 #: netbox/templates/dcim/rackreservation.html:22 #: netbox/templates/dcim/region.html:26 netbox/templates/dcim/site.html:31 #: netbox/templates/ipam/prefix.html:49 netbox/templates/ipam/vlan.html:16 @@ -1016,9 +1014,9 @@ msgstr "" #: netbox/dcim/forms/filtersets.py:744 netbox/dcim/forms/filtersets.py:988 #: netbox/dcim/forms/filtersets.py:1102 netbox/dcim/forms/filtersets.py:1141 #: netbox/dcim/forms/object_create.py:383 netbox/extras/filtersets.py:520 -#: netbox/ipam/forms/bulk_edit.py:212 netbox/ipam/forms/bulk_edit.py:448 -#: netbox/ipam/forms/bulk_edit.py:524 netbox/ipam/forms/filtersets.py:222 -#: netbox/ipam/forms/filtersets.py:427 netbox/ipam/forms/filtersets.py:480 +#: netbox/ipam/forms/bulk_edit.py:213 netbox/ipam/forms/bulk_edit.py:479 +#: netbox/ipam/forms/filtersets.py:222 netbox/ipam/forms/filtersets.py:427 +#: netbox/ipam/forms/filtersets.py:480 #: netbox/virtualization/forms/bulk_edit.py:86 #: netbox/virtualization/forms/filtersets.py:69 #: netbox/virtualization/forms/filtersets.py:138 @@ -1083,7 +1081,7 @@ msgstr "" #: netbox/circuits/tables/circuits.py:155 netbox/dcim/forms/bulk_edit.py:117 #: netbox/dcim/forms/bulk_import.py:100 netbox/dcim/forms/model_forms.py:117 #: netbox/dcim/tables/sites.py:89 netbox/extras/forms/filtersets.py:480 -#: netbox/ipam/filtersets.py:999 netbox/ipam/forms/bulk_edit.py:538 +#: netbox/ipam/filtersets.py:999 netbox/ipam/forms/bulk_edit.py:493 #: netbox/ipam/forms/bulk_import.py:436 netbox/ipam/forms/model_forms.py:528 #: netbox/ipam/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:122 #: netbox/ipam/tables/vlans.py:226 @@ -1386,7 +1384,7 @@ msgstr "" #: netbox/extras/tables/tables.py:361 netbox/extras/tables/tables.py:378 #: netbox/extras/tables/tables.py:401 netbox/extras/tables/tables.py:439 #: netbox/extras/tables/tables.py:491 netbox/extras/tables/tables.py:514 -#: netbox/ipam/forms/bulk_edit.py:406 netbox/ipam/forms/filtersets.py:386 +#: netbox/ipam/forms/bulk_edit.py:407 netbox/ipam/forms/filtersets.py:386 #: netbox/ipam/tables/asn.py:16 netbox/ipam/tables/ip.py:85 #: netbox/ipam/tables/ip.py:160 netbox/ipam/tables/services.py:15 #: netbox/ipam/tables/services.py:40 netbox/ipam/tables/vlans.py:64 @@ -3237,8 +3235,8 @@ msgstr "" #: netbox/dcim/tables/devices.py:626 netbox/ipam/filtersets.py:316 #: netbox/ipam/filtersets.py:327 netbox/ipam/filtersets.py:483 #: netbox/ipam/filtersets.py:584 netbox/ipam/filtersets.py:595 -#: netbox/ipam/forms/bulk_edit.py:241 netbox/ipam/forms/bulk_edit.py:297 -#: netbox/ipam/forms/bulk_edit.py:339 netbox/ipam/forms/bulk_import.py:157 +#: netbox/ipam/forms/bulk_edit.py:242 netbox/ipam/forms/bulk_edit.py:298 +#: netbox/ipam/forms/bulk_edit.py:340 netbox/ipam/forms/bulk_import.py:157 #: netbox/ipam/forms/bulk_import.py:243 netbox/ipam/forms/bulk_import.py:279 #: netbox/ipam/forms/filtersets.py:67 netbox/ipam/forms/filtersets.py:172 #: netbox/ipam/forms/filtersets.py:309 netbox/ipam/forms/model_forms.py:62 @@ -3501,7 +3499,7 @@ msgstr "" #: netbox/extras/forms/bulk_edit.py:53 netbox/extras/forms/bulk_edit.py:133 #: netbox/extras/forms/bulk_edit.py:183 netbox/extras/forms/bulk_edit.py:288 #: netbox/extras/forms/filtersets.py:64 netbox/extras/forms/filtersets.py:156 -#: netbox/extras/forms/filtersets.py:243 netbox/ipam/forms/bulk_edit.py:189 +#: netbox/extras/forms/filtersets.py:243 netbox/ipam/forms/bulk_edit.py:190 #: netbox/templates/dcim/device.html:324 #: netbox/templates/dcim/devicetype.html:49 #: netbox/templates/dcim/moduletype.html:34 netbox/templates/dcim/rack.html:81 @@ -3559,8 +3557,8 @@ msgstr "" #: netbox/dcim/tables/devices.py:169 netbox/dcim/tables/devices.py:809 #: netbox/dcim/tables/devices.py:937 netbox/dcim/tables/devicetypes.py:304 #: netbox/dcim/tables/racks.py:129 netbox/extras/filtersets.py:552 -#: netbox/ipam/forms/bulk_edit.py:260 netbox/ipam/forms/bulk_edit.py:310 -#: netbox/ipam/forms/bulk_edit.py:358 netbox/ipam/forms/bulk_edit.py:556 +#: netbox/ipam/forms/bulk_edit.py:261 netbox/ipam/forms/bulk_edit.py:311 +#: netbox/ipam/forms/bulk_edit.py:359 netbox/ipam/forms/bulk_edit.py:511 #: netbox/ipam/forms/bulk_import.py:197 netbox/ipam/forms/bulk_import.py:262 #: netbox/ipam/forms/bulk_import.py:298 netbox/ipam/forms/bulk_import.py:455 #: netbox/ipam/forms/filtersets.py:237 netbox/ipam/forms/filtersets.py:289 @@ -3630,8 +3628,8 @@ msgstr "" #: netbox/dcim/forms/model_forms.py:306 netbox/dcim/forms/model_forms.py:479 #: netbox/dcim/forms/model_forms.py:755 netbox/dcim/forms/object_create.py:400 #: netbox/dcim/tables/devices.py:161 netbox/dcim/tables/power.py:70 -#: netbox/dcim/tables/racks.py:217 netbox/ipam/forms/bulk_edit.py:468 -#: netbox/ipam/forms/filtersets.py:442 netbox/templates/dcim/device.html:30 +#: netbox/dcim/tables/racks.py:217 netbox/ipam/forms/filtersets.py:442 +#: netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 #: netbox/templates/dcim/powerfeed.html:28 netbox/templates/dcim/rack.html:13 #: netbox/templates/dcim/rack/base.html:4 @@ -4012,8 +4010,8 @@ msgid "Wireless LANs" msgstr "" #: netbox/dcim/forms/bulk_edit.py:1508 netbox/dcim/forms/filtersets.py:1328 -#: netbox/dcim/forms/model_forms.py:1390 netbox/ipam/forms/bulk_edit.py:285 -#: netbox/ipam/forms/bulk_edit.py:377 netbox/ipam/forms/filtersets.py:169 +#: netbox/dcim/forms/model_forms.py:1390 netbox/ipam/forms/bulk_edit.py:286 +#: netbox/ipam/forms/bulk_edit.py:378 netbox/ipam/forms/filtersets.py:169 #: netbox/templates/dcim/interface.html:122 #: netbox/templates/ipam/prefix.html:95 #: netbox/virtualization/forms/model_forms.py:349 @@ -4196,9 +4194,8 @@ msgstr "" #: netbox/dcim/forms/bulk_import.py:517 netbox/dcim/forms/filtersets.py:728 #: netbox/dcim/forms/filtersets.py:898 netbox/dcim/forms/model_forms.py:522 #: netbox/dcim/tables/devices.py:202 netbox/extras/filtersets.py:596 -#: netbox/extras/forms/filtersets.py:322 netbox/ipam/forms/bulk_edit.py:482 -#: netbox/ipam/forms/filtersets.py:415 netbox/ipam/forms/filtersets.py:447 -#: netbox/templates/dcim/device.html:239 +#: netbox/extras/forms/filtersets.py:322 netbox/ipam/forms/filtersets.py:415 +#: netbox/ipam/forms/filtersets.py:447 netbox/templates/dcim/device.html:239 #: netbox/templates/virtualization/cluster.html:10 #: netbox/templates/virtualization/virtualmachine.html:92 #: netbox/templates/virtualization/virtualmachine.html:101 @@ -4644,7 +4641,7 @@ msgid "Has virtual device contexts" msgstr "" #: netbox/dcim/forms/filtersets.py:903 netbox/extras/filtersets.py:585 -#: netbox/ipam/forms/bulk_edit.py:479 netbox/ipam/forms/filtersets.py:452 +#: netbox/ipam/forms/filtersets.py:452 #: netbox/virtualization/forms/filtersets.py:112 msgid "Cluster group" msgstr "" @@ -6847,7 +6844,7 @@ msgstr "" #: netbox/dcim/tables/sites.py:30 netbox/dcim/tables/sites.py:57 #: netbox/extras/forms/filtersets.py:351 netbox/extras/forms/model_forms.py:517 -#: netbox/ipam/forms/bulk_edit.py:130 netbox/ipam/forms/model_forms.py:153 +#: netbox/ipam/forms/bulk_edit.py:131 netbox/ipam/forms/model_forms.py:153 #: netbox/ipam/tables/asn.py:66 netbox/netbox/navigation/menu.py:15 #: netbox/netbox/navigation/menu.py:17 msgid "Sites" @@ -9009,7 +9006,7 @@ msgid "Prefixes which contain this prefix or IP" msgstr "" #: netbox/ipam/filtersets.py:304 netbox/ipam/filtersets.py:572 -#: netbox/ipam/forms/bulk_edit.py:342 netbox/ipam/forms/filtersets.py:196 +#: netbox/ipam/forms/bulk_edit.py:343 netbox/ipam/forms/filtersets.py:196 #: netbox/ipam/forms/filtersets.py:331 msgid "Mask length" msgstr "" @@ -9135,16 +9132,16 @@ msgstr "" msgid "Address pattern" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:49 +#: netbox/ipam/forms/bulk_edit.py:50 msgid "Enforce unique space" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:87 +#: netbox/ipam/forms/bulk_edit.py:88 msgid "Is private" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:108 netbox/ipam/forms/bulk_edit.py:137 -#: netbox/ipam/forms/bulk_edit.py:162 netbox/ipam/forms/bulk_import.py:89 +#: netbox/ipam/forms/bulk_edit.py:109 netbox/ipam/forms/bulk_edit.py:138 +#: netbox/ipam/forms/bulk_edit.py:163 netbox/ipam/forms/bulk_import.py:89 #: netbox/ipam/forms/bulk_import.py:109 netbox/ipam/forms/bulk_import.py:129 #: netbox/ipam/forms/filtersets.py:110 netbox/ipam/forms/filtersets.py:125 #: netbox/ipam/forms/filtersets.py:148 netbox/ipam/forms/model_forms.py:96 @@ -9158,18 +9155,18 @@ msgstr "" msgid "RIR" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:170 +#: netbox/ipam/forms/bulk_edit.py:171 msgid "Date added" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:228 netbox/ipam/forms/model_forms.py:586 +#: netbox/ipam/forms/bulk_edit.py:229 netbox/ipam/forms/model_forms.py:586 #: netbox/ipam/forms/model_forms.py:633 netbox/ipam/tables/ip.py:251 #: netbox/templates/ipam/vlan_edit.html:37 #: netbox/templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:233 netbox/ipam/forms/bulk_import.py:185 +#: netbox/ipam/forms/bulk_edit.py:234 netbox/ipam/forms/bulk_import.py:185 #: netbox/ipam/forms/filtersets.py:256 netbox/ipam/forms/model_forms.py:218 #: netbox/ipam/models/vlans.py:234 netbox/ipam/tables/ip.py:255 #: netbox/templates/ipam/prefix.html:60 netbox/templates/ipam/vlan.html:12 @@ -9184,30 +9181,30 @@ msgstr "" msgid "VLAN" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:244 +#: netbox/ipam/forms/bulk_edit.py:245 msgid "Prefix length" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:267 netbox/ipam/forms/filtersets.py:241 +#: netbox/ipam/forms/bulk_edit.py:268 netbox/ipam/forms/filtersets.py:241 #: netbox/templates/ipam/prefix.html:85 msgid "Is a pool" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:272 netbox/ipam/forms/bulk_edit.py:317 +#: netbox/ipam/forms/bulk_edit.py:273 netbox/ipam/forms/bulk_edit.py:318 #: netbox/ipam/forms/filtersets.py:248 netbox/ipam/forms/filtersets.py:293 #: netbox/ipam/models/ip.py:272 netbox/ipam/models/ip.py:539 msgid "Treat as fully utilized" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:286 netbox/ipam/forms/filtersets.py:171 +#: netbox/ipam/forms/bulk_edit.py:287 netbox/ipam/forms/filtersets.py:171 msgid "VLAN Assignment" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:365 netbox/ipam/models/ip.py:772 +#: netbox/ipam/forms/bulk_edit.py:366 netbox/ipam/models/ip.py:772 msgid "DNS name" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:386 netbox/ipam/forms/bulk_edit.py:579 +#: netbox/ipam/forms/bulk_edit.py:387 netbox/ipam/forms/bulk_edit.py:534 #: netbox/ipam/forms/bulk_import.py:394 netbox/ipam/forms/bulk_import.py:469 #: netbox/ipam/forms/bulk_import.py:495 netbox/ipam/forms/filtersets.py:390 #: netbox/ipam/forms/filtersets.py:530 netbox/templates/ipam/fhrpgroup.html:22 @@ -9217,12 +9214,12 @@ msgstr "" msgid "Protocol" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:393 netbox/ipam/forms/filtersets.py:397 +#: netbox/ipam/forms/bulk_edit.py:394 netbox/ipam/forms/filtersets.py:397 #: netbox/ipam/tables/fhrp.py:22 netbox/templates/ipam/fhrpgroup.html:26 msgid "Group ID" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:398 netbox/ipam/forms/filtersets.py:402 +#: netbox/ipam/forms/bulk_edit.py:399 netbox/ipam/forms/filtersets.py:402 #: netbox/wireless/forms/bulk_edit.py:68 netbox/wireless/forms/bulk_edit.py:115 #: netbox/wireless/forms/bulk_import.py:62 #: netbox/wireless/forms/bulk_import.py:65 @@ -9233,11 +9230,11 @@ msgstr "" msgid "Authentication type" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:403 netbox/ipam/forms/filtersets.py:406 +#: netbox/ipam/forms/bulk_edit.py:404 netbox/ipam/forms/filtersets.py:406 msgid "Authentication key" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:420 netbox/ipam/forms/filtersets.py:383 +#: netbox/ipam/forms/bulk_edit.py:421 netbox/ipam/forms/filtersets.py:383 #: netbox/ipam/forms/model_forms.py:474 netbox/netbox/navigation/menu.py:386 #: netbox/templates/ipam/fhrpgroup.html:49 #: netbox/templates/wireless/inc/authentication_attrs.html:5 @@ -9249,25 +9246,25 @@ msgstr "" msgid "Authentication" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:432 netbox/ipam/forms/model_forms.py:575 +#: netbox/ipam/forms/bulk_edit.py:436 netbox/ipam/forms/model_forms.py:575 msgid "Scope type" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:490 netbox/ipam/models/vlans.py:60 -msgid "VLAN ID ranges" -msgstr "" - -#: netbox/ipam/forms/bulk_edit.py:498 netbox/ipam/forms/model_forms.py:578 -#: netbox/ipam/forms/model_forms.py:588 netbox/ipam/tables/vlans.py:71 -#: netbox/templates/ipam/vlangroup.html:38 +#: netbox/ipam/forms/bulk_edit.py:439 netbox/ipam/forms/bulk_edit.py:453 +#: netbox/ipam/forms/model_forms.py:578 netbox/ipam/forms/model_forms.py:588 +#: netbox/ipam/tables/vlans.py:71 netbox/templates/ipam/vlangroup.html:38 msgid "Scope" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:570 +#: netbox/ipam/forms/bulk_edit.py:446 netbox/ipam/models/vlans.py:60 +msgid "VLAN ID ranges" +msgstr "" + +#: netbox/ipam/forms/bulk_edit.py:525 msgid "Site & Group" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:584 netbox/ipam/forms/model_forms.py:659 +#: netbox/ipam/forms/bulk_edit.py:539 netbox/ipam/forms/model_forms.py:659 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/services.py:19 #: netbox/ipam/tables/services.py:49 netbox/templates/ipam/service.html:36 #: netbox/templates/ipam/servicetemplate.html:23 @@ -11088,19 +11085,19 @@ msgstr "" msgid "Row {i}: Object with ID {id} does not exist" msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:699 -#: netbox/netbox/views/generic/bulk_views.py:897 -#: netbox/netbox/views/generic/bulk_views.py:945 +#: netbox/netbox/views/generic/bulk_views.py:702 +#: netbox/netbox/views/generic/bulk_views.py:900 +#: netbox/netbox/views/generic/bulk_views.py:948 #, python-brace-format msgid "No {object_type} were selected." msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:779 +#: netbox/netbox/views/generic/bulk_views.py:782 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:875 +#: netbox/netbox/views/generic/bulk_views.py:878 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "" From e6f41f73f7a643b44693017512b6b6fa51c61b09 Mon Sep 17 00:00:00 2001 From: Ian Bishop <151477169+ianb-mp@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:36:29 +1000 Subject: [PATCH 13/18] Add instructions for authenticating using Google oauth2 (#17527) * Add instructions for authenticating using Google oauth2 Signed-off-by: Ian Bishop <151477169+ianb-mp@users.noreply.github.com> * Add navigation link * Misc cleanup --------- Signed-off-by: Ian Bishop <151477169+ianb-mp@users.noreply.github.com> Co-authored-by: Jeremy Stretch --- docs/administration/authentication/google.md | 52 ++++++++++++++++++ .../authentication/google_login_portal.png | Bin 0 -> 28935 bytes .../authentication/netbox_google_login.png | Bin 0 -> 20774 bytes mkdocs.yml | 1 + 4 files changed, 53 insertions(+) create mode 100644 docs/administration/authentication/google.md create mode 100644 docs/media/authentication/google_login_portal.png create mode 100644 docs/media/authentication/netbox_google_login.png diff --git a/docs/administration/authentication/google.md b/docs/administration/authentication/google.md new file mode 100644 index 000000000..456f3a457 --- /dev/null +++ b/docs/administration/authentication/google.md @@ -0,0 +1,52 @@ +# Google + +This guide explains how to configure single sign-on (SSO) support for NetBox using [Google OAuth2](https://developers.google.com/identity/protocols/oauth2/web-server) as an authentication backend. + +## Google OAuth2 Configuration + +1. Log into [console.cloud.google.com](https://console.cloud.google.com/). +2. Create new project for NetBox. +3. Under "APIs and Services" click "OAuth consent screen" and enter the required information. +4. Under "Credentials," click "Create Credentials" and select "OAuth 2.0 Client ID." Select type "Web application." + - "Authorized JavaScript origins" should follow the format `http[s]://[:]` + - "Authorized redirect URIs" should follow the format `http[s]://[:]/oauth/complete/google-oauth2/` +5. Copy the "Client ID" and "Client Secret" values somewhere convenient. + +!!! note + Google requires the NetBox hostname to use a public top-level-domain (e.g. `.com`, `.net`). The use of IP addresses is not permitted (except `127.0.0.1`). + +For more information, consult [Google's documentation](https://developers.google.com/identity/protocols/oauth2/web-server#prerequisites). + +## NetBox Configuration + +### 1. Enter configuration parameters + +Enter the following configuration parameters in `configuration.py`, substituting your own values: + +```python +REMOTE_AUTH_BACKEND = 'social_core.backends.google.GoogleOAuth2' +SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '{CLIENT_ID}' +SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '{CLIENT_SECRET}' +``` + +### 2. Restart NetBox + +Restart the NetBox services so that the new configuration takes effect. This is typically done with the command below: + +```no-highlight +sudo systemctl restart netbox +``` + +## Testing + +Log out of NetBox if already authenticated, and click the "Log In" button at top right. You should see the normal login form as well as an option to authenticate using Google. Click that link. + +![NetBox Google login form](../../media/authentication/netbox_google_login.png) + +You should be redirected to Google's authentication portal. Enter the username/email and password of your test account to continue. You may also be prompted to grant this application access to your account. + +![NetBox Google login form](../../media/authentication/google_login_portal.png) + +If successful, you will be redirected back to the NetBox UI, and will be logged in as the Google user. You can verify this by navigating to your profile (using the button at top right). + +This user account has been replicated locally to NetBox, and can now be assigned groups and permissions. diff --git a/docs/media/authentication/google_login_portal.png b/docs/media/authentication/google_login_portal.png new file mode 100644 index 0000000000000000000000000000000000000000..55eefd872b34e857c84b878dbec5780ba6db8f07 GIT binary patch literal 28935 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

GRJp$oZK8t)g?kt_ z^=Q3?J`al`sx#l0qc51Fu_hT9!)pg#an#Q`D$^8#*E#3mmqw*eFpckiEn^kDy>EOmP1MVik9cBphkE!N8N~@2P@_6Yq-f90msyi>umW|9A6B)mxfrU9NI3S}$;HIEZzdWwyEzBMAYpf>5kf@ZzJszZ5 z)8B=L;d*jrNJXW;oZ7v;=(>ne(TZ#OGCa(4XO$IO3_?~^FQzO`0+TPzDQ(~jr%2#d zaTFkD7pi!Eu+%T-)k&7?sT5l{e6`*oOT&Mi1M)N-1dixT&m4U$eXYoOrUA-a+?M^M4VER59i+i-ELk~52N^MdmlY}G9dlfdHg|#LqWMn#Y!z13(oB^i> zxGw`B4Q{C12G`HG*YYT}%~*tE+l>df`0GTvl5SdG2dpIttqpn&je9p0(7Hyg#H6Kt zQ$5N6)q6|MNvN*_m zF3>K<$!tDy<-^NtZ#t|;ha{LE`5OmBlm1c-6B3dvRCR4Kq1Cf!d2n`JVXiVG zJr>SL?d{E(5oQ@fq~sPFU&Fu;f>o(ccYnf%d4-n**?%n>0|ge~(pKKr7pQ^Whj4t* z<$c>^-J-+nlhk6>S@+@6cxy!O17SgtJR||LZdXzKv<_9qy6JUh37879%2n$C=y}23 z*U+zSUas$#_sGID$6;yfQ^ zH;>r8^{|(YYvv*fg^btd0-D~u9q7cym#%?x74(Mz8or!IDH(XbDPTy7yN3xawHW#c zK*Q%4;}u1MEje(!XJgh>D{Mo5pCl5aG7fcBYI}L&_ zD4p@N3>XMg+ceua3CsEj6vn31en3Au7ZJ7hN1L_P&t_HEp-#k@#c*;!*Q2DCv-CUD zExZdUY&b0v9IF#;@i`#I*ld^t3!Qy`cujz|g+!7|501;NPm=EOzyM$DtO$3K=?SF^ z(15(<51U)H2V;+1_$rcKy{c7G<+$~|1Voa2#;xYWaKUQ4_cXds*!?u!ku!vbzj~z< zcm#GAm^I*I44r+VTMynWbbr|2yPgQj4O16Gxlgq(Bvt~atJiFp>)Y4jmF_eN-CvDm zf33mpAFjzhRK2)^TfvW?JjG}LSU|zq2rw?gi?93OwMqaBgcbeVSrhi1e%5w|+8PuY z+IdtYI3s=^g7W-TFK2UY_Z0m*{|9r+|8p?jznt|xFktvo@(Ah`jy`cvW$k-;cOSm| E4>fZ3bN~PV literal 0 HcmV?d00001 diff --git a/docs/media/authentication/netbox_google_login.png b/docs/media/authentication/netbox_google_login.png new file mode 100644 index 0000000000000000000000000000000000000000..730173b5d3f7889dcaab0a3e33fde7630c8ac910 GIT binary patch literal 20774 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 E0Nc=_jQ{`u literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index 94a4edcb3..00e03a4ce 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -156,6 +156,7 @@ nav: - Administration: - Authentication: - Overview: 'administration/authentication/overview.md' + - Google: 'administration/authentication/google.md' - Microsoft Entra ID: 'administration/authentication/microsoft-entra-id.md' - Okta: 'administration/authentication/okta.md' - Permissions: 'administration/permissions.md' From 5ddbacaa1f125eca2949ace2f9ea47d11fb2ef1d Mon Sep 17 00:00:00 2001 From: atownson <52260120+atownson@users.noreply.github.com> Date: Fri, 18 Oct 2024 08:49:17 -0500 Subject: [PATCH 14/18] Fixes #17802 - Added opaque background to Rename buttons (#17805) * Added btn-float class to the Rename button * Added btn-float class to the Rename button --- netbox/templates/dcim/component_list.html | 2 +- netbox/templates/dcim/device_list.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/templates/dcim/component_list.html b/netbox/templates/dcim/component_list.html index 6351643e8..6f91aff3e 100644 --- a/netbox/templates/dcim/component_list.html +++ b/netbox/templates/dcim/component_list.html @@ -10,7 +10,7 @@ {% endif %} {% if 'bulk_rename' in actions %} {% with bulk_rename_view=model|validated_viewname:"bulk_rename" %} - {% endwith %} diff --git a/netbox/templates/dcim/device_list.html b/netbox/templates/dcim/device_list.html index 41ef8fc73..493b652f5 100644 --- a/netbox/templates/dcim/device_list.html +++ b/netbox/templates/dcim/device_list.html @@ -78,7 +78,7 @@ {% if 'bulk_edit' in actions %}

{% bulk_edit_button model query_params=request.GET %} -
From ac9f561372c02a7708cb859a06bf2bf67239d15d Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Fri, 18 Oct 2024 15:44:17 +0200 Subject: [PATCH 15/18] Fix social auth for Entra ID Previously Azure AD was renamed to Entra ID. However, as django social auth didn't change its API, just the display names must be changed but not the API names. --- docs/administration/authentication/microsoft-entra-id.md | 2 +- netbox/netbox/authentication/__init__.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/administration/authentication/microsoft-entra-id.md b/docs/administration/authentication/microsoft-entra-id.md index 3451c656f..b44499fbe 100644 --- a/docs/administration/authentication/microsoft-entra-id.md +++ b/docs/administration/authentication/microsoft-entra-id.md @@ -16,7 +16,7 @@ Under the Azure Active Directory dashboard, navigate to **Add > App registration Enter a name for the registration (e.g. "NetBox") and ensure that the "single tenant" option is selected. -Under "Redirect URI", select "Web" for the platform and enter the path to your NetBox installation, ending with `/oauth/complete/entraid-oauth2/`. Note that this URI **must** begin with `https://` unless you are referencing localhost (for development purposes). +Under "Redirect URI", select "Web" for the platform and enter the path to your NetBox installation, ending with `/oauth/complete/azuread-oauth2/`. Note that this URI **must** begin with `https://` unless you are referencing localhost (for development purposes). ![App registration parameters](../../media/authentication/azure_ad_app_registration.png) diff --git a/netbox/netbox/authentication/__init__.py b/netbox/netbox/authentication/__init__.py index f80454f99..7c2df4200 100644 --- a/netbox/netbox/authentication/__init__.py +++ b/netbox/netbox/authentication/__init__.py @@ -20,10 +20,10 @@ AUTH_BACKEND_ATTRS = { 'amazon': ('Amazon AWS', 'aws'), 'apple': ('Apple', 'apple'), 'auth0': ('Auth0', None), - 'entraid-oauth2': ('Microsoft Entra ID', 'microsoft'), - 'entraid-b2c-oauth2': ('Microsoft Entra ID', 'microsoft'), - 'entraid-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), - 'entraid-v2-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-b2c-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-v2-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), 'bitbucket': ('BitBucket', 'bitbucket'), 'bitbucket-oauth2': ('BitBucket', 'bitbucket'), 'digitalocean': ('DigitalOcean', 'digital-ocean'), From d8c5147e02324405d8491291e43a0a2d6ec277e0 Mon Sep 17 00:00:00 2001 From: bctiemann Date: Fri, 18 Oct 2024 10:47:05 -0400 Subject: [PATCH 16/18] Fixes: #17732 - Add a background-color to img elements in docs to ensure readability in dark mode (#17790) * Add a background-color to img elements in docs to ensure readability in dark mode * Limit style changes to those within CMS content blocks; update colors of main netbox_logo.svg * Add a white stroke to the main logo * Add light & dark mode versions of the NetBox logo --------- Co-authored-by: Jeremy Stretch --- docs/development/style-guide.md | 2 +- docs/extra.css | 4 ++++ docs/index.md | 3 ++- docs/netbox_logo_dark.svg | 24 +++++++++++++++++++ ...{netbox_logo.svg => netbox_logo_light.svg} | 0 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 docs/netbox_logo_dark.svg rename docs/{netbox_logo.svg => netbox_logo_light.svg} (100%) diff --git a/docs/development/style-guide.md b/docs/development/style-guide.md index 0d4caf395..9d6630de0 100644 --- a/docs/development/style-guide.md +++ b/docs/development/style-guide.md @@ -76,4 +76,4 @@ When adding a new dependency, a short description of the package and the URL of * When referring to NetBox in writing, use the proper form "NetBox," with the letters N and B capitalized. The lowercase form "netbox" should be used in code, filenames, etc. but never "Netbox" or any other deviation. -* There is an SVG form of the NetBox logo at [docs/netbox_logo.svg](../netbox_logo.svg). It is preferred to use this logo for all purposes as it scales to arbitrary sizes without loss of resolution. If a raster image is required, the SVG logo should be converted to a PNG image of the prescribed size. +* There are SVG forms of the NetBox logo for both [light mode](../netbox_logo_light.svg) and [dark mode](../netbox_logo_dark.svg) available. It is preferred to use the SVG logo for all purposes as it scales to arbitrary sizes without loss of resolution. If a raster image is required, the SVG logo should be converted to a PNG image of the desired size. diff --git a/docs/extra.css b/docs/extra.css index e953fa14c..4b8cd87fe 100644 --- a/docs/extra.css +++ b/docs/extra.css @@ -5,6 +5,10 @@ img { margin-right: auto; } +.md-content img { + background-color: rgba(255, 255, 255, 0.64); +} + /* Tables */ table { margin-bottom: 24px; diff --git a/docs/index.md b/docs/index.md index 5ef650ca6..a79ab03b4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,5 @@ -![NetBox](netbox_logo.svg "NetBox logo"){style="height: 100px; margin-bottom: 3em"} +![NetBox](netbox_logo_light.svg#only-light "NetBox logo"){style="height: 100px; margin-bottom: 3em; background: none;"} +![NetBox](netbox_logo_dark.svg#only-dark "NetBox logo"){style="height: 100px; margin-bottom: 3em; background: none;"} # The Premier Network Source of Truth diff --git a/docs/netbox_logo_dark.svg b/docs/netbox_logo_dark.svg new file mode 100644 index 000000000..958a1d401 --- /dev/null +++ b/docs/netbox_logo_dark.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/netbox_logo.svg b/docs/netbox_logo_light.svg similarity index 100% rename from docs/netbox_logo.svg rename to docs/netbox_logo_light.svg From e13bc0694d02214e716773e6c43804cb2c645556 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Fri, 18 Oct 2024 07:48:15 -0700 Subject: [PATCH 17/18] 17374 correct background color in dark mode for active list item (#17792) Co-authored-by: Jeremy Stretch --- netbox/project-static/dist/netbox.css | Bin 554652 -> 554811 bytes .../styles/overrides/_tabler.scss | 5 +++++ 2 files changed, 5 insertions(+) diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css index 081c9aa6326820ac4c82d6f60f7b869b85a59d4e..cd66ffa0fc5dd4feecb08942203ae83d8d2c3549 100644 GIT binary patch delta 99 zcmbR9R&n<`#fBEf7N!>F7M2#)7Pc1lEgY#4)9tf4#klDmSbMnJB3HHpqjMSpc5+GY-@`Mg{7=L?81c!wj00lQA^Z)<= delta 31 mcmdn}PI1m##fBEf7N!>F7M2#)7Pc1lEgY#4+gTzx%;f;aFbbCd diff --git a/netbox/project-static/styles/overrides/_tabler.scss b/netbox/project-static/styles/overrides/_tabler.scss index 252da8f4a..6f7c7cc8c 100644 --- a/netbox/project-static/styles/overrides/_tabler.scss +++ b/netbox/project-static/styles/overrides/_tabler.scss @@ -131,6 +131,11 @@ body[data-bs-theme=dark] { .toast { color: var(--#{$prefix}body-color); } + .table-primary { + --tblr-table-bg: rgba(var(--tblr-secondary-rgb), 0.48); + --tblr-table-hover-bg: inherit; + --tblr-table-hover-color: inherit; + } } // Do not apply padding to elements inside a

From a2cd4d09834945d8d914d423b5e41745feca6d5e Mon Sep 17 00:00:00 2001
From: Arthur Hanson 
Date: Fri, 18 Oct 2024 07:55:17 -0700
Subject: [PATCH 18/18] 17635 fix script AbortTransaction (#17764)

* 17635 fix script AbortTransaction

* 17635 review changes
---
 netbox/extras/jobs.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/netbox/extras/jobs.py b/netbox/extras/jobs.py
index 64a7d6a69..a913fe456 100644
--- a/netbox/extras/jobs.py
+++ b/netbox/extras/jobs.py
@@ -49,7 +49,6 @@ class ScriptJob(JobRunner):
                 script.log_info(message=_("Database changes have been reverted automatically."))
                 if script.failed:
                     logger.warning("Script failed")
-                    raise
 
         except Exception as e:
             if type(e) is AbortScript: