Merge branch 'main' into feature

This commit is contained in:
Jeremy Stretch 2025-07-31 15:24:33 -04:00
commit 40dd36812c
68 changed files with 5660 additions and 4956 deletions

View File

@ -15,7 +15,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.3.4 placeholder: v4.3.5
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -27,7 +27,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.3.4 placeholder: v4.3.5
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -8,7 +8,9 @@ django-cors-headers
# Runtime UI tool for debugging Django # Runtime UI tool for debugging Django
# https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst # https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst
django-debug-toolbar # django-debug-toolbar v6.0.0 raises "Attribute Error at /: 'function' object has no attribute 'set'"
# see https://github.com/netbox-community/netbox/issues/19974
django-debug-toolbar==5.2.0
# Library for writing reusable URL query filters # Library for writing reusable URL query filters
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst # https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
@ -145,8 +147,7 @@ strawberry-graphql
# Strawberry GraphQL Django extension # Strawberry GraphQL Django extension
# https://github.com/strawberry-graphql/strawberry-django/releases # https://github.com/strawberry-graphql/strawberry-django/releases
# See #19771 strawberry-graphql-django
strawberry-graphql-django==0.60.0
# SVG image rendering (used for rack elevations) # SVG image rendering (used for rack elevations)
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst # https://github.com/mozman/svgwrite/blob/master/NEWS.rst

View File

@ -18,10 +18,10 @@ pg_dump --username netbox --password --host localhost netbox > netbox.sql
!!! note !!! note
You may need to change the username, host, and/or database in the command above to match your installation. You may need to change the username, host, and/or database in the command above to match your installation.
When replicating a production database for development purposes, you may find it convenient to exclude changelog data, which can easily account for the bulk of a database's size. To do this, exclude the `extras_objectchange` table data from the export. The table will still be included in the output file, but will not be populated with any data. When replicating a production database for development purposes, you may find it convenient to exclude changelog data, which can easily account for the bulk of a database's size. To do this, exclude the `core_objectchange` table data from the export. The table will still be included in the output file, but will not be populated with any data.
```no-highlight ```no-highlight
pg_dump ... --exclude-table-data=extras_objectchange netbox > netbox.sql pg_dump ... --exclude-table-data=core_objectchange netbox > netbox.sql
``` ```
### Load an Exported Database ### Load an Exported Database

View File

@ -290,13 +290,6 @@ Quit the server with CONTROL-C.
Next, connect to the name or IP of the server (as defined in `ALLOWED_HOSTS`) on port 8000; for example, <http://127.0.0.1:8000/>. You should be greeted with the NetBox home page. Try logging in using the username and password specified when creating a superuser. Next, connect to the name or IP of the server (as defined in `ALLOWED_HOSTS`) on port 8000; for example, <http://127.0.0.1:8000/>. You should be greeted with the NetBox home page. Try logging in using the username and password specified when creating a superuser.
!!! note
By default RHEL based distros will likely block your testing attempts with firewalld. The development server port can be opened with `firewall-cmd` (add `--permanent` if you want the rule to survive server restarts):
```no-highlight
firewall-cmd --zone=public --add-port=8000/tcp
```
!!! danger "Not for production use" !!! danger "Not for production use"
The development server is for development and testing purposes only. It is neither performant nor secure enough for production use. **Do not use it in production.** The development server is for development and testing purposes only. It is neither performant nor secure enough for production use. **Do not use it in production.**

View File

@ -24,6 +24,14 @@ Jinja2 template code, if being defined locally rather than replicated from a dat
A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior. A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior.
The `undefined` and `finalize` Jinja environment parameters, which must reference a Python class or function, can define a dotted path to the desired resource. For example:
```json
{
"undefined": "jinja2.StrictUndefined"
}
```
### MIME Type ### MIME Type
!!! info "This field was introduced in NetBox v4.3." !!! info "This field was introduced in NetBox v4.3."

View File

@ -26,6 +26,14 @@ Jinja2 template code for rendering the exported data.
A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior. A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior.
The `undefined` and `finalize` Jinja environment parameters, which must reference a Python class or function, can define a dotted path to the desired resource. For example:
```json
{
"undefined": "jinja2.StrictUndefined"
}
```
### MIME Type ### MIME Type
The MIME type to indicate in the response when rendering the export template (optional). Defaults to `text/plain`. The MIME type to indicate in the response when rendering the export template (optional). Defaults to `text/plain`.

View File

@ -80,18 +80,20 @@ GET /api/ipam/vlans/?vid__gt=900
String based (char) fields (Name, Address, etc) support these lookup expressions: String based (char) fields (Name, Address, etc) support these lookup expressions:
| Filter | Description | | Filter | Description |
|---------|----------------------------------------| |----------|----------------------------------------|
| `n` | Not equal to | | `n` | Not equal to |
| `ic` | Contains (case-insensitive) | | `ic` | Contains (case-insensitive) |
| `nic` | Does not contain (case-insensitive) | | `nic` | Does not contain (case-insensitive) |
| `isw` | Starts with (case-insensitive) | | `isw` | Starts with (case-insensitive) |
| `nisw` | Does not start with (case-insensitive) | | `nisw` | Does not start with (case-insensitive) |
| `iew` | Ends with (case-insensitive) | | `iew` | Ends with (case-insensitive) |
| `niew` | Does not end with (case-insensitive) | | `niew` | Does not end with (case-insensitive) |
| `ie` | Exact match (case-insensitive) | | `ie` | Exact match (case-insensitive) |
| `nie` | Inverse exact match (case-insensitive) | | `nie` | Inverse exact match (case-insensitive) |
| `empty` | Is empty/null (boolean) | | `empty` | Is empty/null (boolean) |
| `regex` | Regexp matching |
| `iregex` | Regexp matching (case-insensitive) |
Here is an example of a lookup expression on a string field that will return all devices with `switch` in the name: Here is an example of a lookup expression on a string field that will return all devices with `switch` in the name:

View File

