diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 42a716ae7..b43968731 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.5.7 + placeholder: v3.5.8 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index b04fda1b6..5df3069ba 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.5.7 + placeholder: v3.5.8 validations: required: true - type: dropdown diff --git a/contrib/generated_schema.json b/contrib/generated_schema.json new file mode 100644 index 000000000..8dbcb2847 --- /dev/null +++ b/contrib/generated_schema.json @@ -0,0 +1,561 @@ +{ + "type": "object", + "additionalProperties": false, + "definitions": { + "airflow": { + "type": "string", + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "passive", + "mixed" + ] + }, + "weight-unit": { + "type": "string", + "enum": [ + "kg", + "g", + "lb", + "oz" + ] + }, + "subdevice-role": { + "type": "string", + "enum": [ + "parent", + "child" + ] + }, + "console-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ] + } + } + }, + "console-server-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ] + } + } + }, + "power-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "iec-60320-c6", + "iec-60320-c8", + "iec-60320-c14", + "iec-60320-c16", + "iec-60320-c20", + "iec-60320-c22", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15p", + "nema-5-15p", + "nema-5-20p", + "nema-5-30p", + "nema-5-50p", + "nema-6-15p", + "nema-6-20p", + "nema-6-30p", + "nema-6-50p", + "nema-10-30p", + "nema-10-50p", + "nema-14-20p", + "nema-14-30p", + "nema-14-50p", + "nema-14-60p", + "nema-15-15p", + "nema-15-20p", + "nema-15-30p", + "nema-15-50p", + "nema-15-60p", + "nema-l1-15p", + "nema-l5-15p", + "nema-l5-20p", + "nema-l5-30p", + "nema-l5-50p", + "nema-l6-15p", + "nema-l6-20p", + "nema-l6-30p", + "nema-l6-50p", + "nema-l10-30p", + "nema-l14-20p", + "nema-l14-30p", + "nema-l14-50p", + "nema-l14-60p", + "nema-l15-20p", + "nema-l15-30p", + "nema-l15-50p", + "nema-l15-60p", + "nema-l21-20p", + "nema-l21-30p", + "nema-l22-30p", + "cs6361c", + "cs6365c", + "cs8165c", + "cs8265c", + "cs8365c", + "cs8465c", + "ita-c", + "ita-e", + "ita-f", + "ita-ef", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "usb-3-b", + "usb-3-micro-b", + "dc-terminal", + "saf-d-grid", + "neutrik-powercon-20", + "neutrik-powercon-32", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other" + ] + } + } + }, + "power-outlet": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "iec-60320-c5", + "iec-60320-c7", + "iec-60320-c13", + "iec-60320-c15", + "iec-60320-c19", + "iec-60320-c21", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15r", + "nema-5-15r", + "nema-5-20r", + "nema-5-30r", + "nema-5-50r", + "nema-6-15r", + "nema-6-20r", + "nema-6-30r", + "nema-6-50r", + "nema-10-30r", + "nema-10-50r", + "nema-14-20r", + "nema-14-30r", + "nema-14-50r", + "nema-14-60r", + "nema-15-15r", + "nema-15-20r", + "nema-15-30r", + "nema-15-50r", + "nema-15-60r", + "nema-l1-15r", + "nema-l5-15r", + "nema-l5-20r", + "nema-l5-30r", + "nema-l5-50r", + "nema-l6-15r", + "nema-l6-20r", + "nema-l6-30r", + "nema-l6-50r", + "nema-l10-30r", + "nema-l14-20r", + "nema-l14-30r", + "nema-l14-50r", + "nema-l14-60r", + "nema-l15-20r", + "nema-l15-30r", + "nema-l15-50r", + "nema-l15-60r", + "nema-l21-20r", + "nema-l21-30r", + "nema-l22-30r", + "CS6360C", + "CS6364C", + "CS8164C", + "CS8264C", + "CS8364C", + "CS8464C", + "ita-e", + "ita-f", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "ita-multistandard", + "usb-a", + "usb-micro-b", + "usb-c", + "dc-terminal", + "hdot-cx", + "saf-d-grid", + "neutrik-powercon-20a", + "neutrik-powercon-32a", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other" + ] + }, + "feed-leg": { + "type": "string", + "enum": [ + "A", + "B", + "C" + ] + } + } + }, + "interface": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "virtual", + "bridge", + "lag", + "100base-fx", + "100base-lfx", + "100base-tx", + "100base-t1", + "1000base-t", + "2.5gbase-t", + "5gbase-t", + "10gbase-t", + "10gbase-cx4", + "1000base-x-gbic", + "1000base-x-sfp", + "10gbase-x-sfpp", + "10gbase-x-xfp", + "10gbase-x-xenpak", + "10gbase-x-x2", + "25gbase-x-sfp28", + "50gbase-x-sfp56", + "40gbase-x-qsfpp", + "50gbase-x-sfp28", + "100gbase-x-cfp", + "100gbase-x-cfp2", + "200gbase-x-cfp2", + "100gbase-x-cfp4", + "100gbase-x-cxp", + "100gbase-x-cpak", + "100gbase-x-dsfp", + "100gbase-x-sfpdd", + "100gbase-x-qsfp28", + "100gbase-x-qsfpdd", + "200gbase-x-qsfp56", + "200gbase-x-qsfpdd", + "400gbase-x-qsfpdd", + "400gbase-x-osfp", + "400gbase-x-cdfp", + "400gbase-x-cfp8", + "800gbase-x-qsfpdd", + "800gbase-x-osfp", + "1000base-kx", + "10gbase-kr", + "10gbase-kx4", + "25gbase-kr", + "40gbase-kr4", + "50gbase-kr", + "100gbase-kp4", + "100gbase-kr2", + "100gbase-kr4", + "ieee802.11a", + "ieee802.11g", + "ieee802.11n", + "ieee802.11ac", + "ieee802.11ad", + "ieee802.11ax", + "ieee802.11ay", + "ieee802.15.1", + "other-wireless", + "gsm", + "cdma", + "lte", + "sonet-oc3", + "sonet-oc12", + "sonet-oc48", + "sonet-oc192", + "sonet-oc768", + "sonet-oc1920", + "sonet-oc3840", + "1gfc-sfp", + "2gfc-sfp", + "4gfc-sfp", + "8gfc-sfpp", + "16gfc-sfpp", + "32gfc-sfp28", + "64gfc-qsfpp", + "128gfc-qsfp28", + "infiniband-sdr", + "infiniband-ddr", + "infiniband-qdr", + "infiniband-fdr10", + "infiniband-fdr", + "infiniband-edr", + "infiniband-hdr", + "infiniband-ndr", + "infiniband-xdr", + "t1", + "e1", + "t3", + "e3", + "xdsl", + "docsis", + "gpon", + "xg-pon", + "xgs-pon", + "ng-pon2", + "epon", + "10g-epon", + "cisco-stackwise", + "cisco-stackwise-plus", + "cisco-flexstack", + "cisco-flexstack-plus", + "cisco-stackwise-80", + "cisco-stackwise-160", + "cisco-stackwise-320", + "cisco-stackwise-480", + "cisco-stackwise-1t", + "juniper-vcp", + "extreme-summitstack", + "extreme-summitstack-128", + "extreme-summitstack-256", + "extreme-summitstack-512", + "other" + ] + }, + "poe_mode": { + "type": "string", + "enum": [ + "pd", + "pse" + ] + }, + "poe_type": { + "type": "string", + "enum": [ + "type1-ieee802.3af", + "type2-ieee802.3at", + "type3-ieee802.3bt", + "type4-ieee802.3bt", + "passive-24v-2pair", + "passive-24v-4pair", + "passive-48v-2pair", + "passive-48v-4pair" + ] + } + } + }, + "front-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "other" + ] + } + } + }, + "rear-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "other" + ] + } + } + } + } +} diff --git a/docs/development/release-checklist.md b/docs/development/release-checklist.md index 000948ee7..68b777111 100644 --- a/docs/development/release-checklist.md +++ b/docs/development/release-checklist.md @@ -70,6 +70,16 @@ Before each release, update each of NetBox's Python dependencies to its most rec In cases where upgrading a dependency to its most recent release is breaking, it should be constrained to its current minor version in `base_requirements.txt` with an explanatory comment and revisited for the next major NetBox release (see the [Address Constrained Dependencies](#address-constrained-dependencies) section above). +### Rebuild the Device Type Definition Schema + +Run the following command to update the device type definition validation schema: + +```nohighlight +./manage.py buildschema --write +``` + +This will automatically update the schema file at `contrib/generated_schema.json`. + ### Update Version and Changelog * Update the `VERSION` constant in `settings.py` to the new release version. diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index cf47a3b23..f7778275b 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -1,23 +1,33 @@ # NetBox v3.5 -## v3.5.8 (FUTURE) +## v3.5.9 (FUTURE) + +--- + +## v3.5.8 (2023-08-15) ### Enhancements +* [#10030](https://github.com/netbox-community/netbox/issues/10030) - Ship a validation schema for the device type library with each release * [#11675](https://github.com/netbox-community/netbox/issues/11675) - Add support for specifying import/export route targets during VRF bulk import * [#11922](https://github.com/netbox-community/netbox/issues/11922) - Automatically populate any VDC assignments from the parent when adding a child interface via the UI * [#12889](https://github.com/netbox-community/netbox/issues/12889) - Add 400GE CFP2 interface type * [#13033](https://github.com/netbox-community/netbox/issues/13033) - Add human-friendly speed column to interfaces table * [#13151](https://github.com/netbox-community/netbox/issues/13151) - Add "assigned" filter for IP addresses * [#13368](https://github.com/netbox-community/netbox/issues/13368) - List installed plugins on the server error report page +* [#13442](https://github.com/netbox-community/netbox/issues/13442) - Add 200 and 400 Gbps speeds to dropdown choices on interface form ### Bug Fixes +* [#11578](https://github.com/netbox-community/netbox/issues/11578) - Fix schema definition for available IP & VLAN REST API endpoints +* [#12639](https://github.com/netbox-community/netbox/issues/12639) - Raise validation error for invalid alphanumeric ranges when creating objects * [#12665](https://github.com/netbox-community/netbox/issues/12665) - Avoid escaping semicolons when rendering custom links * [#12750](https://github.com/netbox-community/netbox/issues/12750) - Automatically delete an AutoSyncRecord when its object is deleted * [#13343](https://github.com/netbox-community/netbox/issues/13343) - Fix filtering of circuits under provider network view * [#13369](https://github.com/netbox-community/netbox/issues/13369) - Fix job termination status for failed reports * [#13414](https://github.com/netbox-community/netbox/issues/13414) - Fix support for "hide-if-unset" custom fields on bulk import forms +* [#13446](https://github.com/netbox-community/netbox/issues/13446) - Don't disable bulk edit/delete buttons after deselecting "select all" checkbox +* [#13451](https://github.com/netbox-community/netbox/issues/13451) - Disable table ordering for custom link columns --- diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index ebd66135a..1bcf61b20 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -1143,6 +1143,8 @@ class InterfaceSpeedChoices(ChoiceSet): (25000000, '25 Gbps'), (40000000, '40 Gbps'), (100000000, '100 Gbps'), + (200000000, '200 Gbps'), + (400000000, '400 Gbps'), ] diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index b41e63b58..abd7bd6f6 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -55,7 +55,10 @@ class ComponentCreateForm(forms.Form): super().clean() # Validate that all replication fields generate an equal number of values - pattern_count = len(self.cleaned_data[self.replication_fields[0]]) + if not (patterns := self.cleaned_data.get(self.replication_fields[0])): + return + + pattern_count = len(patterns) for field_name in self.replication_fields: value_count = len(self.cleaned_data[field_name]) if self.cleaned_data[field_name] and value_count != pattern_count: diff --git a/netbox/dcim/management/commands/buildschema.py b/netbox/dcim/management/commands/buildschema.py new file mode 100644 index 000000000..44a0e95f2 --- /dev/null +++ b/netbox/dcim/management/commands/buildschema.py @@ -0,0 +1,62 @@ +import json +import os + +from django.conf import settings +from django.core.management.base import BaseCommand +from jinja2 import FileSystemLoader, Environment + +from dcim.choices import * + +TEMPLATE_FILENAME = 'devicetype_schema.jinja2' +OUTPUT_FILENAME = 'contrib/generated_schema.json' + +CHOICES_MAP = { + 'airflow_choices': DeviceAirflowChoices, + 'weight_unit_choices': WeightUnitChoices, + 'subdevice_role_choices': SubdeviceRoleChoices, + 'console_port_type_choices': ConsolePortTypeChoices, + 'console_server_port_type_choices': ConsolePortTypeChoices, + 'power_port_type_choices': PowerPortTypeChoices, + 'power_outlet_type_choices': PowerOutletTypeChoices, + 'power_outlet_feedleg_choices': PowerOutletFeedLegChoices, + 'interface_type_choices': InterfaceTypeChoices, + 'interface_poe_mode_choices': InterfacePoEModeChoices, + 'interface_poe_type_choices': InterfacePoETypeChoices, + 'front_port_type_choices': PortTypeChoices, + 'rear_port_type_choices': PortTypeChoices, +} + + +class Command(BaseCommand): + help = "Generate JSON schema for validating NetBox device type definitions" + + def add_arguments(self, parser): + parser.add_argument( + '--write', + action='store_true', + help="Write the generated schema to file" + ) + + def handle(self, *args, **kwargs): + # Initialize template + template_loader = FileSystemLoader(searchpath=f'{settings.TEMPLATES_DIR}/extras/schema/') + template_env = Environment(loader=template_loader) + template = template_env.get_template(TEMPLATE_FILENAME) + + # Render template + context = { + key: json.dumps(choices.values()) + for key, choices in CHOICES_MAP.items() + } + rendered = template.render(**context) + + if kwargs['write']: + # $root/contrib/generated_schema.json + filename = os.path.join(os.path.split(settings.BASE_DIR)[0], OUTPUT_FILENAME) + with open(filename, mode='w', encoding='UTF-8') as f: + f.write(json.dumps(json.loads(rendered), indent=4)) + f.write('\n') + f.close() + self.stdout.write(self.style.SUCCESS(f"Schema written to {filename}.")) + else: + self.stdout.write(rendered) diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 16b494dd5..da6463e23 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -1,7 +1,5 @@ from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.db import transaction -from django.db.models import F -from django.db.models.functions import Round from django.shortcuts import get_object_or_404 from django_pglocks import advisory_lock from drf_spectacular.utils import extend_schema @@ -16,6 +14,7 @@ from circuits.models import Provider from dcim.models import Site from ipam import filtersets from ipam.models import * +from ipam.models import L2VPN, L2VPNTermination from ipam.utils import get_next_available_prefix from netbox.api.viewsets import NetBoxModelViewSet from netbox.api.viewsets.mixins import ObjectValidationMixin @@ -24,7 +23,6 @@ from netbox.constants import ADVISORY_LOCK_KEYS from utilities.api import get_serializer_for_model from utilities.utils import count_related from . import serializers -from ipam.models import L2VPN, L2VPNTermination class IPAMRootView(APIRootView): @@ -346,7 +344,11 @@ class AvailableASNsView(AvailableObjectsView): def get(self, request, pk): return super().get(request, pk) - @extend_schema(methods=["post"], responses={201: serializers.AvailableASNSerializer(many=True)}) + @extend_schema( + methods=["post"], + responses={201: serializers.ASNSerializer(many=True)}, + request=serializers.ASNSerializer(many=True), + ) def post(self, request, pk): return super().post(request, pk) @@ -395,7 +397,11 @@ class AvailablePrefixesView(AvailableObjectsView): def get(self, request, pk): return super().get(request, pk) - @extend_schema(methods=["post"], responses={201: serializers.PrefixSerializer(many=True)}) + @extend_schema( + methods=["post"], + responses={201: serializers.PrefixSerializer(many=True)}, + request=serializers.PrefixSerializer(many=True), + ) def post(self, request, pk): return super().post(request, pk) @@ -435,7 +441,11 @@ class AvailableIPAddressesView(AvailableObjectsView): def get(self, request, pk): return super().get(request, pk) - @extend_schema(methods=["post"], responses={201: serializers.IPAddressSerializer(many=True)}) + @extend_schema( + methods=["post"], + responses={201: serializers.IPAddressSerializer(many=True)}, + request=serializers.IPAddressSerializer(many=True), + ) def post(self, request, pk): return super().post(request, pk) @@ -482,6 +492,10 @@ class AvailableVLANsView(AvailableObjectsView): def get(self, request, pk): return super().get(request, pk) - @extend_schema(methods=["post"], responses={201: serializers.VLANSerializer(many=True)}) + @extend_schema( + methods=["post"], + responses={201: serializers.VLANSerializer(many=True)}, + request=serializers.VLANSerializer(many=True), + ) def post(self, request, pk): return super().post(request, pk) diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index 299d70565..e4e967f81 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -256,7 +256,7 @@ class IPRangeFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): model = IPRange fieldsets = ( (None, ('q', 'filter_id', 'tag')), - (_('Attriubtes'), ('family', 'vrf_id', 'status', 'role_id', 'mark_utilized')), + (_('Attributes'), ('family', 'vrf_id', 'status', 'role_id', 'mark_utilized')), (_('Tenant'), ('tenant_group_id', 'tenant_id')), ) family = forms.ChoiceField( diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 92a556ed2..69ef8f52d 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -25,7 +25,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW # Environment setup # -VERSION = '3.6-beta1' +VERSION = '3.6-beta2' # Hostname HOSTNAME = platform.node() diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index bc67b0b95..6d6eeb97e 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -511,9 +511,9 @@ class CustomLinkColumn(tables.Column): """ def __init__(self, customlink, *args, **kwargs): self.customlink = customlink - kwargs['accessor'] = Accessor('pk') - if 'verbose_name' not in kwargs: - kwargs['verbose_name'] = customlink.name + kwargs.setdefault('accessor', Accessor('pk')) + kwargs.setdefault('orderable', False) + kwargs.setdefault('verbose_name', customlink.name) super().__init__(*args, **kwargs) diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index b62436d75..84bfecae3 100644 --- a/netbox/project-static/dist/netbox.js +++ b/netbox/project-static/dist/netbox.js @@ -1,11 +1,11 @@ -(()=>{var j_=Object.create;var ia=Object.defineProperty,H_=Object.defineProperties,B_=Object.getOwnPropertyDescriptor,F_=Object.getOwnPropertyDescriptors,z_=Object.getOwnPropertyNames,hh=Object.getOwnPropertySymbols,$_=Object.getPrototypeOf,ph=Object.prototype.hasOwnProperty,U_=Object.prototype.propertyIsEnumerable;var lu=(yr,br,mr)=>br in yr?ia(yr,br,{enumerable:!0,configurable:!0,writable:!0,value:mr}):yr[br]=mr,ci=(yr,br)=>{for(var mr in br||(br={}))ph.call(br,mr)&&lu(yr,mr,br[mr]);if(hh)for(var mr of hh(br))U_.call(br,mr)&&lu(yr,mr,br[mr]);return yr},al=(yr,br)=>H_(yr,F_(br)),mh=yr=>ia(yr,"__esModule",{value:!0});var jr=(yr,br)=>()=>(br||yr((br={exports:{}}).exports,br),br.exports),V_=(yr,br)=>{mh(yr);for(var mr in br)ia(yr,mr,{get:br[mr],enumerable:!0})},W_=(yr,br,mr)=>{if(br&&typeof br=="object"||typeof br=="function")for(let Er of z_(br))!ph.call(yr,Er)&&Er!=="default"&&ia(yr,Er,{get:()=>br[Er],enumerable:!(mr=B_(br,Er))||mr.enumerable});return yr},vn=yr=>W_(mh(ia(yr!=null?j_($_(yr)):{},"default",yr&&yr.__esModule&&"default"in yr?{get:()=>yr.default,enumerable:!0}:{value:yr,enumerable:!0})),yr);var di=(yr,br,mr)=>(lu(yr,typeof br!="symbol"?br+"":br,mr),mr);var $i=(yr,br,mr)=>new Promise((Er,wr)=>{var _r=Tr=>{try{Sr(mr.next(Tr))}catch(Dr){wr(Dr)}},xr=Tr=>{try{Sr(mr.throw(Tr))}catch(Dr){wr(Dr)}},Sr=Tr=>Tr.done?Er(Tr.value):Promise.resolve(Tr.value).then(_r,xr);Sr((mr=mr.apply(yr,br)).next())});var Jp=jr((exports,module)=>{(function(yr,br){typeof define=="function"&&define.amd?define([],br):yr.htmx=yr.htmx||br()})(typeof self!="undefined"?self:exports,function(){return function(){"use strict";var U={onLoad:t,process:vt,on:X,off:F,trigger:$,ajax:nr,find:R,findAll:O,closest:N,values:function(yr,br){var mr=Pt(yr,br||"post");return mr.values},remove:q,addClass:L,removeClass:T,toggleClass:A,takeClass:H,defineExtension:fr,removeExtension:cr,logAll:C,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:!1,scrollBehavior:"smooth",defaultFocusScroll:!1},parseInterval:v,_:e,createEventSource:function(yr){return new EventSource(yr,{withCredentials:!0})},createWebSocket:function(yr){return new WebSocket(yr,[])},version:"1.8.0"},r={addTriggerHandler:st,bodyContains:K,canAccessLocalStorage:E,filterValues:Ut,hasAttribute:o,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Qt,getHeaders:Bt,getInputValues:Pt,getInternalData:W,getSwapSpecification:_t,getTriggerSpecs:Me,getTarget:re,makeFragment:g,mergeObjects:Y,makeSettleInfo:Gt,oobSwap:ae,selectAndSwap:Ee,settleImmediately:Lt,shouldCancel:je,triggerEvent:$,triggerErrorEvent:J,withExtensions:xt},n=["get","post","put","delete","patch"],i=n.map(function(yr){return"[hx-"+yr+"], [data-hx-"+yr+"]"}).join(", ");function v(yr){if(yr!=null)return yr.slice(-2)=="ms"?parseFloat(yr.slice(0,-2))||void 0:yr.slice(-1)=="s"?parseFloat(yr.slice(0,-1))*1e3||void 0:yr.slice(-1)=="m"?parseFloat(yr.slice(0,-1))*1e3*60||void 0:parseFloat(yr)||void 0}function f(yr,br){return yr.getAttribute&&yr.getAttribute(br)}function o(yr,br){return yr.hasAttribute&&(yr.hasAttribute(br)||yr.hasAttribute("data-"+br))}function V(yr,br){return f(yr,br)||f(yr,"data-"+br)}function u(yr){return yr.parentElement}function _(){return document}function h(yr,br){for(;yr&&!br(yr);)yr=u(yr);return yr||null}function a(yr,br,mr){var Er=V(br,mr),wr=V(br,"hx-disinherit");return yr!==br&&wr&&(wr==="*"||wr.split(" ").indexOf(mr)>=0)?"unset":Er}function z(yr,br){var mr=null;if(h(yr,function(Er){return mr=a(yr,Er,br)}),mr!=="unset")return mr}function d(yr,br){var mr=yr.matches||yr.matchesSelector||yr.msMatchesSelector||yr.mozMatchesSelector||yr.webkitMatchesSelector||yr.oMatchesSelector;return mr&&mr.call(yr,br)}function s(yr){var br=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,mr=br.exec(yr);return mr?mr[1].toLowerCase():""}function l(yr,br){for(var mr=new DOMParser,Er=mr.parseFromString(yr,"text/html"),wr=Er.body;br>0;)br--,wr=wr.firstChild;return wr==null&&(wr=_().createDocumentFragment()),wr}function g(yr){if(U.config.useTemplateFragments){var br=l("
"+yr+"",0);return br.querySelector("template").content}else{var mr=s(yr);switch(mr){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l("