From 63814d5a179b51bbeefb58f025e68cac7afdc18e 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 49c5626754ad495e2b09b58994e1589719dbf1e7 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 7022e6f05f796f181cadd67204ecaa662159307f 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 e64fefe5ee0dd541d16315cb00dc85b46b428b21 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 b1bef25b0566813b5bf315a4e33042d8fcec39c7 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 bc8553d5b42d0a5195ede45acf091d3ce3d80675 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 5f449cca6817931166e5ae5fb88a3eecc1314546 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 4b7c13dfbc8dd9a102acf8789dad445218aaafc2 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 7dbc6f1d2bf0e8e5e0491b40d37ea6f88424a12e 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 9eb5a36361a38c6973faa9e9c19abe5e20339d83 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 ffa98527c75b7fdd08d070734b27ac948fa85c2f 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 39d606ee8034686052b8bb8175ddbf878d77bc73 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 2d2853d422a6e480a23e5fb90296a7eaa3efedb0 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 914f867771612de4799d21abad674468ffcfc828 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 0dd797eb621ac4236c1528b74ccaca4d583e9fde 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 3dd83180572b8e8fdc70be8ea43e7dbee44a35f3 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 40ff86f1d254933bceb962e8542d277d16cccbd8 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 70adc8ac808e944fc590ae26d7fc146aa54f43fe 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: