Merge pull request #17478 from netbox-community/develop

Release v4.1.1
This commit is contained in:
Jeremy Stretch 2024-09-12 14:51:13 -04:00 committed by GitHub
commit 0e34fba922
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
118 changed files with 56655 additions and 48497 deletions

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.1.0 placeholder: v4.1.1
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -26,7 +26,7 @@ body:
attributes: attributes:
label: NetBox Version label: NetBox Version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.1.0 placeholder: v4.1.1
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -45,6 +45,7 @@ jobs:
This PR has been automatically closed due to lack of activity. This PR has been automatically closed due to lack of activity.
days-before-pr-stale: 15 days-before-pr-stale: 15
days-before-pr-close: 15 days-before-pr-close: 15
exempt-pr-labels: 'status: blocked'
stale-pr-label: 'pending closure' stale-pr-label: 'pending closure'
stale-pr-message: > stale-pr-message: >
This PR has been automatically marked as stale because it has not had This PR has been automatically marked as stale because it has not had

View File

@ -84,10 +84,6 @@ Jinja2
# https://python-markdown.github.io/changelog/ # https://python-markdown.github.io/changelog/
Markdown Markdown
# File inclusion plugin for Python-Markdown
# https://github.com/cmacmackin/markdown-include
markdown-include
# MkDocs Material theme (for documentation build) # MkDocs Material theme (for documentation build)
# https://squidfunk.github.io/mkdocs-material/changelog/ # https://squidfunk.github.io/mkdocs-material/changelog/
mkdocs-material mkdocs-material

View File

@ -20,7 +20,7 @@
Alias /static /opt/netbox/netbox/static Alias /static /opt/netbox/netbox/static
<Directory /opt/netbox/netbox/static> <Directory /opt/netbox/netbox/static>
Options Indexes FollowSymLinks MultiViews Options FollowSymLinks MultiViews
AllowOverride None AllowOverride None
Require all granted Require all granted
</Directory> </Directory>

View File

@ -149,6 +149,7 @@
"nema-l15-60p", "nema-l15-60p",
"nema-l21-20p", "nema-l21-20p",
"nema-l21-30p", "nema-l21-30p",
"nema-l22-20p",
"nema-l22-30p", "nema-l22-30p",
"cs6361c", "cs6361c",
"cs6365c", "cs6365c",
@ -262,6 +263,7 @@
"nema-l15-60r", "nema-l15-60r",
"nema-l21-20r", "nema-l21-20r",
"nema-l21-30r", "nema-l21-30r",
"nema-l22-20r",
"nema-l22-30r", "nema-l22-30r",
"CS6360C", "CS6360C",
"CS6364C", "CS6364C",
@ -518,6 +520,14 @@
"urm-p4", "urm-p4",
"urm-p8", "urm-p8",
"splice", "splice",
"usb-a",
"usb-b",
"usb-c",
"usb-mini-a",
"usb-mini-b",
"usb-micro-a",
"usb-micro-b",
"usb-micro-ab",
"other" "other"
] ]
} }
@ -575,6 +585,14 @@
"urm-p4", "urm-p4",
"urm-p8", "urm-p8",
"splice", "splice",
"usb-a",
"usb-b",
"usb-c",
"usb-mini-a",
"usb-mini-b",
"usb-micro-a",
"usb-micro-b",
"usb-micro-ab",
"other" "other"
] ]
} }

View File

@ -17,6 +17,9 @@ They can also be used as a mechanism for validating the integrity of data within
Custom scripts are Python code which exists outside the NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're completely custom, there is no inherent limitation on what a script can accomplish. Custom scripts are Python code which exists outside the NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're completely custom, there is no inherent limitation on what a script can accomplish.
!!! danger "Only install trusted scripts"
Custom scripts have unrestricted access to change anything in the databse and are inherently unsafe and should only be installed and run from trusted sources. You should also review and set permissions for who can run scripts if the script can modify any data.
## Writing Custom Scripts ## Writing Custom Scripts
All custom scripts must inherit from the `extras.scripts.Script` base class. This class provides the functionality necessary to generate forms and log activity. All custom scripts must inherit from the `extras.scripts.Script` base class. This class provides the functionality necessary to generate forms and log activity.