@ -1,5 +1,21 @@
# NetBox v4.3 # NetBox v4.3
## v4.3.5 (2025-07-29)
### Enhancements
* [#18797](https://github.com/netbox-community/netbox/issues/18797) - Added jinja2.StrictUndefined option for config template rendering to catch undefined variables
* [#18936](https://github.com/netbox-community/netbox/issues/18936) - Cable imports now accept color names (e.g. "red", "blue") in addition to hex color codes
* [#19840](https://github.com/netbox-community/netbox/issues/19840) - Cable imports now support specifying site information for better organization
* [#19902](https://github.com/netbox-community/netbox/issues/19902) - Device names in rack elevation SVG exports are automatically truncated to prevent overflow beyond rack unit boundaries
* [#19903](https://github.com/netbox-community/netbox/issues/19903) - String field filters now support `regex` and `iregex` lookups for advanced pattern matching
* [#19910](https://github.com/netbox-community/netbox/issues/19910) - Internet-dependent links are no longer visible when running in air-gapped environments
### Bug Fixes
* [#18900](https://github.com/netbox-community/netbox/issues/18900) - REST API paginator now raises proper exceptions when attempting to paginate unordered querysets
* [#19916](https://github.com/netbox-community/netbox/issues/19916) - Rack elevation image/label dropdown functionality restored
* [#19934](https://github.com/netbox-community/netbox/issues/19934) - Added missing description field to tenant bulk edit form
* [#19956](https://github.com/netbox-community/netbox/issues/19956) - Prevent duplicate deletion records in changelog from cascading deletions
## v4.3.4 (2025-07-15) ## v4.3.4 (2025-07-15)
### Enhancements ### Enhancements

View File

@ -1,30 +1,29 @@
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_rq.queues import get_redis_connection
from django_rq.settings import QUEUES_LIST
from django_rq.utils import get_statistics
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.routers import APIRootView from rest_framework.routers import APIRootView
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
from rq.job import Job as RQ_Job
from rq.worker import Worker
from core import filtersets from core import filtersets
from core.choices import DataSourceStatusChoices
from core.jobs import SyncDataSourceJob from core.jobs import SyncDataSourceJob
from core.models import * from core.models import *
from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs, requeue_rq_job, stop_rq_job from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs, requeue_rq_job, stop_rq_job
from django_rq.queues import get_redis_connection
from django_rq.utils import get_statistics
from django_rq.settings import QUEUES_LIST
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from netbox.api.metadata import ContentTypeMetadata from netbox.api.metadata import ContentTypeMetadata
from netbox.api.pagination import LimitOffsetListPagination from netbox.api.pagination import LimitOffsetListPagination
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
from rest_framework import viewsets
from rest_framework.permissions import IsAdminUser
from rq.job import Job as RQ_Job
from rq.worker import Worker
from . import serializers from . import serializers
@ -51,10 +50,8 @@ class DataSourceViewSet(NetBoxModelViewSet):
if not request.user.has_perm('core.sync_datasource', obj=datasource): if not request.user.has_perm('core.sync_datasource', obj=datasource):
raise PermissionDenied(_("This user does not have permission to synchronize this data source.")) raise PermissionDenied(_("This user does not have permission to synchronize this data source."))
# Enqueue the sync job & update the DataSource's status # Enqueue the sync job
SyncDataSourceJob.enqueue(instance=datasource, user=request.user) SyncDataSourceJob.enqueue(instance=datasource, user=request.user)
datasource.status = DataSourceStatusChoices.QUEUED
DataSource.objects.filter(pk=datasource.pk).update(status=datasource.status)
serializer = serializers.DataSourceSerializer(datasource, context={'request': request}) serializer = serializers.DataSourceSerializer(datasource, context={'request': request})

View File

@ -28,6 +28,17 @@ class SyncDataSourceJob(JobRunner):
class Meta: class Meta:
name = 'Synchronization' name = 'Synchronization'
@classmethod
def enqueue(cls, *args, **kwargs):
job = super().enqueue(*args, **kwargs)
# Update the DataSource's synchronization status to queued
if datasource := job.object:
datasource.status = DataSourceStatusChoices.QUEUED
DataSource.objects.filter(pk=datasource.pk).update(status=datasource.status)
return job
def run(self, *args, **kwargs): def run(self, *args, **kwargs):
datasource = DataSource.objects.get(pk=self.job.object_id) datasource = DataSource.objects.get(pk=self.job.object_id)
self.logger.debug(f"Found DataSource ID {datasource.pk}") self.logger.debug(f"Found DataSource ID {datasource.pk}")

View File

@ -34,7 +34,6 @@ from utilities.json import ConfigJSONEncoder
from utilities.query import count_related from utilities.query import count_related
from utilities.views import ContentTypePermissionRequiredMixin, GetRelatedModelsMixin, ViewTab, register_model_view from utilities.views import ContentTypePermissionRequiredMixin, GetRelatedModelsMixin, ViewTab, register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .choices import DataSourceStatusChoices
from .jobs import SyncDataSourceJob from .jobs import SyncDataSourceJob
from .models import * from .models import *
from .plugins import get_catalog_plugins, get_local_plugins from .plugins import get_catalog_plugins, get_local_plugins
@ -79,12 +78,8 @@ class DataSourceSyncView(BaseObjectView):
def post(self, request, pk): def post(self, request, pk):
datasource = get_object_or_404(self.queryset, pk=pk) datasource = get_object_or_404(self.queryset, pk=pk)
# Enqueue the sync job
# Enqueue the sync job & update the DataSource's status
job = SyncDataSourceJob.enqueue(instance=datasource, user=request.user) job = SyncDataSourceJob.enqueue(instance=datasource, user=request.user)
datasource.status = DataSourceStatusChoices.QUEUED
DataSource.objects.filter(pk=datasource.pk).update(status=datasource.status)
messages.success( messages.success(
request, request,
_("Queued job #{id} to sync {datasource}").format(id=job.pk, datasource=datasource) _("Queued job #{id} to sync {datasource}").format(id=job.pk, datasource=datasource)

View File

@ -1335,6 +1335,13 @@ class MACAddressImportForm(NetBoxModelImportForm):
class CableImportForm(NetBoxModelImportForm): class CableImportForm(NetBoxModelImportForm):
# Termination A # Termination A
side_a_site = CSVModelChoiceField(
label=_('Side A site'),
queryset=Site.objects.all(),
required=False,
to_field_name='name',
help_text=_('Site of parent device A (if any)'),
)
side_a_device = CSVModelChoiceField( side_a_device = CSVModelChoiceField(
label=_('Side A device'), label=_('Side A device'),
queryset=Device.objects.all(), queryset=Device.objects.all(),
@ -1353,6 +1360,13 @@ class CableImportForm(NetBoxModelImportForm):
) )
# Termination B # Termination B
side_b_site = CSVModelChoiceField(
label=_('Side B site'),
queryset=Site.objects.all(),
required=False,
to_field_name='name',
help_text=_('Site of parent device B (if any)'),
)
side_b_device = CSVModelChoiceField( side_b_device = CSVModelChoiceField(
label=_('Side B device'), label=_('Side B device'),
queryset=Device.objects.all(), queryset=Device.objects.all(),
@ -1396,14 +1410,39 @@ class CableImportForm(NetBoxModelImportForm):
required=False, required=False,
help_text=_('Length unit') help_text=_('Length unit')
) )
color = forms.CharField(
label=_('Color'),
required=False,
max_length=16,
help_text=_('Color name (e.g. "Red") or hex code (e.g. "f44336")')
)
class Meta: class Meta:
model = Cable model = Cable
fields = [ fields = [
'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type', 'side_a_site', 'side_a_device', 'side_a_type', 'side_a_name', 'side_b_site', 'side_b_device', 'side_b_type',
'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description', 'comments', 'tags', 'side_b_name', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description',
'comments', 'tags',
] ]
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
if data:
# Limit choices for side_a_device to the assigned side_a_site
if side_a_site := data.get('side_a_site'):
side_a_device_params = {f'site__{self.fields["side_a_site"].to_field_name}': side_a_site}
self.fields['side_a_device'].queryset = self.fields['side_a_device'].queryset.filter(
**side_a_device_params
)
# Limit choices for side_b_device to the assigned side_b_site
if side_b_site := data.get('side_b_site'):
side_b_device_params = {f'site__{self.fields["side_b_site"].to_field_name}': side_b_site}
self.fields['side_b_device'].queryset = self.fields['side_b_device'].queryset.filter(
**side_b_device_params
)
def _clean_side(self, side): def _clean_side(self, side):
""" """
Derive a Cable's A/B termination objects. Derive a Cable's A/B termination objects.
@ -1440,6 +1479,24 @@ class CableImportForm(NetBoxModelImportForm):
setattr(self.instance, f'{side}_terminations', [termination_object]) setattr(self.instance, f'{side}_terminations', [termination_object])
return termination_object return termination_object
def _clean_color(self, color):
"""
Derive a colors hex code
:param color: color as hex or color name
"""
color_parsed = color.strip().lower()
for hex_code, label in ColorChoices.CHOICES:
if color.lower() == label.lower():
color_parsed = hex_code
if len(color_parsed) > 6:
raise forms.ValidationError(
_(f"{color} did not match any used color name and was longer than six characters: invalid hex.")
)
return color_parsed
def clean_side_a_name(self): def clean_side_a_name(self):
return self._clean_side('a') return self._clean_side('a')
@ -1451,11 +1508,14 @@ class CableImportForm(NetBoxModelImportForm):
length_unit = self.cleaned_data.get('length_unit', None) length_unit = self.cleaned_data.get('length_unit', None)
return length_unit if length_unit is not None else '' return length_unit if length_unit is not None else ''
def clean_color(self):
color = self.cleaned_data.get('color', None)
return self._clean_color(color) if color is not None else ''
# #
# Virtual chassis # Virtual chassis
# #
class VirtualChassisImportForm(NetBoxModelImportForm): class VirtualChassisImportForm(NetBoxModelImportForm):
master = CSVModelChoiceField( master = CSVModelChoiceField(
label=_('Master'), label=_('Master'),

View File

@ -3,6 +3,7 @@ import svgwrite
from svgwrite.container import Hyperlink from svgwrite.container import Hyperlink
from svgwrite.image import Image from svgwrite.image import Image
from svgwrite.gradients import LinearGradient from svgwrite.gradients import LinearGradient
from svgwrite.masking import ClipPath
from svgwrite.shapes import Rect from svgwrite.shapes import Rect
from svgwrite.text import Text from svgwrite.text import Text
@ -67,6 +68,20 @@ def get_device_description(device):
return description return description
def truncate_text(text, width, font_size=15):
"""
Truncate text to fit within the width of a rectangle.
:param text: The text to truncate
:param width: Width of rectangle
:param font_size: Font size (default is 15, ~0.875rem)
"""
char_width = font_size * 0.6 # 0.6 is an approximation of the average character width in pixels
max_char = int(width / char_width)
return text if len(text) <= max_char else text[:max_char] + '...'
class RackElevationSVG: class RackElevationSVG:
""" """
Use this class to render a rack elevation as an SVG image. Use this class to render a rack elevation as an SVG image.
@ -177,12 +192,26 @@ class RackElevationSVG:
link = Hyperlink(href=f'{self.base_url}{device.get_absolute_url()}', target="_parent") link = Hyperlink(href=f'{self.base_url}{device.get_absolute_url()}', target="_parent")
link.set_desc(description) link.set_desc(description)
# Create clipPath element
# This is necessary as fallback because the truncate_text method is an approximation
clip_id = f"clip-{device.id}"
clip_path = ClipPath(id=clip_id)
clip_path.add(Rect(coords, size))
self.drawing.defs.add(clip_path)
# Name to display
display_name = truncate_text(name, size[0])
# Add rect element to hyperlink # Add rect element to hyperlink
if color: if color:
link.add(Rect(coords, size, style=f'fill: #{color}', class_=f'slot{css_extra}')) link.add(Rect(coords, size, style=f'fill: #{color}', class_=f'slot{css_extra}'))
else: else:
link.add(Rect(coords, size, class_=f'slot blocked{css_extra}')) link.add(Rect(coords, size, class_=f'slot blocked{css_extra}'))
link.add(Text(name, insert=text_coords, fill=text_color, class_=f'label{css_extra}')) link.add(
Text(display_name, insert=text_coords, fill=text_color, clip_path=f"url(#{clip_id})",
class_=f'label{css_extra}')
)
# Embed device type image if provided # Embed device type image if provided
if self.include_images and image: if self.include_images and image:

View File

@ -3266,17 +3266,27 @@ class CableTestCase(
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
site = Site.objects.create(name='Site 1', slug='site-1') sites = (
Site(name='Site 1', slug='site-1'),
Site(name='Site 2', slug='site-2'),
)
Site.objects.bulk_create(sites)
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
devicetype = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer) devicetype = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer)
role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
vc = VirtualChassis.objects.create(name='Virtual Chassis') vc = VirtualChassis.objects.create(name='Virtual Chassis')
# NOTE: By design, NetBox now allows for the creation of devices with the same name if they belong to
# different sites.
# The CSV test below demonstrates that devices with identical names on different sites can be created
# and referenced successfully.
devices = ( devices = (
Device(name='Device 1', site=site, device_type=devicetype, role=role), # Create 'Device 1' assigned to 'Site 1'
Device(name='Device 2', site=site, device_type=devicetype, role=role), Device(name='Device 1', site=sites[0], device_type=devicetype, role=role),
Device(name='Device 3', site=site, device_type=devicetype, role=role), Device(name='Device 2', site=sites[0], device_type=devicetype, role=role),
Device(name='Device 4', site=site, device_type=devicetype, role=role), Device(name='Device 3', site=sites[0], device_type=devicetype, role=role),
# Create 'Device 1' assigned to 'Site 2' (allowed since the site is different)
Device(name='Device 1', site=sites[1], device_type=devicetype, role=role),
) )
Device.objects.bulk_create(devices) Device.objects.bulk_create(devices)
@ -3327,13 +3337,15 @@ class CableTestCase(
'tags': [t.pk for t in tags], 'tags': [t.pk for t in tags],
} }
# Ensure that CSV bulk import supports assigning terminations from parent devices that share
# the same device name, provided those devices belong to different sites.
cls.csv_data = ( cls.csv_data = (
"side_a_device,side_a_type,side_a_name,side_b_device,side_b_type,side_b_name", "side_a_site,side_a_device,side_a_type,side_a_name,side_b_site,side_b_device,side_b_type,side_b_name",
"Device 3,dcim.interface,Interface 1,Device 4,dcim.interface,Interface 1", "Site 1,Device 3,dcim.interface,Interface 1,Site 2,Device 1,dcim.interface,Interface 1",
"Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2", "Site 1,Device 3,dcim.interface,Interface 2,Site 2,Device 1,dcim.interface,Interface 2",
"Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3", "Site 1,Device 3,dcim.interface,Interface 3,Site 2,Device 1,dcim.interface,Interface 3",
"Device 1,dcim.interface,Device 2 Interface,Device 4,dcim.interface,Interface 4", "Site 1,Device 1,dcim.interface,Device 2 Interface,Site 2,Device 1,dcim.interface,Interface 4",
"Device 1,dcim.interface,Device 3 Interface,Device 4,dcim.interface,Interface 5", "Site 1,Device 1,dcim.interface,Device 3 Interface,Site 2,Device 1,dcim.interface,Interface 5",
) )
cls.csv_update_data = ( cls.csv_update_data = (

View File

@ -184,7 +184,9 @@ class TagViewSet(NetBoxModelViewSet):
class TaggedItemViewSet(RetrieveModelMixin, ListModelMixin, BaseViewSet): class TaggedItemViewSet(RetrieveModelMixin, ListModelMixin, BaseViewSet):
queryset = TaggedItem.objects.prefetch_related('content_type', 'content_object', 'tag') queryset = TaggedItem.objects.prefetch_related(
'content_type', 'content_object', 'tag'
).order_by('tag__weight', 'tag__name')
serializer_class = serializers.TaggedItemSerializer serializer_class = serializers.TaggedItemSerializer
filterset_class = filtersets.TaggedItemFilterSet filterset_class = filtersets.TaggedItemFilterSet

View File

@ -21,6 +21,12 @@ WEBHOOK_EVENT_TYPES = {
JOB_ERRORED: 'job_ended', JOB_ERRORED: 'job_ended',
} }
# Jinja environment parameters which support path imports
JINJA_ENV_PARAMS_WITH_PATH_IMPORT = (
'undefined',
'finalize',
)
# Dashboard # Dashboard
DEFAULT_DASHBOARD = [ DEFAULT_DASHBOARD = [
{ {

View File

@ -2,16 +2,17 @@ import importlib.abc
import importlib.util import importlib.util
import os import os
import sys import sys
from django.core.files.storage import storages from django.core.files.storage import storages
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy as _
from extras.constants import DEFAULT_MIME_TYPE from extras.constants import DEFAULT_MIME_TYPE, JINJA_ENV_PARAMS_WITH_PATH_IMPORT
from extras.utils import filename_from_model, filename_from_object from extras.utils import filename_from_model, filename_from_object
from utilities.jinja2 import render_jinja2 from utilities.jinja2 import render_jinja2
__all__ = ( __all__ = (
'PythonModuleMixin', 'PythonModuleMixin',
'RenderTemplateMixin', 'RenderTemplateMixin',
@ -125,12 +126,22 @@ class RenderTemplateMixin(models.Model):
class_name=self.__class__ class_name=self.__class__
)) ))
def get_environment_params(self):
"""
Pre-processing of any defined Jinja environment parameters (e.g. to support path resolution).
"""
params = self.environment_params or {}
for name, value in params.items():
if name in JINJA_ENV_PARAMS_WITH_PATH_IMPORT and type(value) is str:
params[name] = import_string(value)
return params
def render(self, context=None, queryset=None): def render(self, context=None, queryset=None):
""" """
Render the template with the provided context. The context is passed to the Jinja2 environment as a dictionary. Render the template with the provided context. The context is passed to the Jinja2 environment as a dictionary.
""" """
context = self.get_context(context=context, queryset=queryset) context = self.get_context(context=context, queryset=queryset)
env_params = self.environment_params or {} env_params = self.get_environment_params()
output = render_jinja2(self.template_code, context, env_params, getattr(self, 'data_file', None)) output = render_jinja2(self.template_code, context, env_params, getattr(self, 'data_file', None))
# Replace CRLF-style line terminators # Replace CRLF-style line terminators

View File

@ -83,3 +83,6 @@ class TaggedItem(GenericTaggedItemBase):
indexes = [models.Index(fields=["content_type", "object_id"])] indexes = [models.Index(fields=["content_type", "object_id"])]
verbose_name = _('tagged item') verbose_name = _('tagged item')
verbose_name_plural = _('tagged items') verbose_name_plural = _('tagged items')
# Note: while there is no ordering applied here (because it would basically be done on fields
# of the related `tag`), there is an ordering applied to extras.api.views.TaggedItemViewSet
# to allow for proper pagination.

View File

@ -12,3 +12,7 @@ class SerializerNotFound(Exception):
class GraphQLTypeNotFound(Exception): class GraphQLTypeNotFound(Exception):
pass pass
class QuerySetNotOrdered(Exception):
pass

View File

@ -1,6 +1,7 @@
from django.db.models import QuerySet from django.db.models import QuerySet
from rest_framework.pagination import LimitOffsetPagination from rest_framework.pagination import LimitOffsetPagination
from netbox.api.exceptions import QuerySetNotOrdered
from netbox.config import get_config from netbox.config import get_config
@ -15,6 +16,12 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
def paginate_queryset(self, queryset, request, view=None): def paginate_queryset(self, queryset, request, view=None):
if isinstance(queryset, QuerySet) and not queryset.ordered:
raise QuerySetNotOrdered(
"Paginating over an unordered queryset is unreliable. Ensure that a minimal "
"ordering has been applied to the queryset for this API endpoint."
)
if isinstance(queryset, QuerySet): if isinstance(queryset, QuerySet):
self.count = self.get_queryset_count(queryset) self.count = self.get_queryset_count(queryset)
else: else:

View File

@ -1,8 +1,13 @@
import uuid import uuid
from django.test import RequestFactory, TestCase
from django.urls import reverse from django.urls import reverse
from rest_framework.request import Request
from netbox.api.exceptions import QuerySetNotOrdered
from netbox.api.pagination import OptionalLimitOffsetPagination
from utilities.testing import APITestCase from utilities.testing import APITestCase
from users.models import Token
class AppTest(APITestCase): class AppTest(APITestCase):
@ -26,3 +31,40 @@ class AppTest(APITestCase):
response = self.client.get(f'{url}?format=api', **self.header) response = self.client.get(f'{url}?format=api', **self.header)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
class OptionalLimitOffsetPaginationTest(TestCase):
def setUp(self):
self.paginator = OptionalLimitOffsetPagination()
self.factory = RequestFactory()
def _make_drf_request(self, path='/', query_params=None):
"""Helper to create a proper DRF Request object"""
return Request(self.factory.get(path, query_params or {}))
def test_raises_exception_for_unordered_queryset(self):
"""Should raise QuerySetNotOrdered for unordered QuerySet"""
queryset = Token.objects.all().order_by()
request = self._make_drf_request()
with self.assertRaises(QuerySetNotOrdered) as cm:
self.paginator.paginate_queryset(queryset, request)
error_msg = str(cm.exception)
self.assertIn("Paginating over an unordered queryset is unreliable", error_msg)
self.assertIn("Ensure that a minimal ordering has been applied", error_msg)
def test_allows_ordered_queryset(self):
"""Should not raise exception for ordered QuerySet"""
queryset = Token.objects.all().order_by('created')
request = self._make_drf_request()
self.paginator.paginate_queryset(queryset, request) # Should not raise exception
def test_allows_non_queryset_iterables(self):
"""Should not raise exception for non-QuerySet iterables"""
iterable = [1, 2, 3, 4, 5]
request = self._make_drf_request()
self.paginator.paginate_queryset(iterable, request) # Should not raise exception

Binary file not shown.

Binary file not shown.

View File

@ -35,7 +35,7 @@ function showRackElements(
selector: string, selector: string,
elevation: HTMLObjectElement, elevation: HTMLObjectElement,
): void { ): void {
const elements = elevation.contentDocument?.querySelectorAll(selector) ?? []; const elements = elevation.querySelectorAll(selector) ?? [];
for (const element of elements) { for (const element of elements) {
element.classList.remove('hidden'); element.classList.remove('hidden');
} }
@ -45,7 +45,7 @@ function hideRackElements(
selector: string, selector: string,
elevation: HTMLObjectElement, elevation: HTMLObjectElement,
): void { ): void {
const elements = elevation.contentDocument?.querySelectorAll(selector) ?? []; const elements = elevation.querySelectorAll(selector) ?? [];
for (const element of elements) { for (const element of elements) {
element.classList.add('hidden'); element.classList.add('hidden');
} }

View File

@ -19,135 +19,135 @@
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
"@esbuild/aix-ppc64@0.25.6": "@esbuild/aix-ppc64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz#164b19122e2ed54f85469df9dea98ddb01d5e79e" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz#a1414903bb38027382f85f03dda6065056757727"
integrity sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw== integrity sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==
"@esbuild/android-arm64@0.25.6": "@esbuild/android-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz#8f539e7def848f764f6432598e51cc3820fde3a5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz#c859994089e9767224269884061f89dae6fb51c6"
integrity sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA== integrity sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==
"@esbuild/android-arm@0.25.6": "@esbuild/android-arm@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.6.tgz#4ceb0f40113e9861169be83e2a670c260dd234ff" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz#96a8f2ca91c6cd29ea90b1af79d83761c8ba0059"
integrity sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg== integrity sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==
"@esbuild/android-x64@0.25.6": "@esbuild/android-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.6.tgz#ad4f280057622c25fe985c08999443a195dc63a8" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz#a3a626c4fec4a024a9fa8c7679c39996e92916f0"
integrity sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A== integrity sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==
"@esbuild/darwin-arm64@0.25.6": "@esbuild/darwin-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz#d1f04027396b3d6afc96bacd0d13167dfd9f01f7" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz#a5e1252ca2983d566af1c0ea39aded65736fc66d"
integrity sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA== integrity sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==
"@esbuild/darwin-x64@0.25.6": "@esbuild/darwin-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz#2b4a6cedb799f635758d7832d75b23772c8ef68f" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz#5271b0df2bb12ce8df886704bfdd1c7cc01385d2"
integrity sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg== integrity sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==
"@esbuild/freebsd-arm64@0.25.6": "@esbuild/freebsd-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz#a26266cc97dd78dc3c3f3d6788b1b83697b1055d" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz#d0a0e7fdf19733b8bb1566b81df1aa0bb7e46ada"
integrity sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg== integrity sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==
"@esbuild/freebsd-x64@0.25.6": "@esbuild/freebsd-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz#9feb8e826735c568ebfd94859b22a3fbb6a9bdd2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz#2de8b2e0899d08f1cb1ef3128e159616e7e85343"
integrity sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ== integrity sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==
"@esbuild/linux-arm64@0.25.6": "@esbuild/linux-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz#c07cbed8e249f4c28e7f32781d36fc4695293d28" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz#a4209efadc0c2975716458484a4e90c237c48ae9"
integrity sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ== integrity sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==
"@esbuild/linux-arm@0.25.6": "@esbuild/linux-arm@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz#d6e2cd8ef3196468065d41f13fa2a61aaa72644a" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz#ccd9e291c24cd8d9142d819d463e2e7200d25b19"
integrity sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw== integrity sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==
"@esbuild/linux-ia32@0.25.6": "@esbuild/linux-ia32@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz#3e682bd47c4eddcc4b8f1393dfc8222482f17997" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz#006ad1536d0c2b28fb3a1cf0b53bcb85aaf92c4d"
integrity sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw== integrity sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==
"@esbuild/linux-loong64@0.25.6": "@esbuild/linux-loong64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz#473f5ea2e52399c08ad4cd6b12e6dbcddd630f05" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz#127b3fbfb2c2e08b1397e985932f718f09a8f5c4"
integrity sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg== integrity sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==
"@esbuild/linux-mips64el@0.25.6": "@esbuild/linux-mips64el@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz#9960631c9fd61605b0939c19043acf4ef2b51718" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz#837d1449517791e3fa7d82675a2d06d9f56cb340"
integrity sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw== integrity sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==
"@esbuild/linux-ppc64@0.25.6": "@esbuild/linux-ppc64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz#477cbf8bb04aa034b94f362c32c86b5c31db8d3e" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz#aa2e3bd93ab8df084212f1895ca4b03c42d9e0fe"
integrity sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw== integrity sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==
"@esbuild/linux-riscv64@0.25.6": "@esbuild/linux-riscv64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz#bcdb46c8fb8e93aa779e9a0a62cd4ac00dcac626" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz#a340620e31093fef72767dd28ab04214b3442083"
integrity sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w== integrity sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==
"@esbuild/linux-s390x@0.25.6": "@esbuild/linux-s390x@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz#f412cf5fdf0aea849ff51c73fd817c6c0234d46d" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz#ddfed266c8c13f5efb3105a0cd47f6dcd0e79e71"
integrity sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw== integrity sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==
"@esbuild/linux-x64@0.25.6": "@esbuild/linux-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz#d8233c09b5ebc0c855712dc5eeb835a3a3341108" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz#9a4f78c75c051e8c060183ebb39a269ba936a2ac"
integrity sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig== integrity sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==
"@esbuild/netbsd-arm64@0.25.6": "@esbuild/netbsd-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz#f51ae8dd1474172e73cf9cbaf8a38d1c72dd8f1a" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz#902c80e1d678047926387230bc037e63e00697d0"
integrity sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q== integrity sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==
"@esbuild/netbsd-x64@0.25.6": "@esbuild/netbsd-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz#a267538602c0e50a858cf41dcfe5d8036f8da8e7" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz#2d9eb4692add2681ff05a14ce99de54fbed7079c"
integrity sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g== integrity sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==
"@esbuild/openbsd-arm64@0.25.6": "@esbuild/openbsd-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz#a51be60c425b85c216479b8c344ad0511635f2d2" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz#89c3b998c6de739db38ab7fb71a8a76b3fa84a45"
integrity sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg== integrity sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==
"@esbuild/openbsd-x64@0.25.6": "@esbuild/openbsd-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz#7e4a743c73f75562e29223ba69d0be6c9c9008da" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz#2f01615cf472b0e48c077045cfd96b5c149365cc"
integrity sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw== integrity sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==
"@esbuild/openharmony-arm64@0.25.6": "@esbuild/openharmony-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz#2087a5028f387879154ebf44bdedfafa17682e5b" resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz#a201f720cd2c3ebf9a6033fcc3feb069a54b509a"
integrity sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA== integrity sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==
"@esbuild/sunos-x64@0.25.6": "@esbuild/sunos-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz#56531f861723ea0dc6283a2bb8837304223cb736" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz#07046c977985a3334667f19e6ab3a01a80862afb"
integrity sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA== integrity sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==
"@esbuild/win32-arm64@0.25.6": "@esbuild/win32-arm64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz#f4989f033deac6fae323acff58764fa8bc01436e" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz#4a5470caf0d16127c05d4833d4934213c69392d1"
integrity sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q== integrity sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==
"@esbuild/win32-ia32@0.25.6": "@esbuild/win32-ia32@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz#b260e9df71e3939eb33925076d39f63cec7d1525" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz#3de3e8470b7b328d99dbc3e9ec1eace207e5bbc4"
integrity sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ== integrity sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==
"@esbuild/win32-x64@0.25.6": "@esbuild/win32-x64@0.25.8":
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz#4276edd5c105bc28b11c6a1f76fb9d29d1bd25c1" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz#610d7ea539d2fcdbe39237b5cc175eb2c4451f9c"
integrity sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA== integrity sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==
"@eslint-community/eslint-utils@^4.2.0": "@eslint-community/eslint-utils@^4.2.0":
version "4.4.0" version "4.4.0"
@ -163,7 +163,12 @@
dependencies: dependencies:
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^3.4.3"
"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": "@eslint-community/regexpp@^4.10.0":
version "4.12.1"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
"@eslint-community/regexpp@^4.6.1":
version "4.11.0" version "4.11.0"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
@ -464,10 +469,10 @@
"@parcel/watcher-win32-ia32" "2.4.1" "@parcel/watcher-win32-ia32" "2.4.1"
"@parcel/watcher-win32-x64" "2.4.1" "@parcel/watcher-win32-x64" "2.4.1"
"@pkgr/core@^0.2.4": "@pkgr/core@^0.2.9":
version "0.2.7" version "0.2.9"
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.7.tgz#eb5014dfd0b03e7f3ba2eeeff506eed89b028058" resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b"
integrity sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg== integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==
"@popperjs/core@^2.11.6", "@popperjs/core@^2.11.8", "@popperjs/core@^2.9.2": "@popperjs/core@^2.11.6", "@popperjs/core@^2.11.8", "@popperjs/core@^2.9.2":
version "2.11.8" version "2.11.8"
@ -840,78 +845,78 @@
"@types/estree" "*" "@types/estree" "*"
"@typescript-eslint/eslint-plugin@^8.37.0": "@typescript-eslint/eslint-plugin@^8.37.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz#332392883f936137cd6252c8eb236d298e514e70" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz#6e5220d16f2691ab6d983c1737dd5b36e17641b7"
integrity sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA== integrity sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==
dependencies: dependencies:
"@eslint-community/regexpp" "^4.10.0" "@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "8.37.0" "@typescript-eslint/scope-manager" "8.38.0"
"@typescript-eslint/type-utils" "8.37.0" "@typescript-eslint/type-utils" "8.38.0"
"@typescript-eslint/utils" "8.37.0" "@typescript-eslint/utils" "8.38.0"
"@typescript-eslint/visitor-keys" "8.37.0" "@typescript-eslint/visitor-keys" "8.38.0"
graphemer "^1.4.0" graphemer "^1.4.0"
ignore "^7.0.0" ignore "^7.0.0"
natural-compare "^1.4.0" natural-compare "^1.4.0"
ts-api-utils "^2.1.0" ts-api-utils "^2.1.0"
"@typescript-eslint/parser@^8.37.0": "@typescript-eslint/parser@^8.37.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.37.0.tgz#b87f6b61e25ad5cc5bbf8baf809b8da889c89804" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.38.0.tgz#6723a5ea881e1777956b1045cba30be5ea838293"
integrity sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA== integrity sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "8.37.0" "@typescript-eslint/scope-manager" "8.38.0"
"@typescript-eslint/types" "8.37.0" "@typescript-eslint/types" "8.38.0"
"@typescript-eslint/typescript-estree" "8.37.0" "@typescript-eslint/typescript-estree" "8.38.0"
"@typescript-eslint/visitor-keys" "8.37.0" "@typescript-eslint/visitor-keys" "8.38.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/project-service@8.37.0": "@typescript-eslint/project-service@8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.37.0.tgz#0594352e32a4ac9258591b88af77b5653800cdfe" resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.38.0.tgz#4900771f943163027fd7d2020a062892056b5e2f"
integrity sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA== integrity sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==
dependencies: dependencies:
"@typescript-eslint/tsconfig-utils" "^8.37.0" "@typescript-eslint/tsconfig-utils" "^8.38.0"
"@typescript-eslint/types" "^8.37.0" "@typescript-eslint/types" "^8.38.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@8.37.0": "@typescript-eslint/scope-manager@8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz#a31a3c80ca2ef4ed58de13742debb692e7d4c0a4" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz#5a0efcb5c9cf6e4121b58f87972f567c69529226"
integrity sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA== integrity sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==
dependencies: dependencies:
"@typescript-eslint/types" "8.37.0" "@typescript-eslint/types" "8.38.0"
"@typescript-eslint/visitor-keys" "8.37.0" "@typescript-eslint/visitor-keys" "8.38.0"
"@typescript-eslint/tsconfig-utils@8.37.0", "@typescript-eslint/tsconfig-utils@^8.37.0": "@typescript-eslint/tsconfig-utils@8.38.0", "@typescript-eslint/tsconfig-utils@^8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz#47a2760d265c6125f8e7864bc5c8537cad2bd053" resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz#6de4ce224a779601a8df667db56527255c42c4d0"
integrity sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg== integrity sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==
"@typescript-eslint/type-utils@8.37.0": "@typescript-eslint/type-utils@8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz#2a682e4c6ff5886712dad57e9787b5e417124507" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz#a56cd84765fa6ec135fe252b5db61e304403a85b"
integrity sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow== integrity sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==
dependencies: dependencies:
"@typescript-eslint/types" "8.37.0" "@typescript-eslint/types" "8.38.0"
"@typescript-eslint/typescript-estree" "8.37.0" "@typescript-eslint/typescript-estree" "8.38.0"
"@typescript-eslint/utils" "8.37.0" "@typescript-eslint/utils" "8.38.0"
debug "^4.3.4" debug "^4.3.4"
ts-api-utils "^2.1.0" ts-api-utils "^2.1.0"
"@typescript-eslint/types@8.37.0", "@typescript-eslint/types@^8.37.0": "@typescript-eslint/types@8.38.0", "@typescript-eslint/types@^8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.37.0.tgz#09517aa9625eb3c68941dde3ac8835740587b6ff" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.38.0.tgz#297351c994976b93c82ac0f0e206c8143aa82529"
integrity sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ== integrity sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==
"@typescript-eslint/typescript-estree@8.37.0": "@typescript-eslint/typescript-estree@8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz#a07e4574d8e6e4355a558f61323730c987f5fcbc" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz#82262199eb6778bba28a319e25ad05b1158957df"
integrity sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg== integrity sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==
dependencies: dependencies:
"@typescript-eslint/project-service" "8.37.0" "@typescript-eslint/project-service" "8.38.0"
"@typescript-eslint/tsconfig-utils" "8.37.0" "@typescript-eslint/tsconfig-utils" "8.38.0"
"@typescript-eslint/types" "8.37.0" "@typescript-eslint/types" "8.38.0"
"@typescript-eslint/visitor-keys" "8.37.0" "@typescript-eslint/visitor-keys" "8.38.0"
debug "^4.3.4" debug "^4.3.4"
fast-glob "^3.3.2" fast-glob "^3.3.2"
is-glob "^4.0.3" is-glob "^4.0.3"
@ -919,22 +924,22 @@
semver "^7.6.0" semver "^7.6.0"
ts-api-utils "^2.1.0" ts-api-utils "^2.1.0"
"@typescript-eslint/utils@8.37.0": "@typescript-eslint/utils@8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.37.0.tgz#189ea59b2709f5d898614611f091a776751ee335" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.38.0.tgz#5f10159899d30eb92ba70e642ca6f754bddbf15a"
integrity sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A== integrity sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.7.0" "@eslint-community/eslint-utils" "^4.7.0"
"@typescript-eslint/scope-manager" "8.37.0" "@typescript-eslint/scope-manager" "8.38.0"
"@typescript-eslint/types" "8.37.0" "@typescript-eslint/types" "8.38.0"
"@typescript-eslint/typescript-estree" "8.37.0" "@typescript-eslint/typescript-estree" "8.38.0"
"@typescript-eslint/visitor-keys@8.37.0": "@typescript-eslint/visitor-keys@8.38.0":
version "8.37.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz#cdb6a6bd3e8d6dd69bd70c1bdda36e2d18737455" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz#a9765a527b082cb8fc60fd8a16e47c7ad5b60ea5"
integrity sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w== integrity sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==
dependencies: dependencies:
"@typescript-eslint/types" "8.37.0" "@typescript-eslint/types" "8.38.0"
eslint-visitor-keys "^4.2.1" eslint-visitor-keys "^4.2.1"
"@ungap/structured-clone@^1.2.0": "@ungap/structured-clone@^1.2.0":
@ -1120,9 +1125,9 @@ brace-expansion@^1.1.7:
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^2.0.1: brace-expansion@^2.0.1:
version "2.0.1" version "2.0.2"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
dependencies: dependencies:
balanced-match "^1.0.0" balanced-match "^1.0.0"
@ -1344,11 +1349,11 @@ debug@^4.3.1, debug@^4.3.2, debug@^4.3.5:
ms "2.1.2" ms "2.1.2"
debug@^4.3.4: debug@^4.3.4:
version "4.3.4" version "4.4.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==
dependencies: dependencies:
ms "2.1.2" ms "^2.1.3"
decode-uri-component@^0.4.1: decode-uri-component@^0.4.1:
version "0.4.1" version "0.4.1"
@ -1633,36 +1638,36 @@ esbuild-sass-plugin@^3.3.1:
sass "^1.71.1" sass "^1.71.1"
esbuild@^0.25.6: esbuild@^0.25.6:
version "0.25.6" version "0.25.8"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.6.tgz#9b82a3db2fa131aec069ab040fd57ed0a880cdcd" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.8.tgz#482d42198b427c9c2f3a81b63d7663aecb1dda07"
integrity sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg== integrity sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==
optionalDependencies: optionalDependencies:
"@esbuild/aix-ppc64" "0.25.6" "@esbuild/aix-ppc64" "0.25.8"
"@esbuild/android-arm" "0.25.6" "@esbuild/android-arm" "0.25.8"
"@esbuild/android-arm64" "0.25.6" "@esbuild/android-arm64" "0.25.8"
"@esbuild/android-x64" "0.25.6" "@esbuild/android-x64" "0.25.8"
"@esbuild/darwin-arm64" "0.25.6" "@esbuild/darwin-arm64" "0.25.8"
"@esbuild/darwin-x64" "0.25.6" "@esbuild/darwin-x64" "0.25.8"
"@esbuild/freebsd-arm64" "0.25.6" "@esbuild/freebsd-arm64" "0.25.8"
"@esbuild/freebsd-x64" "0.25.6" "@esbuild/freebsd-x64" "0.25.8"
"@esbuild/linux-arm" "0.25.6" "@esbuild/linux-arm" "0.25.8"
"@esbuild/linux-arm64" "0.25.6" "@esbuild/linux-arm64" "0.25.8"
"@esbuild/linux-ia32" "0.25.6" "@esbuild/linux-ia32" "0.25.8"
"@esbuild/linux-loong64" "0.25.6" "@esbuild/linux-loong64" "0.25.8"
"@esbuild/linux-mips64el" "0.25.6" "@esbuild/linux-mips64el" "0.25.8"
"@esbuild/linux-ppc64" "0.25.6" "@esbuild/linux-ppc64" "0.25.8"
"@esbuild/linux-riscv64" "0.25.6" "@esbuild/linux-riscv64" "0.25.8"
"@esbuild/linux-s390x" "0.25.6" "@esbuild/linux-s390x" "0.25.8"
"@esbuild/linux-x64" "0.25.6" "@esbuild/linux-x64" "0.25.8"
"@esbuild/netbsd-arm64" "0.25.6" "@esbuild/netbsd-arm64" "0.25.8"
"@esbuild/netbsd-x64" "0.25.6" "@esbuild/netbsd-x64" "0.25.8"
"@esbuild/openbsd-arm64" "0.25.6" "@esbuild/openbsd-arm64" "0.25.8"
"@esbuild/openbsd-x64" "0.25.6" "@esbuild/openbsd-x64" "0.25.8"
"@esbuild/openharmony-arm64" "0.25.6" "@esbuild/openharmony-arm64" "0.25.8"
"@esbuild/sunos-x64" "0.25.6" "@esbuild/sunos-x64" "0.25.8"
"@esbuild/win32-arm64" "0.25.6" "@esbuild/win32-arm64" "0.25.8"
"@esbuild/win32-ia32" "0.25.6" "@esbuild/win32-ia32" "0.25.8"
"@esbuild/win32-x64" "0.25.6" "@esbuild/win32-x64" "0.25.8"
escape-string-regexp@^4.0.0: escape-string-regexp@^4.0.0:
version "4.0.0" version "4.0.0"
@ -1737,9 +1742,9 @@ eslint-plugin-import@^2.32.0:
tsconfig-paths "^3.15.0" tsconfig-paths "^3.15.0"
eslint-plugin-prettier@^5.5.1: eslint-plugin-prettier@^5.5.1:
version "5.5.1" version "5.5.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz#470820964de9aedb37e9ce62c3266d2d26d08d15" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz#1f88e9220a72ac8be171eec5f9d4e4d529b5f4a0"
integrity sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw== integrity sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==
dependencies: dependencies:
prettier-linter-helpers "^1.0.0" prettier-linter-helpers "^1.0.0"
synckit "^0.11.7" synckit "^0.11.7"
@ -1850,15 +1855,15 @@ fast-diff@^1.1.2:
integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
fast-glob@^3.3.2: fast-glob@^3.3.2:
version "3.3.2" version "3.3.3"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
dependencies: dependencies:
"@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3" "@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2" glob-parent "^5.1.2"
merge2 "^1.3.0" merge2 "^1.3.0"
micromatch "^4.0.4" micromatch "^4.0.8"
fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@^2.0.0:
version "2.1.0" version "2.1.0"
@ -1871,9 +1876,9 @@ fast-levenshtein@^2.0.6:
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fastq@^1.6.0: fastq@^1.6.0:
version "1.17.1" version "1.19.1"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5"
integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==
dependencies: dependencies:
reusify "^1.0.4" reusify "^1.0.4"
@ -2741,7 +2746,7 @@ meros@^1.1.4:
resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.0.tgz#c617d2092739d55286bf618129280f362e6242f2" resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.0.tgz#c617d2092739d55286bf618129280f362e6242f2"
integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==
micromatch@^4.0.4, micromatch@^4.0.5: micromatch@^4.0.5, micromatch@^4.0.8:
version "4.0.8" version "4.0.8"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
@ -2773,7 +2778,7 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@^2.1.1: ms@^2.1.1, ms@^2.1.3:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@ -3110,9 +3115,9 @@ resolve@^1.22.4, resolve@^1.22.8:
supports-preserve-symlinks-flag "^1.0.0" supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4: reusify@^1.0.4:
version "1.0.4" version "1.1.0"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==
rimraf@^3.0.2: rimraf@^3.0.2:
version "3.0.2" version "3.0.2"
@ -3217,7 +3222,12 @@ semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.6.0, semver@^7.6.3: semver@^7.6.0:
version "7.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
semver@^7.6.3:
version "7.6.3" version "7.6.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
@ -3430,11 +3440,11 @@ supports-preserve-symlinks-flag@^1.0.0:
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
synckit@^0.11.7: synckit@^0.11.7:
version "0.11.8" version "0.11.11"
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0"
integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A== integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==
dependencies: dependencies:
"@pkgr/core" "^0.2.4" "@pkgr/core" "^0.2.9"
tapable@^2.2.0: tapable@^2.2.0:
version "2.2.1" version "2.2.1"

View File

@ -1,3 +1,3 @@
version: "4.3.4" version: "4.3.5"
edition: "Community" edition: "Community"
published: "2025-07-15" published: "2025-07-29"

View File

@ -55,7 +55,7 @@ Blocks:
{# Release info #} {# Release info #}
<div class="text-muted text-center fs-5 my-3"> <div class="text-muted text-center fs-5 my-3">
{{ settings.RELEASE.name }} {{ settings.RELEASE.name }}
{% if not settings.RELEASE.features.commercial %} {% if not settings.RELEASE.features.commercial and not settings.ISOLATED_DEPLOYMENT %}
<div> <div>
<a href="https://netboxlabs.com/netbox-cloud/" class="text-muted">{% trans "Get" %} Cloud</a> | <a href="https://netboxlabs.com/netbox-cloud/" class="text-muted">{% trans "Get" %} Cloud</a> |
<a href="https://netboxlabs.com/netbox-enterprise/" class="text-muted">{% trans "Get" %} Enterprise</a> <a href="https://netboxlabs.com/netbox-enterprise/" class="text-muted">{% trans "Get" %} Enterprise</a>
@ -184,7 +184,7 @@ Blocks:
{% endif %} {% endif %}
{# Commercial links #} {# Commercial links #}
{% if settings.RELEASE.features.commercial %} {% if settings.RELEASE.features.commercial and not settings.ISOLATED_DEPLOYMENT %}
{# LinkedIn #} {# LinkedIn #}
<li class="list-inline-item"> <li class="list-inline-item">
<a href="https://www.linkedin.com/company/netboxlabs/" target="_blank" class="link-secondary" rel="noopener" aria-label="LinkedIn"> <a href="https://www.linkedin.com/company/netboxlabs/" target="_blank" class="link-secondary" rel="noopener" aria-label="LinkedIn">
@ -199,7 +199,7 @@ Blocks:
</li> </li>
{# Community links #} {# Community links #}
{% else %} {% elif not settings.ISOLATED_DEPLOYMENT %}
{# GitHub #} {# GitHub #}
<li class="list-inline-item"> <li class="list-inline-item">
<a href="https://github.com/netbox-community/netbox" target="_blank" class="link-secondary" rel="noopener" aria-label="{% trans "Source Code" %}"> <a href="https://github.com/netbox-community/netbox" target="_blank" class="link-secondary" rel="noopener" aria-label="{% trans "Source Code" %}">

View File

@ -1,5 +1,5 @@
{% load i18n %} {% load i18n %}
<div style="margin-left: -30px"> <div style="margin-left: -30px" class="rack_elevation">
<div <div
hx-get="{% url 'dcim-api:rack-elevation' pk=object.pk %}?face={{ face }}&render=svg{% if extra_params %}&{{ extra_params }}{% endif %}" hx-get="{% url 'dcim-api:rack-elevation' pk=object.pk %}?face={{ face }}&render=svg{% if extra_params %}&{{ extra_params }}{% endif %}"
hx-trigger="intersect" hx-trigger="intersect"

View File

@ -45,12 +45,17 @@ class TenantBulkEditForm(NetBoxModelBulkEditForm):
queryset=TenantGroup.objects.all(), queryset=TenantGroup.objects.all(),
required=False required=False
) )
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
model = Tenant model = Tenant
fieldsets = ( fieldsets = (
FieldSet('group'), FieldSet('group', 'description'),
) )
nullable_fields = ('group',) nullable_fields = ('group', 'description')
# #

View File

@ -98,6 +98,7 @@ class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
cls.bulk_edit_data = { cls.bulk_edit_data = {
'group': tenant_groups[1].pk, 'group': tenant_groups[1].pk,
'description': 'Bulk edit description',
} }

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

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-16 05:05+0000\n" "POT-Creation-Date: 2025-07-30 05:08+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -215,8 +215,8 @@ msgstr ""
#: netbox/dcim/forms/bulk_edit.py:344 netbox/dcim/forms/bulk_edit.py:730 #: netbox/dcim/forms/bulk_edit.py:344 netbox/dcim/forms/bulk_edit.py:730
#: netbox/dcim/forms/bulk_edit.py:935 netbox/dcim/forms/bulk_import.py:134 #: netbox/dcim/forms/bulk_edit.py:935 netbox/dcim/forms/bulk_import.py:134
#: netbox/dcim/forms/bulk_import.py:236 netbox/dcim/forms/bulk_import.py:337 #: netbox/dcim/forms/bulk_import.py:236 netbox/dcim/forms/bulk_import.py:337
#: netbox/dcim/forms/bulk_import.py:598 netbox/dcim/forms/bulk_import.py:1479 #: netbox/dcim/forms/bulk_import.py:598 netbox/dcim/forms/bulk_import.py:1539
#: netbox/dcim/forms/bulk_import.py:1507 netbox/dcim/forms/filtersets.py:89 #: netbox/dcim/forms/bulk_import.py:1567 netbox/dcim/forms/filtersets.py:89
#: netbox/dcim/forms/filtersets.py:227 netbox/dcim/forms/filtersets.py:344 #: netbox/dcim/forms/filtersets.py:227 netbox/dcim/forms/filtersets.py:344
#: netbox/dcim/forms/filtersets.py:441 netbox/dcim/forms/filtersets.py:773 #: netbox/dcim/forms/filtersets.py:441 netbox/dcim/forms/filtersets.py:773
#: netbox/dcim/forms/filtersets.py:992 netbox/dcim/forms/filtersets.py:1065 #: netbox/dcim/forms/filtersets.py:992 netbox/dcim/forms/filtersets.py:1065
@ -587,9 +587,10 @@ msgstr ""
#: netbox/templates/wireless/wirelesslan.html:34 #: netbox/templates/wireless/wirelesslan.html:34
#: netbox/templates/wireless/wirelesslangroup.html:33 #: netbox/templates/wireless/wirelesslangroup.html:33
#: netbox/templates/wireless/wirelesslink.html:34 #: netbox/templates/wireless/wirelesslink.html:34
#: netbox/tenancy/forms/bulk_edit.py:32 netbox/tenancy/forms/bulk_edit.py:82 #: netbox/tenancy/forms/bulk_edit.py:32 netbox/tenancy/forms/bulk_edit.py:49
#: netbox/tenancy/forms/bulk_edit.py:130 netbox/users/forms/bulk_edit.py:64 #: netbox/tenancy/forms/bulk_edit.py:87 netbox/tenancy/forms/bulk_edit.py:135
#: netbox/users/forms/bulk_edit.py:82 netbox/users/forms/bulk_edit.py:112 #: netbox/users/forms/bulk_edit.py:64 netbox/users/forms/bulk_edit.py:82
#: netbox/users/forms/bulk_edit.py:112
#: netbox/virtualization/forms/bulk_edit.py:33 #: netbox/virtualization/forms/bulk_edit.py:33
#: netbox/virtualization/forms/bulk_edit.py:47 #: netbox/virtualization/forms/bulk_edit.py:47
#: netbox/virtualization/forms/bulk_edit.py:82 #: netbox/virtualization/forms/bulk_edit.py:82
@ -655,13 +656,13 @@ msgstr ""
#: netbox/circuits/forms/filtersets.py:321 netbox/dcim/forms/bulk_edit.py:216 #: netbox/circuits/forms/filtersets.py:321 netbox/dcim/forms/bulk_edit.py:216
#: netbox/dcim/forms/bulk_edit.py:656 netbox/dcim/forms/bulk_edit.py:866 #: netbox/dcim/forms/bulk_edit.py:656 netbox/dcim/forms/bulk_edit.py:866
#: netbox/dcim/forms/bulk_edit.py:1235 netbox/dcim/forms/bulk_edit.py:1262 #: netbox/dcim/forms/bulk_edit.py:1235 netbox/dcim/forms/bulk_edit.py:1262
#: netbox/dcim/forms/bulk_edit.py:1796 netbox/dcim/forms/filtersets.py:1132 #: netbox/dcim/forms/bulk_edit.py:1796 netbox/dcim/forms/bulk_import.py:1414
#: netbox/dcim/forms/filtersets.py:1390 netbox/dcim/forms/filtersets.py:1543 #: netbox/dcim/forms/filtersets.py:1132 netbox/dcim/forms/filtersets.py:1390
#: netbox/dcim/forms/filtersets.py:1567 netbox/dcim/tables/devices.py:748 #: netbox/dcim/forms/filtersets.py:1543 netbox/dcim/forms/filtersets.py:1567
#: netbox/dcim/tables/devices.py:804 netbox/dcim/tables/devices.py:1045 #: netbox/dcim/tables/devices.py:748 netbox/dcim/tables/devices.py:804
#: netbox/dcim/tables/devicetypes.py:256 netbox/dcim/tables/devicetypes.py:271 #: netbox/dcim/tables/devices.py:1045 netbox/dcim/tables/devicetypes.py:256
#: netbox/dcim/tables/racks.py:33 netbox/extras/forms/bulk_edit.py:303 #: netbox/dcim/tables/devicetypes.py:271 netbox/dcim/tables/racks.py:33
#: netbox/extras/tables/tables.py:487 #: netbox/extras/forms/bulk_edit.py:303 netbox/extras/tables/tables.py:487
#: netbox/templates/circuits/circuittype.html:30 #: netbox/templates/circuits/circuittype.html:30
#: netbox/templates/circuits/virtualcircuittype.html:30 #: netbox/templates/circuits/virtualcircuittype.html:30
#: netbox/templates/dcim/cable.html:40 netbox/templates/dcim/devicerole.html:38 #: netbox/templates/dcim/cable.html:40 netbox/templates/dcim/devicerole.html:38
@ -692,8 +693,8 @@ msgstr ""
#: netbox/dcim/forms/bulk_import.py:766 netbox/dcim/forms/bulk_import.py:792 #: netbox/dcim/forms/bulk_import.py:766 netbox/dcim/forms/bulk_import.py:792
#: netbox/dcim/forms/bulk_import.py:818 netbox/dcim/forms/bulk_import.py:838 #: netbox/dcim/forms/bulk_import.py:818 netbox/dcim/forms/bulk_import.py:838
#: netbox/dcim/forms/bulk_import.py:924 netbox/dcim/forms/bulk_import.py:1018 #: netbox/dcim/forms/bulk_import.py:924 netbox/dcim/forms/bulk_import.py:1018
#: netbox/dcim/forms/bulk_import.py:1060 netbox/dcim/forms/bulk_import.py:1381 #: netbox/dcim/forms/bulk_import.py:1060 netbox/dcim/forms/bulk_import.py:1395
#: netbox/dcim/forms/bulk_import.py:1544 netbox/dcim/forms/filtersets.py:1023 #: netbox/dcim/forms/bulk_import.py:1604 netbox/dcim/forms/filtersets.py:1023
#: netbox/dcim/forms/filtersets.py:1122 netbox/dcim/forms/filtersets.py:1243 #: netbox/dcim/forms/filtersets.py:1122 netbox/dcim/forms/filtersets.py:1243
#: netbox/dcim/forms/filtersets.py:1315 netbox/dcim/forms/filtersets.py:1340 #: netbox/dcim/forms/filtersets.py:1315 netbox/dcim/forms/filtersets.py:1340
#: netbox/dcim/forms/filtersets.py:1364 netbox/dcim/forms/filtersets.py:1384 #: netbox/dcim/forms/filtersets.py:1364 netbox/dcim/forms/filtersets.py:1384
@ -763,8 +764,8 @@ msgstr ""
#: netbox/dcim/forms/bulk_edit.py:1819 netbox/dcim/forms/bulk_import.py:91 #: netbox/dcim/forms/bulk_edit.py:1819 netbox/dcim/forms/bulk_import.py:91
#: netbox/dcim/forms/bulk_import.py:150 netbox/dcim/forms/bulk_import.py:254 #: netbox/dcim/forms/bulk_import.py:150 netbox/dcim/forms/bulk_import.py:254
#: netbox/dcim/forms/bulk_import.py:563 netbox/dcim/forms/bulk_import.py:717 #: netbox/dcim/forms/bulk_import.py:563 netbox/dcim/forms/bulk_import.py:717
#: netbox/dcim/forms/bulk_import.py:1168 netbox/dcim/forms/bulk_import.py:1375 #: netbox/dcim/forms/bulk_import.py:1168 netbox/dcim/forms/bulk_import.py:1389
#: netbox/dcim/forms/bulk_import.py:1539 netbox/dcim/forms/bulk_import.py:1603 #: netbox/dcim/forms/bulk_import.py:1599 netbox/dcim/forms/bulk_import.py:1663
#: netbox/dcim/forms/filtersets.py:180 netbox/dcim/forms/filtersets.py:239 #: netbox/dcim/forms/filtersets.py:180 netbox/dcim/forms/filtersets.py:239
#: netbox/dcim/forms/filtersets.py:361 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:361 netbox/dcim/forms/filtersets.py:819
#: netbox/dcim/forms/filtersets.py:944 netbox/dcim/forms/filtersets.py:1026 #: netbox/dcim/forms/filtersets.py:944 netbox/dcim/forms/filtersets.py:1026
@ -841,8 +842,8 @@ msgstr ""
#: netbox/dcim/forms/bulk_edit.py:856 netbox/dcim/forms/bulk_edit.py:1824 #: netbox/dcim/forms/bulk_edit.py:856 netbox/dcim/forms/bulk_edit.py:1824
#: netbox/dcim/forms/bulk_import.py:110 netbox/dcim/forms/bulk_import.py:155 #: netbox/dcim/forms/bulk_import.py:110 netbox/dcim/forms/bulk_import.py:155
#: netbox/dcim/forms/bulk_import.py:247 netbox/dcim/forms/bulk_import.py:362 #: netbox/dcim/forms/bulk_import.py:247 netbox/dcim/forms/bulk_import.py:362
#: netbox/dcim/forms/bulk_import.py:537 netbox/dcim/forms/bulk_import.py:1387 #: netbox/dcim/forms/bulk_import.py:537 netbox/dcim/forms/bulk_import.py:1401
#: netbox/dcim/forms/bulk_import.py:1596 netbox/dcim/forms/filtersets.py:175 #: netbox/dcim/forms/bulk_import.py:1656 netbox/dcim/forms/filtersets.py:175
#: netbox/dcim/forms/filtersets.py:207 netbox/dcim/forms/filtersets.py:325 #: netbox/dcim/forms/filtersets.py:207 netbox/dcim/forms/filtersets.py:325
#: netbox/dcim/forms/filtersets.py:401 netbox/dcim/forms/filtersets.py:422 #: netbox/dcim/forms/filtersets.py:401 netbox/dcim/forms/filtersets.py:422
#: netbox/dcim/forms/filtersets.py:742 netbox/dcim/forms/filtersets.py:936 #: netbox/dcim/forms/filtersets.py:742 netbox/dcim/forms/filtersets.py:936
@ -1012,7 +1013,7 @@ msgstr ""
#: netbox/circuits/forms/bulk_edit.py:215 #: netbox/circuits/forms/bulk_edit.py:215
#: netbox/circuits/forms/model_forms.py:171 #: netbox/circuits/forms/model_forms.py:171
#: netbox/dcim/forms/bulk_import.py:1348 netbox/dcim/forms/bulk_import.py:1366 #: netbox/dcim/forms/bulk_import.py:1355 netbox/dcim/forms/bulk_import.py:1380
msgid "Termination type" msgid "Termination type"
msgstr "" msgstr ""
@ -1065,7 +1066,7 @@ msgstr ""
#: netbox/templates/dcim/virtualchassis.html:68 #: netbox/templates/dcim/virtualchassis.html:68
#: netbox/templates/dcim/virtualchassis_edit.html:60 #: netbox/templates/dcim/virtualchassis_edit.html:60
#: netbox/templates/ipam/inc/panels/fhrp_groups.html:26 #: netbox/templates/ipam/inc/panels/fhrp_groups.html:26
#: netbox/tenancy/forms/bulk_edit.py:159 netbox/tenancy/forms/filtersets.py:110 #: netbox/tenancy/forms/bulk_edit.py:164 netbox/tenancy/forms/filtersets.py:110
msgid "Priority" msgid "Priority"
msgstr "" msgstr ""
@ -1116,7 +1117,7 @@ msgstr ""
#: netbox/templates/virtualization/virtualmachine.html:23 #: netbox/templates/virtualization/virtualmachine.html:23
#: netbox/templates/vpn/tunneltermination.html:17 #: netbox/templates/vpn/tunneltermination.html:17
#: netbox/templates/wireless/inc/wirelesslink_interface.html:20 #: netbox/templates/wireless/inc/wirelesslink_interface.html:20
#: netbox/tenancy/forms/bulk_edit.py:154 netbox/tenancy/forms/filtersets.py:107 #: netbox/tenancy/forms/bulk_edit.py:159 netbox/tenancy/forms/filtersets.py:107
#: netbox/tenancy/forms/model_forms.py:139 #: netbox/tenancy/forms/model_forms.py:139
#: netbox/tenancy/tables/contacts.py:110 #: netbox/tenancy/tables/contacts.py:110
#: netbox/virtualization/forms/bulk_edit.py:127 #: netbox/virtualization/forms/bulk_edit.py:127
@ -1148,7 +1149,7 @@ msgstr ""
#: netbox/circuits/forms/bulk_import.py:229 netbox/dcim/forms/bulk_import.py:93 #: netbox/circuits/forms/bulk_import.py:229 netbox/dcim/forms/bulk_import.py:93
#: netbox/dcim/forms/bulk_import.py:152 netbox/dcim/forms/bulk_import.py:256 #: netbox/dcim/forms/bulk_import.py:152 netbox/dcim/forms/bulk_import.py:256
#: netbox/dcim/forms/bulk_import.py:565 netbox/dcim/forms/bulk_import.py:719 #: netbox/dcim/forms/bulk_import.py:565 netbox/dcim/forms/bulk_import.py:719
#: netbox/dcim/forms/bulk_import.py:1170 netbox/dcim/forms/bulk_import.py:1541 #: netbox/dcim/forms/bulk_import.py:1170 netbox/dcim/forms/bulk_import.py:1601
#: netbox/ipam/forms/bulk_import.py:197 netbox/ipam/forms/bulk_import.py:265 #: netbox/ipam/forms/bulk_import.py:197 netbox/ipam/forms/bulk_import.py:265
#: netbox/ipam/forms/bulk_import.py:301 netbox/ipam/forms/bulk_import.py:498 #: netbox/ipam/forms/bulk_import.py:301 netbox/ipam/forms/bulk_import.py:498
#: netbox/ipam/forms/bulk_import.py:511 #: netbox/ipam/forms/bulk_import.py:511
@ -1164,8 +1165,8 @@ msgstr ""
#: netbox/circuits/forms/bulk_import.py:236 #: netbox/circuits/forms/bulk_import.py:236
#: netbox/dcim/forms/bulk_import.py:114 netbox/dcim/forms/bulk_import.py:159 #: netbox/dcim/forms/bulk_import.py:114 netbox/dcim/forms/bulk_import.py:159
#: netbox/dcim/forms/bulk_import.py:366 netbox/dcim/forms/bulk_import.py:541 #: netbox/dcim/forms/bulk_import.py:366 netbox/dcim/forms/bulk_import.py:541
#: netbox/dcim/forms/bulk_import.py:1391 netbox/dcim/forms/bulk_import.py:1536 #: netbox/dcim/forms/bulk_import.py:1405 netbox/dcim/forms/bulk_import.py:1596
#: netbox/dcim/forms/bulk_import.py:1600 netbox/ipam/forms/bulk_import.py:45 #: netbox/dcim/forms/bulk_import.py:1660 netbox/ipam/forms/bulk_import.py:45
#: netbox/ipam/forms/bulk_import.py:74 netbox/ipam/forms/bulk_import.py:102 #: netbox/ipam/forms/bulk_import.py:74 netbox/ipam/forms/bulk_import.py:102
#: netbox/ipam/forms/bulk_import.py:122 netbox/ipam/forms/bulk_import.py:142 #: netbox/ipam/forms/bulk_import.py:122 netbox/ipam/forms/bulk_import.py:142
#: netbox/ipam/forms/bulk_import.py:171 netbox/ipam/forms/bulk_import.py:260 #: netbox/ipam/forms/bulk_import.py:171 netbox/ipam/forms/bulk_import.py:260
@ -1245,8 +1246,8 @@ msgstr ""
#: netbox/dcim/forms/bulk_edit.py:466 netbox/dcim/forms/bulk_edit.py:735 #: netbox/dcim/forms/bulk_edit.py:466 netbox/dcim/forms/bulk_edit.py:735
#: netbox/dcim/forms/bulk_edit.py:790 netbox/dcim/forms/bulk_edit.py:944 #: netbox/dcim/forms/bulk_edit.py:790 netbox/dcim/forms/bulk_edit.py:944
#: netbox/dcim/forms/bulk_import.py:241 netbox/dcim/forms/bulk_import.py:343 #: netbox/dcim/forms/bulk_import.py:241 netbox/dcim/forms/bulk_import.py:343
#: netbox/dcim/forms/bulk_import.py:604 netbox/dcim/forms/bulk_import.py:1485 #: netbox/dcim/forms/bulk_import.py:604 netbox/dcim/forms/bulk_import.py:1545
#: netbox/dcim/forms/bulk_import.py:1519 netbox/dcim/forms/filtersets.py:97 #: netbox/dcim/forms/bulk_import.py:1579 netbox/dcim/forms/filtersets.py:97
#: netbox/dcim/forms/filtersets.py:324 netbox/dcim/forms/filtersets.py:358 #: netbox/dcim/forms/filtersets.py:324 netbox/dcim/forms/filtersets.py:358
#: netbox/dcim/forms/filtersets.py:398 netbox/dcim/forms/filtersets.py:449 #: netbox/dcim/forms/filtersets.py:398 netbox/dcim/forms/filtersets.py:449
#: netbox/dcim/forms/filtersets.py:739 netbox/dcim/forms/filtersets.py:782 #: netbox/dcim/forms/filtersets.py:739 netbox/dcim/forms/filtersets.py:782
@ -1948,7 +1949,7 @@ msgstr ""
#: netbox/dcim/forms/bulk_import.py:1007 netbox/dcim/forms/bulk_import.py:1055 #: netbox/dcim/forms/bulk_import.py:1007 netbox/dcim/forms/bulk_import.py:1055
#: netbox/dcim/forms/bulk_import.py:1072 netbox/dcim/forms/bulk_import.py:1084 #: netbox/dcim/forms/bulk_import.py:1072 netbox/dcim/forms/bulk_import.py:1084
#: netbox/dcim/forms/bulk_import.py:1132 netbox/dcim/forms/bulk_import.py:1254 #: netbox/dcim/forms/bulk_import.py:1132 netbox/dcim/forms/bulk_import.py:1254
#: netbox/dcim/forms/bulk_import.py:1590 netbox/dcim/forms/connections.py:24 #: netbox/dcim/forms/bulk_import.py:1650 netbox/dcim/forms/connections.py:24
#: netbox/dcim/forms/filtersets.py:133 netbox/dcim/forms/filtersets.py:941 #: netbox/dcim/forms/filtersets.py:133 netbox/dcim/forms/filtersets.py:941
#: netbox/dcim/forms/filtersets.py:973 netbox/dcim/forms/filtersets.py:1119 #: netbox/dcim/forms/filtersets.py:973 netbox/dcim/forms/filtersets.py:1119
#: netbox/dcim/forms/filtersets.py:1310 netbox/dcim/forms/filtersets.py:1335 #: netbox/dcim/forms/filtersets.py:1310 netbox/dcim/forms/filtersets.py:1335
@ -2017,7 +2018,7 @@ msgstr ""
msgid "Swapped terminations for circuit {circuit}." msgid "Swapped terminations for circuit {circuit}."
msgstr "" msgstr ""
#: netbox/core/api/views.py:51 #: netbox/core/api/views.py:50
msgid "This user does not have permission to synchronize this data source." msgid "This user does not have permission to synchronize this data source."
msgstr "" msgstr ""
@ -2748,7 +2749,7 @@ msgid ""
"enqueue() cannot be called with values for both schedule_at and immediate." "enqueue() cannot be called with values for both schedule_at and immediate."
msgstr "" msgstr ""
#: netbox/core/signals.py:125 #: netbox/core/signals.py:131
#, python-brace-format #, python-brace-format
msgid "Deletion is prevented by a protection rule: {message}" msgid "Deletion is prevented by a protection rule: {message}"
msgstr "" msgstr ""
@ -2913,7 +2914,7 @@ msgstr ""
msgid "No workers found" msgid "No workers found"
msgstr "" msgstr ""
#: netbox/core/utils.py:84 netbox/core/utils.py:150 netbox/core/views.py:398 #: netbox/core/utils.py:84 netbox/core/utils.py:150 netbox/core/views.py:393
#, python-brace-format #, python-brace-format
msgid "Job {job_id} not found" msgid "Job {job_id} not found"
msgstr "" msgstr ""
@ -2923,51 +2924,51 @@ msgstr ""
msgid "Job {id} not found." msgid "Job {id} not found."
msgstr "" msgstr ""
#: netbox/core/views.py:89 #: netbox/core/views.py:84
#, python-brace-format #, python-brace-format
msgid "Queued job #{id} to sync {datasource}" msgid "Queued job #{id} to sync {datasource}"
msgstr "" msgstr ""
#: netbox/core/views.py:334 #: netbox/core/views.py:329
#, python-brace-format #, python-brace-format
msgid "Restored configuration revision #{id}" msgid "Restored configuration revision #{id}"
msgstr "" msgstr ""
#: netbox/core/views.py:437 #: netbox/core/views.py:432
#, python-brace-format #, python-brace-format
msgid "Job {id} has been deleted." msgid "Job {id} has been deleted."
msgstr "" msgstr ""
#: netbox/core/views.py:439 #: netbox/core/views.py:434
#, python-brace-format #, python-brace-format
msgid "Error deleting job {id}: {error}" msgid "Error deleting job {id}: {error}"
msgstr "" msgstr ""
#: netbox/core/views.py:448 #: netbox/core/views.py:443
#, python-brace-format #, python-brace-format
msgid "Job {id} has been re-enqueued." msgid "Job {id} has been re-enqueued."
msgstr "" msgstr ""
#: netbox/core/views.py:457 #: netbox/core/views.py:452
#, python-brace-format #, python-brace-format
msgid "Job {id} has been enqueued." msgid "Job {id} has been enqueued."
msgstr "" msgstr ""
#: netbox/core/views.py:466 #: netbox/core/views.py:461
#, python-brace-format #, python-brace-format
msgid "Job {id} has been stopped." msgid "Job {id} has been stopped."
msgstr "" msgstr ""
#: netbox/core/views.py:468 #: netbox/core/views.py:463
#, python-brace-format #, python-brace-format
msgid "Failed to stop job {id}" msgid "Failed to stop job {id}"
msgstr "" msgstr ""
#: netbox/core/views.py:603 #: netbox/core/views.py:598
msgid "Plugins catalog could not be loaded" msgid "Plugins catalog could not be loaded"
msgstr "" msgstr ""
#: netbox/core/views.py:639 #: netbox/core/views.py:634
#, python-brace-format #, python-brace-format
msgid "Plugin {name} not found" msgid "Plugin {name} not found"
msgstr "" msgstr ""
@ -3106,7 +3107,7 @@ msgstr ""
#: netbox/templates/tenancy/tenantgroup.html:37 #: netbox/templates/tenancy/tenantgroup.html:37
#: netbox/templates/virtualization/vminterface.html:39 #: netbox/templates/virtualization/vminterface.html:39
#: netbox/templates/wireless/wirelesslangroup.html:37 #: netbox/templates/wireless/wirelesslangroup.html:37
#: netbox/tenancy/forms/bulk_edit.py:27 netbox/tenancy/forms/bulk_edit.py:62 #: netbox/tenancy/forms/bulk_edit.py:27 netbox/tenancy/forms/bulk_edit.py:67
#: netbox/tenancy/forms/bulk_import.py:24 #: netbox/tenancy/forms/bulk_import.py:24
#: netbox/tenancy/forms/bulk_import.py:58 #: netbox/tenancy/forms/bulk_import.py:58
#: netbox/tenancy/forms/model_forms.py:25 #: netbox/tenancy/forms/model_forms.py:25
@ -4193,8 +4194,8 @@ msgstr ""
#: netbox/dcim/forms/bulk_edit.py:465 netbox/dcim/forms/bulk_edit.py:972 #: netbox/dcim/forms/bulk_edit.py:465 netbox/dcim/forms/bulk_edit.py:972
#: netbox/dcim/forms/bulk_import.py:350 netbox/dcim/forms/bulk_import.py:353 #: netbox/dcim/forms/bulk_import.py:350 netbox/dcim/forms/bulk_import.py:353
#: netbox/dcim/forms/bulk_import.py:611 netbox/dcim/forms/bulk_import.py:1526 #: netbox/dcim/forms/bulk_import.py:611 netbox/dcim/forms/bulk_import.py:1586
#: netbox/dcim/forms/bulk_import.py:1530 netbox/dcim/forms/filtersets.py:106 #: netbox/dcim/forms/bulk_import.py:1590 netbox/dcim/forms/filtersets.py:106
#: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:407 #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:407
#: netbox/dcim/forms/filtersets.py:421 netbox/dcim/forms/filtersets.py:459 #: netbox/dcim/forms/filtersets.py:421 netbox/dcim/forms/filtersets.py:459
#: netbox/dcim/forms/filtersets.py:792 netbox/dcim/forms/filtersets.py:1005 #: netbox/dcim/forms/filtersets.py:792 netbox/dcim/forms/filtersets.py:1005
@ -4394,8 +4395,8 @@ msgstr ""
msgid "Length" msgid "Length"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_edit.py:875 netbox/dcim/forms/bulk_import.py:1394 #: netbox/dcim/forms/bulk_edit.py:875 netbox/dcim/forms/bulk_import.py:1408
#: netbox/dcim/forms/bulk_import.py:1397 netbox/dcim/forms/filtersets.py:1140 #: netbox/dcim/forms/bulk_import.py:1411 netbox/dcim/forms/filtersets.py:1140
msgid "Length unit" msgid "Length unit"
msgstr "" msgstr ""
@ -4404,17 +4405,17 @@ msgstr ""
msgid "Domain" msgid "Domain"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_edit.py:967 netbox/dcim/forms/bulk_import.py:1513 #: netbox/dcim/forms/bulk_edit.py:967 netbox/dcim/forms/bulk_import.py:1573
#: netbox/dcim/forms/filtersets.py:1226 netbox/dcim/forms/model_forms.py:855 #: netbox/dcim/forms/filtersets.py:1226 netbox/dcim/forms/model_forms.py:855
msgid "Power panel" msgid "Power panel"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_edit.py:989 netbox/dcim/forms/bulk_import.py:1549 #: netbox/dcim/forms/bulk_edit.py:989 netbox/dcim/forms/bulk_import.py:1609
#: netbox/dcim/forms/filtersets.py:1248 netbox/templates/dcim/powerfeed.html:83 #: netbox/dcim/forms/filtersets.py:1248 netbox/templates/dcim/powerfeed.html:83
msgid "Supply" msgid "Supply"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_edit.py:995 netbox/dcim/forms/bulk_import.py:1554 #: netbox/dcim/forms/bulk_edit.py:995 netbox/dcim/forms/bulk_import.py:1614
#: netbox/dcim/forms/filtersets.py:1253 netbox/templates/dcim/powerfeed.html:95 #: netbox/dcim/forms/filtersets.py:1253 netbox/templates/dcim/powerfeed.html:95
msgid "Phase" msgid "Phase"
msgstr "" msgstr ""
@ -4652,7 +4653,7 @@ msgid "available options"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:137 netbox/dcim/forms/bulk_import.py:601 #: netbox/dcim/forms/bulk_import.py:137 netbox/dcim/forms/bulk_import.py:601
#: netbox/dcim/forms/bulk_import.py:1510 netbox/ipam/forms/bulk_import.py:479 #: netbox/dcim/forms/bulk_import.py:1570 netbox/ipam/forms/bulk_import.py:479
#: netbox/virtualization/forms/bulk_import.py:64 #: netbox/virtualization/forms/bulk_import.py:64
#: netbox/virtualization/forms/bulk_import.py:95 #: netbox/virtualization/forms/bulk_import.py:95
msgid "Assigned site" msgid "Assigned site"
@ -4715,7 +4716,7 @@ msgstr ""
msgid "Parent site" msgid "Parent site"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:347 netbox/dcim/forms/bulk_import.py:1523 #: netbox/dcim/forms/bulk_import.py:347 netbox/dcim/forms/bulk_import.py:1583
msgid "Rack's location (if any)" msgid "Rack's location (if any)"
msgstr "" msgstr ""
@ -4766,7 +4767,7 @@ msgstr ""
msgid "Limit platform assignments to this manufacturer" msgid "Limit platform assignments to this manufacturer"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:534 netbox/dcim/forms/bulk_import.py:1593 #: netbox/dcim/forms/bulk_import.py:534 netbox/dcim/forms/bulk_import.py:1653
#: netbox/tenancy/forms/bulk_import.py:105 #: netbox/tenancy/forms/bulk_import.py:105
msgid "Assigned role" msgid "Assigned role"
msgstr "" msgstr ""
@ -4956,7 +4957,7 @@ msgid "Corresponding rear port"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1020 netbox/dcim/forms/bulk_import.py:1061 #: netbox/dcim/forms/bulk_import.py:1020 netbox/dcim/forms/bulk_import.py:1061
#: netbox/dcim/forms/bulk_import.py:1384 #: netbox/dcim/forms/bulk_import.py:1398
msgid "Physical medium classification" msgid "Physical medium classification"
msgstr "" msgstr ""
@ -5045,102 +5046,131 @@ msgid "Must specify the parent device or VM when assigning an interface"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1339 #: netbox/dcim/forms/bulk_import.py:1339
msgid "Side A site"
msgstr ""
#: netbox/dcim/forms/bulk_import.py:1343
#: netbox/wireless/forms/bulk_import.py:94
msgid "Site of parent device A (if any)"
msgstr ""
#: netbox/dcim/forms/bulk_import.py:1346
msgid "Side A device" msgid "Side A device"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1342 netbox/dcim/forms/bulk_import.py:1360 #: netbox/dcim/forms/bulk_import.py:1349 netbox/dcim/forms/bulk_import.py:1374
msgid "Device name" msgid "Device name"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1345 #: netbox/dcim/forms/bulk_import.py:1352
msgid "Side A type" msgid "Side A type"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1351 #: netbox/dcim/forms/bulk_import.py:1358
msgid "Side A name" msgid "Side A name"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1352 netbox/dcim/forms/bulk_import.py:1370 #: netbox/dcim/forms/bulk_import.py:1359 netbox/dcim/forms/bulk_import.py:1384
msgid "Termination name" msgid "Termination name"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1357 #: netbox/dcim/forms/bulk_import.py:1364
msgid "Side B site"
msgstr ""
#: netbox/dcim/forms/bulk_import.py:1368
#: netbox/wireless/forms/bulk_import.py:115
msgid "Site of parent device B (if any)"
msgstr ""
#: netbox/dcim/forms/bulk_import.py:1371
msgid "Side B device" msgid "Side B device"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1363 #: netbox/dcim/forms/bulk_import.py:1377
msgid "Side B type" msgid "Side B type"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1369 #: netbox/dcim/forms/bulk_import.py:1383
msgid "Side B name" msgid "Side B name"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1378 #: netbox/dcim/forms/bulk_import.py:1392
#: netbox/wireless/forms/bulk_import.py:134 #: netbox/wireless/forms/bulk_import.py:134
msgid "Connection status" msgid "Connection status"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1430 #: netbox/dcim/forms/bulk_import.py:1417
msgid "Color name (e.g. \"Red\") or hex code (e.g. \"f44336\")"
msgstr ""
#: netbox/dcim/forms/bulk_import.py:1469
#, python-brace-format #, python-brace-format
msgid "Side {side_upper}: {device} {termination_object} is already connected" msgid "Side {side_upper}: {device} {termination_object} is already connected"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1436 #: netbox/dcim/forms/bulk_import.py:1475
#, python-brace-format #, python-brace-format
msgid "{side_upper} side termination not found: {device} {name}" msgid "{side_upper} side termination not found: {device} {name}"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1461 netbox/dcim/forms/model_forms.py:891 #: netbox/dcim/forms/bulk_import.py:1496
#, python-brace-format
msgid ""
"{color} did not match any used color name and was longer than six "
"characters: invalid hex."
msgstr ""
#: netbox/dcim/forms/bulk_import.py:1521 netbox/dcim/forms/model_forms.py:891
#: netbox/dcim/tables/devices.py:1069 netbox/templates/dcim/device.html:138 #: netbox/dcim/tables/devices.py:1069 netbox/templates/dcim/device.html:138
#: netbox/templates/dcim/virtualchassis.html:27 #: netbox/templates/dcim/virtualchassis.html:27
#: netbox/templates/dcim/virtualchassis.html:67 #: netbox/templates/dcim/virtualchassis.html:67
msgid "Master" msgid "Master"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1465 #: netbox/dcim/forms/bulk_import.py:1525
msgid "Master device" msgid "Master device"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1482 #: netbox/dcim/forms/bulk_import.py:1542
msgid "Name of parent site" msgid "Name of parent site"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1516 #: netbox/dcim/forms/bulk_import.py:1576
msgid "Upstream power panel" msgid "Upstream power panel"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1546 #: netbox/dcim/forms/bulk_import.py:1606
msgid "Primary or redundant" msgid "Primary or redundant"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1551 #: netbox/dcim/forms/bulk_import.py:1611
msgid "Supply type (AC/DC)" msgid "Supply type (AC/DC)"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1556 #: netbox/dcim/forms/bulk_import.py:1616
msgid "Single or three-phase" msgid "Single or three-phase"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1607 netbox/dcim/forms/model_forms.py:1847 #: netbox/dcim/forms/bulk_import.py:1667 netbox/dcim/forms/model_forms.py:1847
#: netbox/templates/dcim/device.html:196 #: netbox/templates/dcim/device.html:196
#: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/dcim/virtualdevicecontext.html:30
#: netbox/templates/virtualization/virtualmachine.html:52 #: netbox/templates/virtualization/virtualmachine.html:52
msgid "Primary IPv4" msgid "Primary IPv4"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1611 #: netbox/dcim/forms/bulk_import.py:1671
msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgid "IPv4 address with mask, e.g. 1.2.3.4/24"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1614 netbox/dcim/forms/model_forms.py:1856 #: netbox/dcim/forms/bulk_import.py:1674 netbox/dcim/forms/model_forms.py:1856
#: netbox/templates/dcim/device.html:212 #: netbox/templates/dcim/device.html:212
#: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/dcim/virtualdevicecontext.html:41
#: netbox/templates/virtualization/virtualmachine.html:68 #: netbox/templates/virtualization/virtualmachine.html:68
msgid "Primary IPv6" msgid "Primary IPv6"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_import.py:1618 #: netbox/dcim/forms/bulk_import.py:1678
msgid "IPv6 address with prefix length, e.g. 2001:db8::1/64" msgid "IPv6 address with prefix length, e.g. 2001:db8::1/64"
msgstr "" msgstr ""
@ -7784,7 +7814,7 @@ msgid "No"
msgstr "" msgstr ""
#: netbox/extras/choices.py:108 netbox/templates/tenancy/contact.html:67 #: netbox/extras/choices.py:108 netbox/templates/tenancy/contact.html:67
#: netbox/tenancy/forms/bulk_edit.py:125 #: netbox/tenancy/forms/bulk_edit.py:130
#: netbox/wireless/forms/model_forms.py:173 #: netbox/wireless/forms/model_forms.py:173
msgid "Link" msgid "Link"
msgstr "" msgstr ""
@ -8146,7 +8176,7 @@ msgstr ""
#: netbox/extras/forms/bulk_edit.py:155 netbox/extras/forms/bulk_edit.py:354 #: netbox/extras/forms/bulk_edit.py:155 netbox/extras/forms/bulk_edit.py:354
#: netbox/extras/forms/filtersets.py:192 netbox/extras/forms/filtersets.py:470 #: netbox/extras/forms/filtersets.py:192 netbox/extras/forms/filtersets.py:470
#: netbox/extras/models/mixins.py:100 #: netbox/extras/models/mixins.py:101
msgid "MIME type" msgid "MIME type"
msgstr "" msgstr ""
@ -8325,7 +8355,7 @@ msgstr ""
#: netbox/netbox/navigation/menu.py:433 #: netbox/netbox/navigation/menu.py:433
#: netbox/templates/extras/notificationgroup.html:31 #: netbox/templates/extras/notificationgroup.html:31
#: netbox/templates/tenancy/contact.html:21 #: netbox/templates/tenancy/contact.html:21
#: netbox/tenancy/forms/bulk_edit.py:139 netbox/tenancy/forms/filtersets.py:78 #: netbox/tenancy/forms/bulk_edit.py:144 netbox/tenancy/forms/filtersets.py:78
#: netbox/tenancy/forms/model_forms.py:99 netbox/tenancy/tables/contacts.py:68 #: netbox/tenancy/forms/model_forms.py:99 netbox/tenancy/tables/contacts.py:68
#: netbox/users/forms/model_forms.py:182 netbox/users/forms/model_forms.py:194 #: netbox/users/forms/model_forms.py:182 netbox/users/forms/model_forms.py:194
#: netbox/users/forms/model_forms.py:306 netbox/users/tables.py:35 #: netbox/users/forms/model_forms.py:306 netbox/users/tables.py:35
@ -9025,51 +9055,51 @@ msgstr ""
msgid "dashboards" msgid "dashboards"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:85 #: netbox/extras/models/mixins.py:86
msgid "template code" msgid "template code"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:86 #: netbox/extras/models/mixins.py:87
msgid "Jinja template code." msgid "Jinja template code."
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:89 #: netbox/extras/models/mixins.py:90
msgid "environment parameters" msgid "environment parameters"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:94 #: netbox/extras/models/mixins.py:95
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Any <a href=\"{url}\">additional parameters</a> to pass when constructing " "Any <a href=\"{url}\">additional parameters</a> to pass when constructing "
"the Jinja environment" "the Jinja environment"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:101 #: netbox/extras/models/mixins.py:102
#, python-brace-format #, python-brace-format
msgid "Defaults to <code>{default}</code>" msgid "Defaults to <code>{default}</code>"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:106 #: netbox/extras/models/mixins.py:107
msgid "Filename to give to the rendered export file" msgid "Filename to give to the rendered export file"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:109 #: netbox/extras/models/mixins.py:110
msgid "file extension" msgid "file extension"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:112 #: netbox/extras/models/mixins.py:113
msgid "Extension to append to the rendered filename" msgid "Extension to append to the rendered filename"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:115 #: netbox/extras/models/mixins.py:116
msgid "as attachment" msgid "as attachment"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:117 #: netbox/extras/models/mixins.py:118
msgid "Download file as attachment" msgid "Download file as attachment"
msgstr "" msgstr ""
#: netbox/extras/models/mixins.py:124 #: netbox/extras/models/mixins.py:125
#, python-brace-format #, python-brace-format
msgid "{class_name} must implement a get_context() method." msgid "{class_name} must implement a get_context() method."
msgstr "" msgstr ""
@ -9855,7 +9885,7 @@ msgstr ""
#: netbox/ipam/filtersets.py:466 netbox/ipam/filtersets.py:470 #: netbox/ipam/filtersets.py:466 netbox/ipam/filtersets.py:470
#: netbox/ipam/filtersets.py:562 netbox/ipam/forms/model_forms.py:506 #: netbox/ipam/filtersets.py:562 netbox/ipam/forms/model_forms.py:506
#: netbox/templates/tenancy/contact.html:63 #: netbox/templates/tenancy/contact.html:63
#: netbox/tenancy/forms/bulk_edit.py:120 #: netbox/tenancy/forms/bulk_edit.py:125
msgid "Address" msgid "Address"
msgstr "" msgstr ""
@ -12311,7 +12341,7 @@ msgstr ""
#: netbox/templates/account/profile.html:27 #: netbox/templates/account/profile.html:27
#: netbox/templates/tenancy/contact.html:53 netbox/templates/users/user.html:23 #: netbox/templates/tenancy/contact.html:53 netbox/templates/users/user.html:23
#: netbox/tenancy/forms/bulk_edit.py:116 #: netbox/tenancy/forms/bulk_edit.py:121
msgid "Email" msgid "Email"
msgstr "" msgstr ""
@ -14826,7 +14856,7 @@ msgid ""
msgstr "" msgstr ""
#: netbox/templates/tenancy/contact.html:18 netbox/tenancy/filtersets.py:152 #: netbox/templates/tenancy/contact.html:18 netbox/tenancy/filtersets.py:152
#: netbox/tenancy/forms/bulk_edit.py:149 netbox/tenancy/forms/filtersets.py:102 #: netbox/tenancy/forms/bulk_edit.py:154 netbox/tenancy/forms/filtersets.py:102
#: netbox/tenancy/forms/forms.py:57 netbox/tenancy/forms/model_forms.py:108 #: netbox/tenancy/forms/forms.py:57 netbox/tenancy/forms/model_forms.py:108
#: netbox/tenancy/forms/model_forms.py:132 #: netbox/tenancy/forms/model_forms.py:132
#: netbox/tenancy/tables/contacts.py:106 #: netbox/tenancy/tables/contacts.py:106
@ -14834,12 +14864,12 @@ msgid "Contact"
msgstr "" msgstr ""
#: netbox/templates/tenancy/contact.html:39 #: netbox/templates/tenancy/contact.html:39
#: netbox/tenancy/forms/bulk_edit.py:106 #: netbox/tenancy/forms/bulk_edit.py:111
msgid "Title" msgid "Title"
msgstr "" msgstr ""
#: netbox/templates/tenancy/contact.html:43 #: netbox/templates/tenancy/contact.html:43
#: netbox/tenancy/forms/bulk_edit.py:111 netbox/tenancy/tables/contacts.py:72 #: netbox/tenancy/forms/bulk_edit.py:116 netbox/tenancy/tables/contacts.py:72
msgid "Phone" msgid "Phone"
msgstr "" msgstr ""
@ -15208,15 +15238,15 @@ msgstr ""
msgid "Tenant Group (slug)" msgid "Tenant Group (slug)"
msgstr "" msgstr ""
#: netbox/tenancy/forms/bulk_edit.py:67 #: netbox/tenancy/forms/bulk_edit.py:72
msgid "Desciption" msgid "Desciption"
msgstr "" msgstr ""
#: netbox/tenancy/forms/bulk_edit.py:96 #: netbox/tenancy/forms/bulk_edit.py:101
msgid "Add groups" msgid "Add groups"
msgstr "" msgstr ""
#: netbox/tenancy/forms/bulk_edit.py:101 #: netbox/tenancy/forms/bulk_edit.py:106
msgid "Remove groups" msgid "Remove groups"
msgstr "" msgstr ""
@ -16673,10 +16703,6 @@ msgstr ""
msgid "Bridged VLAN" msgid "Bridged VLAN"
msgstr "" msgstr ""
#: netbox/wireless/forms/bulk_import.py:94
msgid "Site of parent device A (if any)"
msgstr ""
#: netbox/wireless/forms/bulk_import.py:100 #: netbox/wireless/forms/bulk_import.py:100
msgid "Parent device of assigned interface A" msgid "Parent device of assigned interface A"
msgstr "" msgstr ""
@ -16690,10 +16716,6 @@ msgstr ""
msgid "Assigned interface A" msgid "Assigned interface A"
msgstr "" msgstr ""
#: netbox/wireless/forms/bulk_import.py:115
msgid "Site of parent device B (if any)"
msgstr ""
#: netbox/wireless/forms/bulk_import.py:121 #: netbox/wireless/forms/bulk_import.py:121
msgid "Parent device of assigned interface B" msgid "Parent device of assigned interface B"
msgstr "" msgstr ""

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

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

View File

@ -0,0 +1,17 @@
# Generated by Django 5.2.4 on 2025-07-23 17:28
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0009_update_group_perms'),
]
operations = [
migrations.AlterModelOptions(
name='token',
options={'ordering': ('-created',)},
),
]

View File

@ -5,7 +5,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
('contenttypes', '0002_remove_content_type_name'), ('contenttypes', '0002_remove_content_type_name'),
('users', '0009_update_group_perms'), ('users', '0010_add_token_meta_ordering'),
] ]
operations = [ operations = [

View File

@ -74,6 +74,7 @@ class Token(models.Model):
class Meta: class Meta:
verbose_name = _('token') verbose_name = _('token')
verbose_name_plural = _('tokens') verbose_name_plural = _('tokens')
ordering = ('-created',)
def __str__(self): def __str__(self):
return self.key if settings.ALLOW_TOKEN_RETRIEVAL else self.partial return self.key if settings.ALLOW_TOKEN_RETRIEVAL else self.partial

View File

@ -13,6 +13,8 @@ FILTER_CHAR_BASED_LOOKUP_MAP = dict(
ie='iexact', ie='iexact',
nie='iexact', nie='iexact',
empty='empty', empty='empty',
regex='regex',
iregex='iregex',
) )
FILTER_NUMERIC_BASED_LOOKUP_MAP = dict( FILTER_NUMERIC_BASED_LOOKUP_MAP = dict(

View File

@ -67,5 +67,8 @@ def reapply_model_ordering(queryset: QuerySet) -> QuerySet:
# MPTT-based models are exempt from this; use caution when annotating querysets of these models # MPTT-based models are exempt from this; use caution when annotating querysets of these models
if any(isinstance(manager, TreeManager) for manager in queryset.model._meta.local_managers): if any(isinstance(manager, TreeManager) for manager in queryset.model._meta.local_managers):
return queryset return queryset
elif queryset.ordered:
return queryset
ordering = queryset.model._meta.ordering ordering = queryset.model._meta.ordering
return queryset.order_by(*ordering) return queryset.order_by(*ordering)

View File

@ -180,6 +180,10 @@ class BaseFilterSetTest(TestCase):
self.assertEqual(self.filters['charfield__niew'].exclude, True) self.assertEqual(self.filters['charfield__niew'].exclude, True)
self.assertEqual(self.filters['charfield__empty'].lookup_expr, 'empty') self.assertEqual(self.filters['charfield__empty'].lookup_expr, 'empty')
self.assertEqual(self.filters['charfield__empty'].exclude, False) self.assertEqual(self.filters['charfield__empty'].exclude, False)
self.assertEqual(self.filters['charfield__regex'].lookup_expr, 'regex')
self.assertEqual(self.filters['charfield__regex'].exclude, False)
self.assertEqual(self.filters['charfield__iregex'].lookup_expr, 'iregex')
self.assertEqual(self.filters['charfield__iregex'].exclude, False)
def test_number_filter(self): def test_number_filter(self):
self.assertIsInstance(self.filters['numberfield'], django_filters.NumberFilter) self.assertIsInstance(self.filters['numberfield'], django_filters.NumberFilter)
@ -220,6 +224,10 @@ class BaseFilterSetTest(TestCase):
self.assertEqual(self.filters['macaddressfield__iew'].exclude, False) self.assertEqual(self.filters['macaddressfield__iew'].exclude, False)
self.assertEqual(self.filters['macaddressfield__niew'].lookup_expr, 'iendswith') self.assertEqual(self.filters['macaddressfield__niew'].lookup_expr, 'iendswith')
self.assertEqual(self.filters['macaddressfield__niew'].exclude, True) self.assertEqual(self.filters['macaddressfield__niew'].exclude, True)
self.assertEqual(self.filters['macaddressfield__regex'].lookup_expr, 'regex')
self.assertEqual(self.filters['macaddressfield__regex'].exclude, False)
self.assertEqual(self.filters['macaddressfield__iregex'].lookup_expr, 'iregex')
self.assertEqual(self.filters['macaddressfield__iregex'].exclude, False)
def test_model_choice_filter(self): def test_model_choice_filter(self):
self.assertIsInstance(self.filters['modelchoicefield'], django_filters.ModelChoiceFilter) self.assertIsInstance(self.filters['modelchoicefield'], django_filters.ModelChoiceFilter)
@ -257,6 +265,10 @@ class BaseFilterSetTest(TestCase):
self.assertEqual(self.filters['multivaluecharfield__iew'].exclude, False) self.assertEqual(self.filters['multivaluecharfield__iew'].exclude, False)
self.assertEqual(self.filters['multivaluecharfield__niew'].lookup_expr, 'iendswith') self.assertEqual(self.filters['multivaluecharfield__niew'].lookup_expr, 'iendswith')
self.assertEqual(self.filters['multivaluecharfield__niew'].exclude, True) self.assertEqual(self.filters['multivaluecharfield__niew'].exclude, True)
self.assertEqual(self.filters['multivaluecharfield__regex'].lookup_expr, 'regex')
self.assertEqual(self.filters['multivaluecharfield__regex'].exclude, False)
self.assertEqual(self.filters['multivaluecharfield__iregex'].lookup_expr, 'iregex')
self.assertEqual(self.filters['multivaluecharfield__iregex'].exclude, False)
def test_multi_value_date_filter(self): def test_multi_value_date_filter(self):
self.assertIsInstance(self.filters['datefield'], MultiValueDateFilter) self.assertIsInstance(self.filters['datefield'], MultiValueDateFilter)
@ -340,6 +352,10 @@ class BaseFilterSetTest(TestCase):
self.assertEqual(self.filters['multiplechoicefield__iew'].exclude, False) self.assertEqual(self.filters['multiplechoicefield__iew'].exclude, False)
self.assertEqual(self.filters['multiplechoicefield__niew'].lookup_expr, 'iendswith') self.assertEqual(self.filters['multiplechoicefield__niew'].lookup_expr, 'iendswith')
self.assertEqual(self.filters['multiplechoicefield__niew'].exclude, True) self.assertEqual(self.filters['multiplechoicefield__niew'].exclude, True)
self.assertEqual(self.filters['multiplechoicefield__regex'].lookup_expr, 'regex')
self.assertEqual(self.filters['multiplechoicefield__regex'].exclude, False)
self.assertEqual(self.filters['multiplechoicefield__iregex'].lookup_expr, 'iregex')
self.assertEqual(self.filters['multiplechoicefield__iregex'].exclude, False)
def test_tag_filter(self): def test_tag_filter(self):
self.assertIsInstance(self.filters['tagfield'], TagFilter) self.assertIsInstance(self.filters['tagfield'], TagFilter)
@ -534,6 +550,14 @@ class DynamicFilterLookupExpressionTest(TestCase):
params = {'slug__niew': ['-1']} params = {'slug__niew': ['-1']}
self.assertEqual(SiteFilterSet(params, Site.objects.all()).qs.count(), 2) self.assertEqual(SiteFilterSet(params, Site.objects.all()).qs.count(), 2)
def test_site_slug_regex(self):
params = {'slug__regex': ['^def-[a-z]*-2$']}
self.assertEqual(SiteFilterSet(params, Site.objects.all()).qs.count(), 1)
def test_site_slug_iregex(self):
params = {'slug__iregex': ['^DEF-[a-z]*-2$']}
self.assertEqual(SiteFilterSet(params, Site.objects.all()).qs.count(), 1)
def test_provider_asn_lt(self): def test_provider_asn_lt(self):
params = {'asn__lt': [65101]} params = {'asn__lt': [65101]}
self.assertEqual(ASNFilterSet(params, ASN.objects.all()).qs.count(), 1) self.assertEqual(ASNFilterSet(params, ASN.objects.all()).qs.count(), 1)
@ -618,6 +642,14 @@ class DynamicFilterLookupExpressionTest(TestCase):
params = {'mac_address__nic': ['aa:', 'bb']} params = {'mac_address__nic': ['aa:', 'bb']}
self.assertEqual(DeviceFilterSet(params, Device.objects.all()).qs.count(), 1) self.assertEqual(DeviceFilterSet(params, Device.objects.all()).qs.count(), 1)
def test_device_mac_address_regex(self):
params = {'mac_address__regex': ['^cc.*:03$']}
self.assertEqual(DeviceFilterSet(params, Device.objects.all()).qs.count(), 1)
def test_device_mac_address_iregex(self):
params = {'mac_address__iregex': ['^CC.*:03$']}
self.assertEqual(DeviceFilterSet(params, Device.objects.all()).qs.count(), 1)
def test_interface_rf_role_empty(self): def test_interface_rf_role_empty(self):
params = {'rf_role__empty': 'true'} params = {'rf_role__empty': 'true'}
self.assertEqual(InterfaceFilterSet(params, Interface.objects.all()).qs.count(), 5) self.assertEqual(InterfaceFilterSet(params, Interface.objects.all()).qs.count(), 5)

View File

@ -3,7 +3,7 @@
[project] [project]
name = "netbox" name = "netbox"
version = "4.3.4" version = "4.3.5"
requires-python = ">=3.10" requires-python = ">=3.10"
authors = [ authors = [
{ name = "NetBox Community" } { name = "NetBox Community" }

View File

@ -20,21 +20,21 @@ drf-spectacular-sidecar==2025.7.1
feedparser==6.0.11 feedparser==6.0.11
gunicorn==23.0.0 gunicorn==23.0.0
Jinja2==3.1.6 Jinja2==3.1.6
jsonschema==4.24.0 jsonschema==4.25.0
Markdown==3.8.2 Markdown==3.8.2
mkdocs-material==9.6.15 mkdocs-material==9.6.16
mkdocstrings[python]==0.29.1 mkdocstrings[python]==0.30.0
netaddr==1.3.0 netaddr==1.3.0
nh3==0.2.22 nh3==0.3.0
Pillow==11.3.0 Pillow==11.3.0
psycopg[c,pool]==3.2.9 psycopg[c,pool]==3.2.9
PyYAML==6.0.2 PyYAML==6.0.2
requests==2.32.4 requests==2.32.4
rq==2.4.0 rq==2.4.1
social-auth-app-django==5.5.1 social-auth-app-django==5.5.1
social-auth-core==4.7.0 social-auth-core==4.7.0
strawberry-graphql==0.276.0 strawberry-graphql==0.278.0
strawberry-graphql-django==0.60.0 strawberry-graphql-django==0.65.1
svgwrite==1.4.3 svgwrite==1.4.3
tablib==3.8.0 tablib==3.8.0
tzdata==2025.2 tzdata==2025.2