View File

@ -39,6 +39,10 @@ mkdocs serve
Follow these instructions to perform a new installation of NetBox in a temporary environment. This process must not be automated: The goal of this step is to catch any errors or omissions in the documentation, and ensure that it is kept up-to-date for each release. Make any necessary changes to the documentation before proceeding with the release. Follow these instructions to perform a new installation of NetBox in a temporary environment. This process must not be automated: The goal of this step is to catch any errors or omissions in the documentation, and ensure that it is kept up-to-date for each release. Make any necessary changes to the documentation before proceeding with the release.
### Test Upgrade Paths
Upgrading from a previous version typically involves database migrations, which must work without errors. Supported upgrade paths include from one minor version to another within the same major version (i.e. 4.0 to 4.1), as well as from the latest patch version of the previous minor version (i.e. 3.7 to 4.0 or to 4.1). Prior to release, test all these supported paths by loading demo data from the source version and performing a `./manage.py migrate`.
### Merge the Release Branch ### Merge the Release Branch
Submit a pull request to merge the `feature` branch into the `develop` branch in preparation for its release. Once it has been merged, continue with the section for patch releases below. Submit a pull request to merge the `feature` branch into the `develop` branch in preparation for its release. Once it has been merged, continue with the section for patch releases below.

View File

@ -1,5 +1,31 @@
# NetBox v4.1 # NetBox v4.1
## v4.1.1 (2024-09-12)
### Enhancements
* [#16926](https://github.com/netbox-community/netbox/issues/16926) - Add USB front & rear port types
* [#17347](https://github.com/netbox-community/netbox/issues/17347) - Add NEMA L22-20 power port & outlet types
### Bug Fixes
* [#17066](https://github.com/netbox-community/netbox/issues/17066) - Fix OpenAPI schema definition for custom scripts REST API endpoint
* [#17332](https://github.com/netbox-community/netbox/issues/17332) - Restore pagination for object list dashboard widgets
* [#17333](https://github.com/netbox-community/netbox/issues/17333) - Avoid prefetching all jobs when retrieving custom scripts via the REST API
* [#17353](https://github.com/netbox-community/netbox/issues/17353) - Fix styling of map buttons under site and device views
* [#17354](https://github.com/netbox-community/netbox/issues/17354) - Prevent object & multi-object custom fields from breaking bulk import forms
* [#17362](https://github.com/netbox-community/netbox/issues/17362) - Remove duplicate prefixes & IP addresses returned by the `present_in_vrf` query filter
* [#17364](https://github.com/netbox-community/netbox/issues/17364) - Fix rendering of Markdown tables inside object list dashboard widgets
* [#17387](https://github.com/netbox-community/netbox/issues/17387) - Fix display of the changelog tab for users with sufficient permission
* [#17410](https://github.com/netbox-community/netbox/issues/17410) - Enable debug toolbar middleware for `strawberry-django` only when `DEBUG` is true
* [#17414](https://github.com/netbox-community/netbox/issues/17414) - Fix support for declaring individual VLAN IDs within a VLAN group
* [#17431](https://github.com/netbox-community/netbox/issues/17431) - Fix database migration error when upgrading to v4.1 from v3.7 or earlier
* [#17437](https://github.com/netbox-community/netbox/issues/17437) - Fix exception when specifying a bridge relationship on an interface template
* [#17444](https://github.com/netbox-community/netbox/issues/17444) - Custom script fails to execute when triggered by an event rule
* [#17457](https://github.com/netbox-community/netbox/issues/17457) - GraphQL `service_list` filter should not require a port number
---
## v4.1.0 (2024-09-03) ## v4.1.0 (2024-09-03)
### Breaking Changes ### Breaking Changes

View File

@ -72,7 +72,7 @@ class NestedInterfaceTemplateSerializer(WritableNestedSerializer):
class Meta: class Meta:
model = models.InterfaceTemplate model = models.InterfaceTemplate
fields = ['id', 'url', 'display_url', 'display', 'name'] fields = ['id', 'url', 'display', 'name']
class NestedDeviceBaySerializer(WritableNestedSerializer): class NestedDeviceBaySerializer(WritableNestedSerializer):

View File

@ -396,6 +396,7 @@ class PowerPortTypeChoices(ChoiceSet):
TYPE_NEMA_L1560P = 'nema-l15-60p' TYPE_NEMA_L1560P = 'nema-l15-60p'
TYPE_NEMA_L2120P = 'nema-l21-20p' TYPE_NEMA_L2120P = 'nema-l21-20p'
TYPE_NEMA_L2130P = 'nema-l21-30p' TYPE_NEMA_L2130P = 'nema-l21-30p'
TYPE_NEMA_L2220P = 'nema-l22-20p'
TYPE_NEMA_L2230P = 'nema-l22-30p' TYPE_NEMA_L2230P = 'nema-l22-30p'
# California style # California style
TYPE_CS6361C = 'cs6361c' TYPE_CS6361C = 'cs6361c'
@ -517,6 +518,7 @@ class PowerPortTypeChoices(ChoiceSet):
(TYPE_NEMA_L1560P, 'NEMA L15-60P'), (TYPE_NEMA_L1560P, 'NEMA L15-60P'),
(TYPE_NEMA_L2120P, 'NEMA L21-20P'), (TYPE_NEMA_L2120P, 'NEMA L21-20P'),
(TYPE_NEMA_L2130P, 'NEMA L21-30P'), (TYPE_NEMA_L2130P, 'NEMA L21-30P'),
(TYPE_NEMA_L2220P, 'NEMA L22-20P'),
(TYPE_NEMA_L2230P, 'NEMA L22-30P'), (TYPE_NEMA_L2230P, 'NEMA L22-30P'),
)), )),
(_('California Style'), ( (_('California Style'), (
@ -649,6 +651,7 @@ class PowerOutletTypeChoices(ChoiceSet):
TYPE_NEMA_L1560R = 'nema-l15-60r' TYPE_NEMA_L1560R = 'nema-l15-60r'
TYPE_NEMA_L2120R = 'nema-l21-20r' TYPE_NEMA_L2120R = 'nema-l21-20r'
TYPE_NEMA_L2130R = 'nema-l21-30r' TYPE_NEMA_L2130R = 'nema-l21-30r'
TYPE_NEMA_L2220R = 'nema-l22-20r'
TYPE_NEMA_L2230R = 'nema-l22-30r' TYPE_NEMA_L2230R = 'nema-l22-30r'
# California style # California style
TYPE_CS6360C = 'CS6360C' TYPE_CS6360C = 'CS6360C'
@ -763,6 +766,7 @@ class PowerOutletTypeChoices(ChoiceSet):
(TYPE_NEMA_L1560R, 'NEMA L15-60R'), (TYPE_NEMA_L1560R, 'NEMA L15-60R'),
(TYPE_NEMA_L2120R, 'NEMA L21-20R'), (TYPE_NEMA_L2120R, 'NEMA L21-20R'),
(TYPE_NEMA_L2130R, 'NEMA L21-30R'), (TYPE_NEMA_L2130R, 'NEMA L21-30R'),
(TYPE_NEMA_L2220R, 'NEMA L22-20R'),
(TYPE_NEMA_L2230R, 'NEMA L22-30R'), (TYPE_NEMA_L2230R, 'NEMA L22-30R'),
)), )),
(_('California Style'), ( (_('California Style'), (
@ -1347,6 +1351,14 @@ class PortTypeChoices(ChoiceSet):
TYPE_URM_P2 = 'urm-p2' TYPE_URM_P2 = 'urm-p2'
TYPE_URM_P4 = 'urm-p4' TYPE_URM_P4 = 'urm-p4'
TYPE_URM_P8 = 'urm-p8' TYPE_URM_P8 = 'urm-p8'
TYPE_USB_A = 'usb-a'
TYPE_USB_B = 'usb-b'
TYPE_USB_C = 'usb-c'
TYPE_USB_MINI_A = 'usb-mini-a'
TYPE_USB_MINI_B = 'usb-mini-b'
TYPE_USB_MICRO_A = 'usb-micro-a'
TYPE_USB_MICRO_B = 'usb-micro-b'
TYPE_USB_MICRO_AB = 'usb-micro-ab'
TYPE_OTHER = 'other' TYPE_OTHER = 'other'
CHOICES = ( CHOICES = (
@ -1406,6 +1418,19 @@ class PortTypeChoices(ChoiceSet):
(TYPE_SPLICE, 'Splice'), (TYPE_SPLICE, 'Splice'),
), ),
), ),
(
_('USB'),
(
(TYPE_USB_A, 'USB Type A'),
(TYPE_USB_B, 'USB Type B'),
(TYPE_USB_C, 'USB Type C'),
(TYPE_USB_MINI_A, 'USB Mini A'),
(TYPE_USB_MINI_B, 'USB Mini B'),
(TYPE_USB_MICRO_A, 'USB Micro A'),
(TYPE_USB_MICRO_B, 'USB Micro B'),
(TYPE_USB_MICRO_AB, 'USB Micro AB'),
),
),
( (
_('Other'), _('Other'),
( (
@ -1444,6 +1469,7 @@ class CableTypeChoices(ChoiceSet):
TYPE_SMF_OS2 = 'smf-os2' TYPE_SMF_OS2 = 'smf-os2'
TYPE_AOC = 'aoc' TYPE_AOC = 'aoc'
TYPE_POWER = 'power' TYPE_POWER = 'power'
TYPE_USB = 'usb'
CHOICES = ( CHOICES = (
( (
@ -1476,6 +1502,7 @@ class CableTypeChoices(ChoiceSet):
(TYPE_AOC, 'Active Optical Cabling (AOC)'), (TYPE_AOC, 'Active Optical Cabling (AOC)'),
), ),
), ),
(TYPE_USB, _('USB')),
(TYPE_POWER, _('Power')), (TYPE_POWER, _('Power')),
) )

View File

@ -975,8 +975,8 @@ class InterfaceTemplateForm(ModularComponentTemplateForm):
queryset=InterfaceTemplate.objects.all(), queryset=InterfaceTemplate.objects.all(),
required=False, required=False,
query_params={ query_params={
'devicetype_id': '$device_type', 'device_type_id': '$device_type',
'moduletype_id': '$module_type', 'module_type_id': '$module_type',
} }
) )

View File

@ -2,6 +2,7 @@ from django.http import Http404
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django_rq.queues import get_connection from django_rq.queues import get_connection
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import status from rest_framework import status
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
@ -229,9 +230,13 @@ class ConfigTemplateViewSet(SyncedDataMixin, ConfigTemplateRenderMixin, NetBoxMo
# Scripts # Scripts
# #
@extend_schema_view(
update=extend_schema(request=serializers.ScriptInputSerializer),
partial_update=extend_schema(request=serializers.ScriptInputSerializer),
)
class ScriptViewSet(ModelViewSet): class ScriptViewSet(ModelViewSet):
permission_classes = [IsAuthenticatedOrLoginNotRequired] permission_classes = [IsAuthenticatedOrLoginNotRequired]
queryset = Script.objects.prefetch_related('jobs') queryset = Script.objects.all()
serializer_class = serializers.ScriptSerializer serializer_class = serializers.ScriptSerializer
filterset_class = filtersets.ScriptFilterSet filterset_class = filtersets.ScriptFilterSet

View File

@ -525,23 +525,29 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
elif self.type == CustomFieldTypeChoices.TYPE_OBJECT: elif self.type == CustomFieldTypeChoices.TYPE_OBJECT:
model = self.related_object_type.model_class() model = self.related_object_type.model_class()
field_class = CSVModelChoiceField if for_csv_import else DynamicModelChoiceField field_class = CSVModelChoiceField if for_csv_import else DynamicModelChoiceField
field = field_class( kwargs = {
queryset=model.objects.all(), 'queryset': model.objects.all(),
required=required, 'required': required,
initial=initial, 'initial': initial,
query_params=self.related_object_filter }
) if not for_csv_import:
kwargs['query_params'] = self.related_object_filter
field = field_class(**kwargs)
# Multiple objects # Multiple objects
elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT: elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
model = self.related_object_type.model_class() model = self.related_object_type.model_class()
field_class = CSVModelMultipleChoiceField if for_csv_import else DynamicModelMultipleChoiceField field_class = CSVModelMultipleChoiceField if for_csv_import else DynamicModelMultipleChoiceField
field = field_class( kwargs = {
queryset=model.objects.all(), 'queryset': model.objects.all(),
required=required, 'required': required,
initial=initial, 'initial': initial,
query_params=self.related_object_filter }
) if not for_csv_import:
kwargs['query_params'] = self.related_object_filter
field = field_class(**kwargs)
# Text # Text
else: else:

View File

@ -458,7 +458,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
return queryset.filter( return queryset.filter(
Q(vrf=vrf) | Q(vrf=vrf) |
Q(vrf__export_targets__in=vrf.import_targets.all()) Q(vrf__export_targets__in=vrf.import_targets.all())
) ).distinct()
class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet): class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
@ -738,7 +738,7 @@ class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
return queryset.filter( return queryset.filter(
Q(vrf=vrf) | Q(vrf=vrf) |
Q(vrf__export_targets__in=vrf.import_targets.all()) Q(vrf__export_targets__in=vrf.import_targets.all())
) ).distinct()
def filter_device(self, queryset, name, value): def filter_device(self, queryset, name, value):
devices = Device.objects.filter(**{'{}__in'.format(name): value}) devices = Device.objects.filter(**{'{}__in'.format(name): value})

View File

@ -100,7 +100,7 @@ class VLANGroup(OrganizationalModel):
if self.vid_ranges and check_ranges_overlap(self.vid_ranges): if self.vid_ranges and check_ranges_overlap(self.vid_ranges):
raise ValidationError({'vid_ranges': _("Ranges cannot overlap.")}) raise ValidationError({'vid_ranges': _("Ranges cannot overlap.")})
for vid_range in self.vid_ranges: for vid_range in self.vid_ranges:
if vid_range.lower >= vid_range.upper: if vid_range.lower > vid_range.upper:
raise ValidationError({ raise ValidationError({
'vid_ranges': _( 'vid_ranges': _(
"Maximum child VID must be greater than or equal to minimum child VID ({value})" "Maximum child VID must be greater than or equal to minimum child VID ({value})"

View File

@ -543,3 +543,17 @@ class TestVLANGroup(TestCase):
vlan = VLAN(vid=109, name='VLAN 109', group=vlangroup) vlan = VLAN(vid=109, name='VLAN 109', group=vlangroup)
vlan.full_clean() vlan.full_clean()
def test_overlapping_vlan(self):
vlangroup = VLANGroup(
name='VLAN Group 1',
slug='vlan-group-1',
vid_ranges=string_to_ranges('2-4,3-5'),
)
with self.assertRaises(ValidationError):
vlangroup.full_clean()
# make sure single vlan range works
vlangroup.vid_ranges = string_to_ranges('2-2')
vlangroup.full_clean()
vlangroup.save()

View File

@ -47,7 +47,7 @@ def map_strawberry_type(field):
pass pass
elif isinstance(field, NumericArrayFilter): elif isinstance(field, NumericArrayFilter):
should_create_function = True should_create_function = True
attr_type = int attr_type = int | None
elif isinstance(field, TreeNodeMultipleChoiceFilter): elif isinstance(field, TreeNodeMultipleChoiceFilter):
should_create_function = True should_create_function = True
attr_type = List[str] | None attr_type = List[str] | None

View File

@ -100,7 +100,8 @@ class JobRunner(ABC):
This method is a wrapper of `Job.enqueue()` using `handle()` as function callback. See its documentation for This method is a wrapper of `Job.enqueue()` using `handle()` as function callback. See its documentation for
parameters. parameters.
""" """
return Job.enqueue(cls.handle, name=cls.name, *args, **kwargs) name = kwargs.pop('name', None) or cls.name
return Job.enqueue(cls.handle, name=name, *args, **kwargs)
@classmethod @classmethod
@advisory_lock(ADVISORY_LOCK_KEYS['job-schedules']) @advisory_lock(ADVISORY_LOCK_KEYS['job-schedules'])

View File

@ -416,7 +416,6 @@ if not DJANGO_ADMIN_ENABLED:
# Middleware # Middleware
MIDDLEWARE = [ MIDDLEWARE = [
"strawberry_django.middlewares.debug_toolbar.DebugToolbarMiddleware",
'corsheaders.middleware.CorsMiddleware', 'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware', 'django.middleware.locale.LocaleMiddleware',
@ -431,6 +430,13 @@ MIDDLEWARE = [
'netbox.middleware.CoreMiddleware', 'netbox.middleware.CoreMiddleware',
'netbox.middleware.MaintenanceModeMiddleware', 'netbox.middleware.MaintenanceModeMiddleware',
] ]
if DEBUG:
MIDDLEWARE = [
"strawberry_django.middlewares.debug_toolbar.DebugToolbarMiddleware",
*MIDDLEWARE,
]
if METRICS_ENABLED: if METRICS_ENABLED:
# If metrics are enabled, add the before & after Prometheus middleware # If metrics are enabled, add the before & after Prometheus middleware
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -38,7 +38,7 @@ class ObjectChangeLogView(ConditionalLoginRequiredMixin, View):
base_template = None base_template = None
tab = ViewTab( tab = ViewTab(
label=_('Changelog'), label=_('Changelog'),
permission='extras.view_objectchange', permission='core.view_objectchange',
weight=10000 weight=10000
) )

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -22,7 +22,7 @@
"validate:formatting:scripts": "prettier -c src/**/*.ts" "validate:formatting:scripts": "prettier -c src/**/*.ts"
}, },
"dependencies": { "dependencies": {
"@fontsource-variable/plus-jakarta-sans": "^5.0.22", "@fontsource-variable/plus-jakarta-sans": "^5.1.0",
"@mdi/font": "7.4.47", "@mdi/font": "7.4.47",
"@tabler/core": "1.0.0-beta20", "@tabler/core": "1.0.0-beta20",
"bootstrap": "5.3.3", "bootstrap": "5.3.3",
@ -31,7 +31,7 @@
"gridstack": "10.3.1", "gridstack": "10.3.1",
"htmx.org": "1.9.12", "htmx.org": "1.9.12",
"query-string": "9.1.0", "query-string": "9.1.0",
"sass": "1.77.8", "sass": "1.78.0",
"tom-select": "2.3.1", "tom-select": "2.3.1",
"typeface-roboto-mono": "1.1.13" "typeface-roboto-mono": "1.1.13"
}, },

View File

@ -200,10 +200,10 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.2.tgz#d8bae93ac8b815b2bd7a98078cf91e2724ef11e5" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.2.tgz#d8bae93ac8b815b2bd7a98078cf91e2724ef11e5"
integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw== integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==
"@fontsource-variable/plus-jakarta-sans@^5.0.22": "@fontsource-variable/plus-jakarta-sans@^5.1.0":
version "5.0.22" version "5.1.0"
resolved "https://registry.yarnpkg.com/@fontsource-variable/plus-jakarta-sans/-/plus-jakarta-sans-5.0.22.tgz#9f96b5f5381b93a422d35d89b0bc7871c4c06d60" resolved "https://registry.yarnpkg.com/@fontsource-variable/plus-jakarta-sans/-/plus-jakarta-sans-5.1.0.tgz#9dd5aa72277c43d408f6e34cd658d823fbef1fe1"
integrity sha512-YDV+FVn+icpvB004N3R973K/0W75FRPaEmeOoZ5nBuPAN8hizhUfTHpWpEv9FUr3C8FkPFwzrVl3gvdkzDdBxw== integrity sha512-K7o2GO7/quVFFyfjTqYZu0ng4KJGf60KSAGbvUWwKuH+/Giyl6Qe/EqnGLcjrl9AmXZzTvtRB1xpkASn8FNFoQ==
"@graphiql/plugin-explorer@3.2.2": "@graphiql/plugin-explorer@3.2.2":
version "3.2.2" version "3.2.2"
@ -2561,7 +2561,16 @@ safe-regex-test@^1.0.3:
es-errors "^1.3.0" es-errors "^1.3.0"
is-regex "^1.1.4" is-regex "^1.1.4"
sass@1.77.8, sass@^1.71.1: sass@1.78.0:
version "1.78.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.78.0.tgz#cef369b2f9dc21ea1d2cf22c979f52365da60841"
integrity sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
sass@^1.71.1:
version "1.77.8" version "1.77.8"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.8.tgz#9f18b449ea401759ef7ec1752a16373e296b52bd" resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.8.tgz#9f18b449ea401759ef7ec1752a16373e296b52bd"
integrity sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ== integrity sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==

View File

@ -1,3 +1,3 @@
version: "4.1.0" version: "4.1.1"
edition: "Community" edition: "Community"
published: "2024-09-03" published: "2024-09-12"

View File

@ -61,7 +61,7 @@
</div> </div>
</div> </div>
</div> </div>
{% if perms.extras.view_objectchange %} {% if perms.core.view_objectchange %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">

View File

@ -63,9 +63,9 @@
<td class="position-relative"> <td class="position-relative">
{% if object.latitude and object.longitude %} {% if object.latitude and object.longitude %}
{% if config.MAPS_URL %} {% if config.MAPS_URL %}
<div class="position-absolute top-50 end-0 translate-middle-y d-print-none"> <div class="position-absolute top-50 end-0 me-2 translate-middle-y d-print-none">
<a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary"> <a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary btn-sm">
<i class="mdi mdi-map-marker"></i> {% trans "Map It" %} <i class="mdi mdi-map-marker"></i> {% trans "Map" %}
</a> </a>
</div> </div>
{% endif %} {% endif %}

View File

@ -95,9 +95,9 @@
<td class="position-relative"> <td class="position-relative">
{% if object.latitude and object.longitude %} {% if object.latitude and object.longitude %}
{% if config.MAPS_URL %} {% if config.MAPS_URL %}
<div class="position-absolute top-50 end-0 translate-middle-y d-print-none"> <div class="position-absolute top-50 end-0 me-2 translate-middle-y d-print-none">
<a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary"> <a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary btn-sm">
<i class="mdi mdi-map-marker"></i> {% trans "Map It" %} <i class="mdi mdi-map-marker"></i> {% trans "Map" %}
</a> </a>
</div> </div>
{% endif %} {% endif %}

View File

@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
{% if htmx_url and has_permission %} {% if htmx_url and has_permission %}
<div class="htmx-container" hx-get="{{ htmx_url }}" hx-trigger="load" hx-target="this" hx-select="table" hx-swap="innerHTML"></div> <div class="htmx-container" hx-get="{{ htmx_url }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"></div>
{% elif htmx_url %} {% elif htmx_url %}
<div class="text-muted text-center"> <div class="text-muted text-center">
<i class="mdi mdi-lock-outline"></i> {% trans "No permission to view this content" %}. <i class="mdi mdi-lock-outline"></i> {% trans "No permission to view this content" %}.

View File

@ -28,7 +28,7 @@
{% block page %} {% block page %}
{# Render the user's customized dashboard #} {# Render the user's customized dashboard #}
<div class="grid-stack m-2" id="dashboard"> <div class="grid-stack m-2" id="dashboard" hx-disinherit="*">
{% for widget in dashboard %} {% for widget in dashboard %}
{% include 'extras/dashboard/widget.html' %} {% include 'extras/dashboard/widget.html' %}
{% endfor %} {% endfor %}

View File

@ -71,7 +71,7 @@
</div> </div>
</div> </div>
</div> </div>
{% if perms.extras.view_objectchange %} {% if perms.core.view_objectchange %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More