From 7702b0ebb0ccf0bbf9e3632d74bed36a96035324 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2024 15:04:46 -0400 Subject: [PATCH 01/42] PRVB --- docs/release-notes/version-4.0.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-4.0.md b/docs/release-notes/version-4.0.md index cfc3dc1975a..4537a8800f9 100644 --- a/docs/release-notes/version-4.0.md +++ b/docs/release-notes/version-4.0.md @@ -1,5 +1,9 @@ # NetBox v4.0 +## v4.0.7 (FUTURE) + +--- + ## v4.0.6 (2024-06-24) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 40974f64836..3a8e51e056c 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -25,7 +25,7 @@ # Environment setup # -VERSION = '4.0.6' +VERSION = '4.0.7-dev' HOSTNAME = platform.node() # Set the base directory two levels up BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From 65e40603ff970c42624af9e1bbffe1c1a05cc8c7 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 05:01:53 +0000 Subject: [PATCH 02/42] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 189 ++++++++++--------- 1 file changed, 99 insertions(+), 90 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index 6334d3b9bad..53282465404 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-22 05:02+0000\n" +"POT-Creation-Date: 2024-06-25 05:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -299,6 +299,7 @@ msgstr "" #: netbox/tenancy/filtersets.py:100 netbox/users/filtersets.py:23 #: netbox/users/filtersets.py:52 netbox/users/filtersets.py:92 #: netbox/users/filtersets.py:140 netbox/utilities/forms/forms.py:104 +#: netbox/utilities/templates/navigation/menu.html:16 msgid "Search" msgstr "" @@ -1095,7 +1096,7 @@ msgstr "" #: netbox/dcim/models/device_component_templates.py:61 #: netbox/dcim/models/device_components.py:69 netbox/dcim/models/racks.py:538 #: netbox/extras/models/configs.py:45 netbox/extras/models/configs.py:219 -#: netbox/extras/models/customfields.py:123 netbox/extras/models/models.py:60 +#: netbox/extras/models/customfields.py:124 netbox/extras/models/models.py:60 #: netbox/extras/models/models.py:186 netbox/extras/models/models.py:424 #: netbox/extras/models/models.py:539 netbox/extras/models/staging.py:32 #: netbox/extras/models/tags.py:32 netbox/netbox/models/__init__.py:109 @@ -1134,7 +1135,7 @@ msgstr "" #: netbox/dcim/models/power.py:39 netbox/dcim/models/power.py:92 #: netbox/dcim/models/racks.py:63 netbox/dcim/models/sites.py:138 #: netbox/extras/models/configs.py:36 netbox/extras/models/configs.py:215 -#: netbox/extras/models/customfields.py:90 netbox/extras/models/models.py:55 +#: netbox/extras/models/customfields.py:91 netbox/extras/models/models.py:55 #: netbox/extras/models/models.py:181 netbox/extras/models/models.py:324 #: netbox/extras/models/models.py:420 netbox/extras/models/models.py:529 #: netbox/extras/models/models.py:624 netbox/extras/models/scripts.py:30 @@ -1773,7 +1774,7 @@ msgstr "" #: netbox/dcim/models/device_components.py:606 #: netbox/dcim/models/device_components.py:971 #: netbox/dcim/models/device_components.py:1045 netbox/dcim/models/power.py:102 -#: netbox/dcim/models/racks.py:128 netbox/extras/models/customfields.py:76 +#: netbox/dcim/models/racks.py:128 netbox/extras/models/customfields.py:77 #: netbox/extras/models/search.py:41 #: netbox/virtualization/models/clusters.py:61 netbox/vpn/models/l2vpn.py:32 msgid "type" @@ -4536,7 +4537,7 @@ msgstr "" #: netbox/dcim/models/cables.py:62 #: netbox/dcim/models/device_component_templates.py:55 #: netbox/dcim/models/device_components.py:63 -#: netbox/extras/models/customfields.py:109 +#: netbox/extras/models/customfields.py:110 msgid "label" msgstr "" @@ -5581,7 +5582,7 @@ msgstr "" msgid "Numeric identifier unique to the parent device" msgstr "" -#: netbox/dcim/models/devices.py:1398 netbox/extras/models/customfields.py:210 +#: netbox/dcim/models/devices.py:1398 netbox/extras/models/customfields.py:211 #: netbox/extras/models/models.py:127 netbox/extras/models/models.py:722 #: netbox/netbox/models/__init__.py:114 msgid "comments" @@ -5923,43 +5924,43 @@ msgstr "" msgid "Parent location ({parent}) must belong to the same site ({site})." msgstr "" -#: netbox/dcim/tables/cables.py:54 +#: netbox/dcim/tables/cables.py:55 msgid "Termination A" msgstr "" -#: netbox/dcim/tables/cables.py:59 +#: netbox/dcim/tables/cables.py:60 msgid "Termination B" msgstr "" -#: netbox/dcim/tables/cables.py:65 netbox/wireless/tables/wirelesslink.py:22 +#: netbox/dcim/tables/cables.py:66 netbox/wireless/tables/wirelesslink.py:22 msgid "Device A" msgstr "" -#: netbox/dcim/tables/cables.py:71 netbox/wireless/tables/wirelesslink.py:31 +#: netbox/dcim/tables/cables.py:72 netbox/wireless/tables/wirelesslink.py:31 msgid "Device B" msgstr "" -#: netbox/dcim/tables/cables.py:77 +#: netbox/dcim/tables/cables.py:78 msgid "Location A" msgstr "" -#: netbox/dcim/tables/cables.py:83 +#: netbox/dcim/tables/cables.py:84 msgid "Location B" msgstr "" -#: netbox/dcim/tables/cables.py:89 +#: netbox/dcim/tables/cables.py:90 msgid "Rack A" msgstr "" -#: netbox/dcim/tables/cables.py:95 +#: netbox/dcim/tables/cables.py:96 msgid "Rack B" msgstr "" -#: netbox/dcim/tables/cables.py:101 +#: netbox/dcim/tables/cables.py:102 msgid "Site A" msgstr "" -#: netbox/dcim/tables/cables.py:107 +#: netbox/dcim/tables/cables.py:108 msgid "Site B" msgstr "" @@ -6856,13 +6857,13 @@ msgstr "" #: netbox/extras/forms/bulk_edit.py:53 netbox/extras/forms/bulk_import.py:57 #: netbox/extras/forms/filtersets.py:79 -#: netbox/extras/models/customfields.py:194 +#: netbox/extras/models/customfields.py:195 msgid "UI visible" msgstr "" #: netbox/extras/forms/bulk_edit.py:58 netbox/extras/forms/bulk_import.py:63 #: netbox/extras/forms/filtersets.py:84 -#: netbox/extras/models/customfields.py:201 +#: netbox/extras/models/customfields.py:202 msgid "UI editable" msgstr "" @@ -7462,112 +7463,112 @@ msgstr "" msgid "config templates" msgstr "" -#: netbox/extras/models/customfields.py:73 +#: netbox/extras/models/customfields.py:74 msgid "The object(s) to which this field applies." msgstr "" -#: netbox/extras/models/customfields.py:80 +#: netbox/extras/models/customfields.py:81 msgid "The type of data this custom field holds" msgstr "" -#: netbox/extras/models/customfields.py:87 +#: netbox/extras/models/customfields.py:88 msgid "The type of NetBox object this field maps to (for object fields)" msgstr "" -#: netbox/extras/models/customfields.py:93 +#: netbox/extras/models/customfields.py:94 msgid "Internal field name" msgstr "" -#: netbox/extras/models/customfields.py:97 +#: netbox/extras/models/customfields.py:98 msgid "Only alphanumeric characters and underscores are allowed." msgstr "" -#: netbox/extras/models/customfields.py:102 +#: netbox/extras/models/customfields.py:103 msgid "Double underscores are not permitted in custom field names." msgstr "" -#: netbox/extras/models/customfields.py:113 +#: netbox/extras/models/customfields.py:114 msgid "" "Name of the field as displayed to users (if not provided, 'the field's name " "will be used)" msgstr "" -#: netbox/extras/models/customfields.py:117 netbox/extras/models/models.py:345 +#: netbox/extras/models/customfields.py:118 netbox/extras/models/models.py:345 msgid "group name" msgstr "" -#: netbox/extras/models/customfields.py:120 +#: netbox/extras/models/customfields.py:121 msgid "Custom fields within the same group will be displayed together" msgstr "" -#: netbox/extras/models/customfields.py:128 +#: netbox/extras/models/customfields.py:129 msgid "required" msgstr "" -#: netbox/extras/models/customfields.py:130 +#: netbox/extras/models/customfields.py:131 msgid "" "If true, this field is required when creating new objects or editing an " "existing object." msgstr "" -#: netbox/extras/models/customfields.py:133 +#: netbox/extras/models/customfields.py:134 msgid "search weight" msgstr "" -#: netbox/extras/models/customfields.py:136 +#: netbox/extras/models/customfields.py:137 msgid "" "Weighting for search. Lower values are considered more important. Fields " "with a search weight of zero will be ignored." msgstr "" -#: netbox/extras/models/customfields.py:141 +#: netbox/extras/models/customfields.py:142 msgid "filter logic" msgstr "" -#: netbox/extras/models/customfields.py:145 +#: netbox/extras/models/customfields.py:146 msgid "" "Loose matches any instance of a given string; exact matches the entire field." msgstr "" -#: netbox/extras/models/customfields.py:148 +#: netbox/extras/models/customfields.py:149 msgid "default" msgstr "" -#: netbox/extras/models/customfields.py:152 +#: netbox/extras/models/customfields.py:153 msgid "" "Default value for the field (must be a JSON value). Encapsulate strings with " "double quotes (e.g. \"Foo\")." msgstr "" -#: netbox/extras/models/customfields.py:157 +#: netbox/extras/models/customfields.py:158 msgid "display weight" msgstr "" -#: netbox/extras/models/customfields.py:158 +#: netbox/extras/models/customfields.py:159 msgid "Fields with higher weights appear lower in a form." msgstr "" -#: netbox/extras/models/customfields.py:163 +#: netbox/extras/models/customfields.py:164 msgid "minimum value" msgstr "" -#: netbox/extras/models/customfields.py:164 +#: netbox/extras/models/customfields.py:165 msgid "Minimum allowed value (for numeric fields)" msgstr "" -#: netbox/extras/models/customfields.py:169 +#: netbox/extras/models/customfields.py:170 msgid "maximum value" msgstr "" -#: netbox/extras/models/customfields.py:170 +#: netbox/extras/models/customfields.py:171 msgid "Maximum allowed value (for numeric fields)" msgstr "" -#: netbox/extras/models/customfields.py:176 +#: netbox/extras/models/customfields.py:177 msgid "validation regex" msgstr "" -#: netbox/extras/models/customfields.py:178 +#: netbox/extras/models/customfields.py:179 #, python-brace-format msgid "" "Regular expression to enforce on text field values. Use ^ and $ to force " @@ -7575,168 +7576,168 @@ msgid "" "values to exactly three uppercase letters." msgstr "" -#: netbox/extras/models/customfields.py:186 +#: netbox/extras/models/customfields.py:187 msgid "choice set" msgstr "" -#: netbox/extras/models/customfields.py:195 +#: netbox/extras/models/customfields.py:196 msgid "Specifies whether the custom field is displayed in the UI" msgstr "" -#: netbox/extras/models/customfields.py:202 +#: netbox/extras/models/customfields.py:203 msgid "Specifies whether the custom field value can be edited in the UI" msgstr "" -#: netbox/extras/models/customfields.py:206 +#: netbox/extras/models/customfields.py:207 msgid "is cloneable" msgstr "" -#: netbox/extras/models/customfields.py:207 +#: netbox/extras/models/customfields.py:208 msgid "Replicate this value when cloning objects" msgstr "" -#: netbox/extras/models/customfields.py:224 +#: netbox/extras/models/customfields.py:225 msgid "custom field" msgstr "" -#: netbox/extras/models/customfields.py:225 +#: netbox/extras/models/customfields.py:226 msgid "custom fields" msgstr "" -#: netbox/extras/models/customfields.py:314 +#: netbox/extras/models/customfields.py:315 #, python-brace-format msgid "Invalid default value \"{value}\": {error}" msgstr "" -#: netbox/extras/models/customfields.py:321 +#: netbox/extras/models/customfields.py:322 msgid "A minimum value may be set only for numeric fields" msgstr "" -#: netbox/extras/models/customfields.py:323 +#: netbox/extras/models/customfields.py:324 msgid "A maximum value may be set only for numeric fields" msgstr "" -#: netbox/extras/models/customfields.py:333 +#: netbox/extras/models/customfields.py:334 msgid "Regular expression validation is supported only for text and URL fields" msgstr "" -#: netbox/extras/models/customfields.py:343 +#: netbox/extras/models/customfields.py:344 msgid "Selection fields must specify a set of choices." msgstr "" -#: netbox/extras/models/customfields.py:347 +#: netbox/extras/models/customfields.py:348 msgid "Choices may be set only on selection fields." msgstr "" -#: netbox/extras/models/customfields.py:354 +#: netbox/extras/models/customfields.py:355 msgid "Object fields must define an object type." msgstr "" -#: netbox/extras/models/customfields.py:359 +#: netbox/extras/models/customfields.py:360 #, python-brace-format msgid "{type} fields may not define an object type." msgstr "" -#: netbox/extras/models/customfields.py:439 +#: netbox/extras/models/customfields.py:440 msgid "True" msgstr "" -#: netbox/extras/models/customfields.py:440 +#: netbox/extras/models/customfields.py:441 msgid "False" msgstr "" -#: netbox/extras/models/customfields.py:522 +#: netbox/extras/models/customfields.py:523 #, python-brace-format msgid "Values must match this regex: {regex}" msgstr "" -#: netbox/extras/models/customfields.py:616 +#: netbox/extras/models/customfields.py:617 msgid "Value must be a string." msgstr "" -#: netbox/extras/models/customfields.py:618 +#: netbox/extras/models/customfields.py:619 #, python-brace-format msgid "Value must match regex '{regex}'" msgstr "" -#: netbox/extras/models/customfields.py:623 +#: netbox/extras/models/customfields.py:624 msgid "Value must be an integer." msgstr "" -#: netbox/extras/models/customfields.py:626 -#: netbox/extras/models/customfields.py:641 +#: netbox/extras/models/customfields.py:627 +#: netbox/extras/models/customfields.py:642 #, python-brace-format msgid "Value must be at least {minimum}" msgstr "" -#: netbox/extras/models/customfields.py:630 -#: netbox/extras/models/customfields.py:645 +#: netbox/extras/models/customfields.py:631 +#: netbox/extras/models/customfields.py:646 #, python-brace-format msgid "Value must not exceed {maximum}" msgstr "" -#: netbox/extras/models/customfields.py:638 +#: netbox/extras/models/customfields.py:639 msgid "Value must be a decimal." msgstr "" -#: netbox/extras/models/customfields.py:650 +#: netbox/extras/models/customfields.py:651 msgid "Value must be true or false." msgstr "" -#: netbox/extras/models/customfields.py:658 +#: netbox/extras/models/customfields.py:659 msgid "Date values must be in ISO 8601 format (YYYY-MM-DD)." msgstr "" -#: netbox/extras/models/customfields.py:671 +#: netbox/extras/models/customfields.py:672 msgid "Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS)." msgstr "" -#: netbox/extras/models/customfields.py:678 +#: netbox/extras/models/customfields.py:679 #, python-brace-format msgid "Invalid choice ({value}) for choice set {choiceset}." msgstr "" -#: netbox/extras/models/customfields.py:688 +#: netbox/extras/models/customfields.py:689 #, python-brace-format msgid "Invalid choice(s) ({value}) for choice set {choiceset}." msgstr "" -#: netbox/extras/models/customfields.py:697 +#: netbox/extras/models/customfields.py:698 #, python-brace-format msgid "Value must be an object ID, not {type}" msgstr "" -#: netbox/extras/models/customfields.py:703 +#: netbox/extras/models/customfields.py:704 #, python-brace-format msgid "Value must be a list of object IDs, not {type}" msgstr "" -#: netbox/extras/models/customfields.py:707 +#: netbox/extras/models/customfields.py:708 #, python-brace-format msgid "Found invalid object ID: {id}" msgstr "" -#: netbox/extras/models/customfields.py:710 +#: netbox/extras/models/customfields.py:711 msgid "Required field cannot be empty." msgstr "" -#: netbox/extras/models/customfields.py:729 +#: netbox/extras/models/customfields.py:730 msgid "Base set of predefined choices (optional)" msgstr "" -#: netbox/extras/models/customfields.py:741 +#: netbox/extras/models/customfields.py:742 msgid "Choices are automatically ordered alphabetically" msgstr "" -#: netbox/extras/models/customfields.py:748 +#: netbox/extras/models/customfields.py:749 msgid "custom field choice set" msgstr "" -#: netbox/extras/models/customfields.py:749 +#: netbox/extras/models/customfields.py:750 msgid "custom field choice sets" msgstr "" -#: netbox/extras/models/customfields.py:785 +#: netbox/extras/models/customfields.py:786 msgid "Must define base or extra choices." msgstr "" @@ -8147,19 +8148,19 @@ msgstr "" msgid "Script Execution Parameters" msgstr "" -#: netbox/extras/scripts.py:664 +#: netbox/extras/scripts.py:666 msgid "Database changes have been reverted automatically." msgstr "" -#: netbox/extras/scripts.py:677 +#: netbox/extras/scripts.py:679 msgid "Script aborted with error: " msgstr "" -#: netbox/extras/scripts.py:687 +#: netbox/extras/scripts.py:689 msgid "An exception occurred: " msgstr "" -#: netbox/extras/scripts.py:690 +#: netbox/extras/scripts.py:692 msgid "Database changes have been reverted due to error." msgstr "" @@ -14060,6 +14061,14 @@ msgstr "" msgid "Move Down" msgstr "" +#: netbox/utilities/templates/navigation/menu.html:14 +msgid "Search…" +msgstr "" + +#: netbox/utilities/templates/navigation/menu.html:14 +msgid "Search NetBox" +msgstr "" + #: netbox/utilities/templates/widgets/apiselect.html:7 msgid "Open selector" msgstr "" @@ -14081,17 +14090,17 @@ msgstr "" msgid "{value} is not a valid regular expression." msgstr "" -#: netbox/utilities/views.py:44 +#: netbox/utilities/views.py:45 #, python-brace-format msgid "{self.__class__.__name__} must implement get_required_permission()" msgstr "" -#: netbox/utilities/views.py:80 +#: netbox/utilities/views.py:81 #, python-brace-format msgid "{class_name} must implement get_required_permission()" msgstr "" -#: netbox/utilities/views.py:104 +#: netbox/utilities/views.py:105 #, python-brace-format msgid "" "{class_name} has no queryset defined. ObjectPermissionRequiredMixin may only " From 33004dfab0679547b2a33b84b39f6b96f08201ae Mon Sep 17 00:00:00 2001 From: Jeff Gehlbach Date: Tue, 25 Jun 2024 16:21:32 -0400 Subject: [PATCH 03/42] Added missing CDN cache clearing step to release checklist in docs --- docs/development/release-checklist.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/development/release-checklist.md b/docs/development/release-checklist.md index 91162f08ad7..019eb2a6c7a 100644 --- a/docs/development/release-checklist.md +++ b/docs/development/release-checklist.md @@ -135,4 +135,6 @@ First, run the `build-site` action, by navigating to Actions > build-site > Run Once the documentation files have been compiled, they must be published by running the `deploy-kinsta` action. Select the desired deployment environment (staging or production) and specify `latest` as the deploy tag. +Clear the CDN cache from the [Kinsta](https://my.kinsta.com/) portal. Navigate to _Sites_ / _NetBox Labs_ / _Live_, select _CDN_ in the left-nav, click the _Clear CDN cache_ button, and confirm the clear operation. + Finally, verify that the documentation at has been updated. From b605dfcba09f36ff1d69d4140dea4074cad68026 Mon Sep 17 00:00:00 2001 From: Julio Oliveira at Encora <149191228+Julio-Oliveira-Encora@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:14:08 -0300 Subject: [PATCH 04/42] 16704 - Define a default help_text for ColorField (#16708) * Added `help_text` to ColorField. * Addressed PR comment to remove the redundant help_text from all the forms where ColorField was used. * Add space before example value --------- Co-authored-by: Jeremy Stretch --- netbox/circuits/forms/bulk_import.py | 3 --- netbox/dcim/forms/bulk_import.py | 12 ------------ netbox/extras/forms/bulk_import.py | 3 --- netbox/utilities/fields.py | 2 ++ 4 files changed, 2 insertions(+), 18 deletions(-) diff --git a/netbox/circuits/forms/bulk_import.py b/netbox/circuits/forms/bulk_import.py index 1ceb44b6070..88fdd2c712d 100644 --- a/netbox/circuits/forms/bulk_import.py +++ b/netbox/circuits/forms/bulk_import.py @@ -66,9 +66,6 @@ class CircuitTypeImportForm(NetBoxModelImportForm): class Meta: model = CircuitType fields = ('name', 'slug', 'color', 'description', 'tags') - help_texts = { - 'color': mark_safe(_('RGB color in hexadecimal. Example:') + ' 00ff00'), - } class CircuitImportForm(NetBoxModelImportForm): diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 5a64cad02fb..1c537512c3a 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -174,9 +174,6 @@ class RackRoleImportForm(NetBoxModelImportForm): class Meta: model = RackRole fields = ('name', 'slug', 'color', 'description', 'tags') - help_texts = { - 'color': mark_safe(_('RGB color in hexadecimal. Example:') + ' 00ff00'), - } class RackImportForm(NetBoxModelImportForm): @@ -384,9 +381,6 @@ class DeviceRoleImportForm(NetBoxModelImportForm): class Meta: model = DeviceRole fields = ('name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags') - help_texts = { - 'color': mark_safe(_('RGB color in hexadecimal. Example:') + ' 00ff00'), - } class PlatformImportForm(NetBoxModelImportForm): @@ -1104,9 +1098,6 @@ class InventoryItemRoleImportForm(NetBoxModelImportForm): class Meta: model = InventoryItemRole fields = ('name', 'slug', 'color', 'description') - help_texts = { - 'color': mark_safe(_('RGB color in hexadecimal. Example:') + ' 00ff00'), - } # @@ -1183,9 +1174,6 @@ class Meta: 'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description', 'comments', 'tags', ] - help_texts = { - 'color': mark_safe(_('RGB color in hexadecimal. Example:') + ' 00ff00'), - } def _clean_side(self, side): """ diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index c09eed3da36..f2cf0b72101 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -228,9 +228,6 @@ class TagImportForm(CSVModelForm): class Meta: model = Tag fields = ('name', 'slug', 'color', 'description') - help_texts = { - 'color': mark_safe(_('RGB color in hexadecimal. Example:') + ' 00ff00'), - } class JournalEntryImportForm(NetBoxModelImportForm): diff --git a/netbox/utilities/fields.py b/netbox/utilities/fields.py index 2640f6886e5..ee71223cb1b 100644 --- a/netbox/utilities/fields.py +++ b/netbox/utilities/fields.py @@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey from django.db import models +from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from utilities.ordering import naturalize @@ -26,6 +27,7 @@ def __init__(self, *args, **kwargs): def formfield(self, **kwargs): kwargs['widget'] = ColorSelect + kwargs['help_text'] = mark_safe(_('RGB color in hexadecimal. Example: ') + '00ff00') return super().formfield(**kwargs) From b241c97e0059affc799d7f788c74b8326a8b66c3 Mon Sep 17 00:00:00 2001 From: Julio Oliveira at Encora <149191228+Julio-Oliveira-Encora@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:54:15 -0300 Subject: [PATCH 05/42] =?UTF-8?q?Was=20added=20to=20searching=20support=20?= =?UTF-8?q?languages=20other=20than=20English=20for=20objec=E2=80=A6=20(#1?= =?UTF-8?q?6706)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Was added to searching support languages other than English for object types(s). * Fix SearchForm field label translation --------- Co-authored-by: Jeremy Stretch --- netbox/netbox/forms/__init__.py | 5 +++-- netbox/netbox/search/backends.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/netbox/netbox/forms/__init__.py b/netbox/netbox/forms/__init__.py index fa82689a5de..f88fb18bc92 100644 --- a/netbox/netbox/forms/__init__.py +++ b/netbox/netbox/forms/__init__.py @@ -1,7 +1,7 @@ import re from django import forms -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from netbox.search import LookupTypes from netbox.search.backends import search_backend @@ -36,7 +36,8 @@ class SearchForm(forms.Form): lookup = forms.ChoiceField( choices=LOOKUP_CHOICES, initial=LookupTypes.PARTIAL, - required=False + required=False, + label=_('Lookup') ) def __init__(self, *args, **kwargs): diff --git a/netbox/netbox/search/backends.py b/netbox/netbox/search/backends.py index 227a79205e1..12243e9b64e 100644 --- a/netbox/netbox/search/backends.py +++ b/netbox/netbox/search/backends.py @@ -8,6 +8,7 @@ from django.db.models.functions import window from django.db.models.signals import post_delete, post_save from django.utils.module_loading import import_string +from django.utils.translation import gettext_lazy as _ import netaddr from netaddr.core import AddrFormatError @@ -39,7 +40,7 @@ def get_object_types(self): # Organize choices by category categories = defaultdict(dict) for label, idx in registry['search'].items(): - categories[idx.get_category()][label] = title(idx.model._meta.verbose_name) + categories[idx.get_category()][label] = _(title(idx.model._meta.verbose_name)) # Compile a nested tuple of choices for form rendering results = ( From c506f60f1208796deda36e6bea4c8f77a8d13a16 Mon Sep 17 00:00:00 2001 From: Julio Oliveira at Encora <149191228+Julio-Oliveira-Encora@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:13:32 -0300 Subject: [PATCH 06/42] 16424 - Allow filtering of Devices by Cluster and Cluster Group (#16674) * Allow filtering Devices by Cluster and Cluster Group. * Allow filtering Devices by Cluster and Cluster Group. * Added tests for cluster and cluster_groups filterset. * Add missing filter & complete tests --------- Co-authored-by: Jeremy Stretch --- netbox/dcim/filtersets.py | 13 ++++++++++++- netbox/dcim/forms/filtersets.py | 12 ++++++++++++ netbox/dcim/tests/test_filtersets.py | 21 +++++++++++++++++---- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 2fb1e9949c9..0848966e885 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -20,7 +20,7 @@ ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter, ) -from virtualization.models import Cluster +from virtualization.models import Cluster, ClusterGroup from vpn.models import L2VPN from wireless.choices import WirelessRoleChoices, WirelessChannelChoices from wireless.models import WirelessLAN, WirelessLink @@ -1018,6 +1018,17 @@ class DeviceFilterSet( queryset=Cluster.objects.all(), label=_('VM cluster (ID)'), ) + cluster_group = django_filters.ModelMultipleChoiceFilter( + field_name='cluster__group__slug', + queryset=ClusterGroup.objects.all(), + to_field_name='slug', + label=_('Cluster group (slug)'), + ) + cluster_group_id = django_filters.ModelMultipleChoiceFilter( + field_name='cluster__group', + queryset=ClusterGroup.objects.all(), + label=_('Cluster group (ID)'), + ) model = django_filters.ModelMultipleChoiceFilter( field_name='device_type__slug', queryset=DeviceType.objects.all(), diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 0a28a4ec445..22e66763b15 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -14,6 +14,7 @@ from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField from utilities.forms.rendering import FieldSet from utilities.forms.widgets import NumberWithOptions +from virtualization.models import Cluster, ClusterGroup from vpn.models import L2VPN from wireless.choices import * @@ -655,6 +656,7 @@ class DeviceFilterForm( 'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports', name=_('Components') ), + FieldSet('cluster_group_id', 'cluster_id', name=_('Cluster')), FieldSet( 'has_primary_ip', 'has_oob_ip', 'virtual_chassis_member', 'config_template_id', 'local_context_data', 'has_virtual_device_context', @@ -821,6 +823,16 @@ class DeviceFilterForm( choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + cluster_id = DynamicModelMultipleChoiceField( + queryset=Cluster.objects.all(), + required=False, + label=_('Cluster') + ) + cluster_group_id = DynamicModelMultipleChoiceField( + queryset=ClusterGroup.objects.all(), + required=False, + label=_('Cluster group') + ) tag = TagFilterField(model) diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 0a22f5a824b..d08e2707fb9 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -9,7 +9,7 @@ from netbox.choices import ColorChoices from tenancy.models import Tenant, TenantGroup from utilities.testing import ChangeLoggedFilterSetTests, create_test_device -from virtualization.models import Cluster, ClusterType +from virtualization.models import Cluster, ClusterType, ClusterGroup from wireless.choices import WirelessChannelChoices, WirelessRoleChoices User = get_user_model() @@ -1959,10 +1959,16 @@ def setUpTestData(cls): Rack.objects.bulk_create(racks) cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1') + cluster_groups = ( + ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'), + ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'), + ClusterGroup(name='Cluster Group 3', slug='cluster-group-3'), + ) + ClusterGroup.objects.bulk_create(cluster_groups) clusters = ( - Cluster(name='Cluster 1', type=cluster_type), - Cluster(name='Cluster 2', type=cluster_type), - Cluster(name='Cluster 3', type=cluster_type), + Cluster(name='Cluster 1', type=cluster_type, group=cluster_groups[0]), + Cluster(name='Cluster 2', type=cluster_type, group=cluster_groups[1]), + Cluster(name='Cluster 3', type=cluster_type, group=cluster_groups[2]), ) Cluster.objects.bulk_create(clusters) @@ -2213,6 +2219,13 @@ def test_cluster(self): params = {'cluster_id': [clusters[0].pk, clusters[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_cluster_group(self): + cluster_groups = ClusterGroup.objects.all()[:2] + params = {'cluster_group_id': [cluster_groups[0].pk, cluster_groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'cluster_group': [cluster_groups[0].slug, cluster_groups[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_model(self): params = {'model': ['model-1', 'model-2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) From c7dcded74fdf8d846ac12f40d03b036015db7ce6 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 05:02:05 +0000 Subject: [PATCH 07/42] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 901 ++++++++++--------- 1 file changed, 453 insertions(+), 448 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index 53282465404..ebbf64e448d 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-25 05:01+0000\n" +"POT-Creation-Date: 2024-06-27 05:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -107,8 +107,8 @@ msgstr "" #: netbox/dcim/filtersets.py:97 netbox/dcim/filtersets.py:151 #: netbox/dcim/filtersets.py:211 netbox/dcim/filtersets.py:297 #: netbox/dcim/filtersets.py:406 netbox/dcim/filtersets.py:969 -#: netbox/dcim/filtersets.py:1305 netbox/dcim/filtersets.py:1832 -#: netbox/dcim/filtersets.py:2075 netbox/dcim/filtersets.py:2133 +#: netbox/dcim/filtersets.py:1316 netbox/dcim/filtersets.py:1843 +#: netbox/dcim/filtersets.py:2086 netbox/dcim/filtersets.py:2144 #: netbox/ipam/filtersets.py:339 netbox/ipam/filtersets.py:945 #: netbox/virtualization/filtersets.py:45 #: netbox/virtualization/filtersets.py:173 netbox/vpn/filtersets.py:377 @@ -119,8 +119,8 @@ msgstr "" #: netbox/dcim/filtersets.py:104 netbox/dcim/filtersets.py:157 #: netbox/dcim/filtersets.py:218 netbox/dcim/filtersets.py:304 #: netbox/dcim/filtersets.py:413 netbox/dcim/filtersets.py:976 -#: netbox/dcim/filtersets.py:1312 netbox/dcim/filtersets.py:1839 -#: netbox/dcim/filtersets.py:2082 netbox/dcim/filtersets.py:2140 +#: netbox/dcim/filtersets.py:1323 netbox/dcim/filtersets.py:1850 +#: netbox/dcim/filtersets.py:2093 netbox/dcim/filtersets.py:2151 #: netbox/extras/filtersets.py:461 netbox/ipam/filtersets.py:346 #: netbox/ipam/filtersets.py:952 netbox/virtualization/filtersets.py:52 #: netbox/virtualization/filtersets.py:180 netbox/vpn/filtersets.py:372 @@ -130,9 +130,9 @@ msgstr "" #: netbox/circuits/filtersets.py:42 netbox/circuits/filtersets.py:209 #: netbox/dcim/filtersets.py:127 netbox/dcim/filtersets.py:224 #: netbox/dcim/filtersets.py:310 netbox/dcim/filtersets.py:419 -#: netbox/dcim/filtersets.py:982 netbox/dcim/filtersets.py:1318 -#: netbox/dcim/filtersets.py:1845 netbox/dcim/filtersets.py:2088 -#: netbox/dcim/filtersets.py:2146 netbox/ipam/filtersets.py:352 +#: netbox/dcim/filtersets.py:982 netbox/dcim/filtersets.py:1329 +#: netbox/dcim/filtersets.py:1856 netbox/dcim/filtersets.py:2099 +#: netbox/dcim/filtersets.py:2157 netbox/ipam/filtersets.py:352 #: netbox/ipam/filtersets.py:958 netbox/virtualization/filtersets.py:58 #: netbox/virtualization/filtersets.py:186 msgid "Site group (ID)" @@ -141,9 +141,9 @@ msgstr "" #: netbox/circuits/filtersets.py:49 netbox/circuits/filtersets.py:216 #: netbox/dcim/filtersets.py:134 netbox/dcim/filtersets.py:231 #: netbox/dcim/filtersets.py:317 netbox/dcim/filtersets.py:426 -#: netbox/dcim/filtersets.py:989 netbox/dcim/filtersets.py:1325 -#: netbox/dcim/filtersets.py:1852 netbox/dcim/filtersets.py:2095 -#: netbox/dcim/filtersets.py:2153 netbox/extras/filtersets.py:467 +#: netbox/dcim/filtersets.py:989 netbox/dcim/filtersets.py:1336 +#: netbox/dcim/filtersets.py:1863 netbox/dcim/filtersets.py:2106 +#: netbox/dcim/filtersets.py:2164 netbox/extras/filtersets.py:467 #: netbox/ipam/filtersets.py:359 netbox/ipam/filtersets.py:965 #: netbox/virtualization/filtersets.py:65 #: netbox/virtualization/filtersets.py:193 @@ -152,7 +152,7 @@ msgstr "" #: netbox/circuits/filtersets.py:54 netbox/circuits/forms/bulk_edit.py:186 #: netbox/circuits/forms/bulk_edit.py:214 -#: netbox/circuits/forms/bulk_import.py:126 +#: netbox/circuits/forms/bulk_import.py:123 #: netbox/circuits/forms/filtersets.py:49 #: netbox/circuits/forms/filtersets.py:169 #: netbox/circuits/forms/filtersets.py:207 @@ -161,15 +161,15 @@ msgstr "" #: netbox/circuits/tables/circuits.py:107 netbox/dcim/forms/bulk_edit.py:167 #: netbox/dcim/forms/bulk_edit.py:239 netbox/dcim/forms/bulk_edit.py:575 #: netbox/dcim/forms/bulk_edit.py:771 netbox/dcim/forms/bulk_import.py:130 -#: netbox/dcim/forms/bulk_import.py:184 netbox/dcim/forms/bulk_import.py:257 -#: netbox/dcim/forms/bulk_import.py:485 netbox/dcim/forms/bulk_import.py:1262 -#: netbox/dcim/forms/bulk_import.py:1290 netbox/dcim/forms/filtersets.py:85 -#: netbox/dcim/forms/filtersets.py:218 netbox/dcim/forms/filtersets.py:265 -#: netbox/dcim/forms/filtersets.py:374 netbox/dcim/forms/filtersets.py:682 -#: netbox/dcim/forms/filtersets.py:916 netbox/dcim/forms/filtersets.py:940 -#: netbox/dcim/forms/filtersets.py:1030 netbox/dcim/forms/filtersets.py:1068 -#: netbox/dcim/forms/filtersets.py:1476 netbox/dcim/forms/filtersets.py:1500 -#: netbox/dcim/forms/filtersets.py:1524 netbox/dcim/forms/model_forms.py:136 +#: netbox/dcim/forms/bulk_import.py:181 netbox/dcim/forms/bulk_import.py:254 +#: netbox/dcim/forms/bulk_import.py:479 netbox/dcim/forms/bulk_import.py:1250 +#: netbox/dcim/forms/bulk_import.py:1278 netbox/dcim/forms/filtersets.py:86 +#: netbox/dcim/forms/filtersets.py:219 netbox/dcim/forms/filtersets.py:266 +#: netbox/dcim/forms/filtersets.py:375 netbox/dcim/forms/filtersets.py:684 +#: netbox/dcim/forms/filtersets.py:928 netbox/dcim/forms/filtersets.py:952 +#: netbox/dcim/forms/filtersets.py:1042 netbox/dcim/forms/filtersets.py:1080 +#: netbox/dcim/forms/filtersets.py:1488 netbox/dcim/forms/filtersets.py:1512 +#: netbox/dcim/forms/filtersets.py:1536 netbox/dcim/forms/model_forms.py:136 #: netbox/dcim/forms/model_forms.py:164 netbox/dcim/forms/model_forms.py:206 #: netbox/dcim/forms/model_forms.py:406 netbox/dcim/forms/model_forms.py:671 #: netbox/dcim/forms/object_create.py:391 netbox/dcim/tables/devices.py:150 @@ -268,8 +268,8 @@ msgstr "" #: netbox/circuits/filtersets.py:221 netbox/circuits/filtersets.py:266 #: netbox/dcim/filtersets.py:235 netbox/dcim/filtersets.py:321 #: netbox/dcim/filtersets.py:394 netbox/dcim/filtersets.py:993 -#: netbox/dcim/filtersets.py:1330 netbox/dcim/filtersets.py:1857 -#: netbox/dcim/filtersets.py:2099 netbox/dcim/filtersets.py:2158 +#: netbox/dcim/filtersets.py:1341 netbox/dcim/filtersets.py:1868 +#: netbox/dcim/filtersets.py:2110 netbox/dcim/filtersets.py:2169 #: netbox/ipam/filtersets.py:232 netbox/ipam/filtersets.py:363 #: netbox/ipam/filtersets.py:969 netbox/virtualization/filtersets.py:69 #: netbox/virtualization/filtersets.py:197 netbox/vpn/filtersets.py:387 @@ -282,7 +282,7 @@ msgstr "" #: netbox/circuits/filtersets.py:258 netbox/core/filtersets.py:73 #: netbox/core/filtersets.py:132 netbox/dcim/filtersets.py:693 -#: netbox/dcim/filtersets.py:1299 netbox/dcim/filtersets.py:2206 +#: netbox/dcim/filtersets.py:1310 netbox/dcim/filtersets.py:2217 #: netbox/extras/filtersets.py:41 netbox/extras/filtersets.py:63 #: netbox/extras/filtersets.py:92 netbox/extras/filtersets.py:127 #: netbox/extras/filtersets.py:176 netbox/extras/filtersets.py:204 @@ -304,7 +304,7 @@ msgid "Search" msgstr "" #: netbox/circuits/filtersets.py:262 netbox/circuits/forms/bulk_edit.py:170 -#: netbox/circuits/forms/bulk_import.py:117 +#: netbox/circuits/forms/bulk_import.py:114 #: netbox/circuits/forms/filtersets.py:196 #: netbox/circuits/forms/filtersets.py:212 #: netbox/circuits/forms/model_forms.py:109 @@ -324,7 +324,7 @@ msgstr "" #: netbox/circuits/forms/bulk_edit.py:28 netbox/circuits/forms/filtersets.py:54 #: netbox/circuits/forms/model_forms.py:27 #: netbox/circuits/tables/providers.py:33 netbox/dcim/forms/bulk_edit.py:127 -#: netbox/dcim/forms/filtersets.py:188 netbox/dcim/forms/model_forms.py:122 +#: netbox/dcim/forms/filtersets.py:189 netbox/dcim/forms/model_forms.py:122 #: netbox/dcim/tables/sites.py:94 netbox/ipam/models/asns.py:126 #: netbox/ipam/tables/asn.py:27 netbox/ipam/views.py:210 #: netbox/netbox/navigation/menu.py:159 netbox/netbox/navigation/menu.py:162 @@ -460,7 +460,7 @@ msgstr "" #: netbox/circuits/forms/bulk_edit.py:121 #: netbox/circuits/forms/bulk_import.py:35 #: netbox/circuits/forms/bulk_import.py:50 -#: netbox/circuits/forms/bulk_import.py:76 +#: netbox/circuits/forms/bulk_import.py:73 #: netbox/circuits/forms/filtersets.py:68 #: netbox/circuits/forms/filtersets.py:86 #: netbox/circuits/forms/filtersets.py:114 @@ -491,8 +491,8 @@ msgstr "" #: netbox/circuits/forms/filtersets.py:105 netbox/dcim/forms/bulk_edit.py:205 #: netbox/dcim/forms/bulk_edit.py:502 netbox/dcim/forms/bulk_edit.py:702 #: netbox/dcim/forms/bulk_edit.py:1071 netbox/dcim/forms/bulk_edit.py:1098 -#: netbox/dcim/forms/bulk_edit.py:1571 netbox/dcim/forms/filtersets.py:983 -#: netbox/dcim/forms/filtersets.py:1359 netbox/dcim/forms/filtersets.py:1380 +#: netbox/dcim/forms/bulk_edit.py:1571 netbox/dcim/forms/filtersets.py:995 +#: netbox/dcim/forms/filtersets.py:1371 netbox/dcim/forms/filtersets.py:1392 #: netbox/dcim/tables/devices.py:687 netbox/dcim/tables/devices.py:744 #: netbox/dcim/tables/devices.py:968 netbox/dcim/tables/devicetypes.py:245 #: netbox/dcim/tables/devicetypes.py:260 netbox/dcim/tables/racks.py:32 @@ -507,7 +507,7 @@ msgid "Color" msgstr "" #: netbox/circuits/forms/bulk_edit.py:116 -#: netbox/circuits/forms/bulk_import.py:89 +#: netbox/circuits/forms/bulk_import.py:86 #: netbox/circuits/forms/filtersets.py:124 netbox/core/forms/bulk_edit.py:18 #: netbox/core/forms/filtersets.py:30 netbox/core/tables/data.py:20 #: netbox/core/tables/jobs.py:18 netbox/dcim/forms/bulk_edit.py:282 @@ -515,17 +515,17 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:887 netbox/dcim/forms/bulk_edit.py:906 #: netbox/dcim/forms/bulk_edit.py:929 netbox/dcim/forms/bulk_edit.py:971 #: netbox/dcim/forms/bulk_edit.py:1015 netbox/dcim/forms/bulk_edit.py:1066 -#: netbox/dcim/forms/bulk_edit.py:1093 netbox/dcim/forms/bulk_import.py:214 -#: netbox/dcim/forms/bulk_import.py:653 netbox/dcim/forms/bulk_import.py:679 -#: netbox/dcim/forms/bulk_import.py:705 netbox/dcim/forms/bulk_import.py:725 -#: netbox/dcim/forms/bulk_import.py:808 netbox/dcim/forms/bulk_import.py:902 -#: netbox/dcim/forms/bulk_import.py:944 netbox/dcim/forms/bulk_import.py:1161 -#: netbox/dcim/forms/bulk_import.py:1327 netbox/dcim/forms/filtersets.py:287 -#: netbox/dcim/forms/filtersets.py:874 netbox/dcim/forms/filtersets.py:973 -#: netbox/dcim/forms/filtersets.py:1094 netbox/dcim/forms/filtersets.py:1164 -#: netbox/dcim/forms/filtersets.py:1186 netbox/dcim/forms/filtersets.py:1208 -#: netbox/dcim/forms/filtersets.py:1225 netbox/dcim/forms/filtersets.py:1259 -#: netbox/dcim/forms/filtersets.py:1354 netbox/dcim/forms/filtersets.py:1375 +#: netbox/dcim/forms/bulk_edit.py:1093 netbox/dcim/forms/bulk_import.py:211 +#: netbox/dcim/forms/bulk_import.py:647 netbox/dcim/forms/bulk_import.py:673 +#: netbox/dcim/forms/bulk_import.py:699 netbox/dcim/forms/bulk_import.py:719 +#: netbox/dcim/forms/bulk_import.py:802 netbox/dcim/forms/bulk_import.py:896 +#: netbox/dcim/forms/bulk_import.py:938 netbox/dcim/forms/bulk_import.py:1152 +#: netbox/dcim/forms/bulk_import.py:1315 netbox/dcim/forms/filtersets.py:288 +#: netbox/dcim/forms/filtersets.py:886 netbox/dcim/forms/filtersets.py:985 +#: netbox/dcim/forms/filtersets.py:1106 netbox/dcim/forms/filtersets.py:1176 +#: netbox/dcim/forms/filtersets.py:1198 netbox/dcim/forms/filtersets.py:1220 +#: netbox/dcim/forms/filtersets.py:1237 netbox/dcim/forms/filtersets.py:1271 +#: netbox/dcim/forms/filtersets.py:1366 netbox/dcim/forms/filtersets.py:1387 #: netbox/dcim/forms/model_forms.py:646 netbox/dcim/forms/model_forms.py:652 #: netbox/dcim/forms/object_import.py:84 netbox/dcim/forms/object_import.py:113 #: netbox/dcim/forms/object_import.py:145 netbox/dcim/tables/devices.py:175 @@ -561,14 +561,14 @@ msgid "Type" msgstr "" #: netbox/circuits/forms/bulk_edit.py:126 -#: netbox/circuits/forms/bulk_import.py:82 +#: netbox/circuits/forms/bulk_import.py:79 #: netbox/circuits/forms/filtersets.py:137 #: netbox/circuits/forms/model_forms.py:96 msgid "Provider account" msgstr "" #: netbox/circuits/forms/bulk_edit.py:134 -#: netbox/circuits/forms/bulk_import.py:95 +#: netbox/circuits/forms/bulk_import.py:92 #: netbox/circuits/forms/filtersets.py:148 netbox/core/forms/filtersets.py:35 #: netbox/core/forms/filtersets.py:76 netbox/core/tables/data.py:23 #: netbox/core/tables/jobs.py:26 netbox/core/tables/tasks.py:88 @@ -577,13 +577,13 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:654 netbox/dcim/forms/bulk_edit.py:686 #: netbox/dcim/forms/bulk_edit.py:813 netbox/dcim/forms/bulk_edit.py:1594 #: netbox/dcim/forms/bulk_import.py:87 netbox/dcim/forms/bulk_import.py:146 -#: netbox/dcim/forms/bulk_import.py:202 netbox/dcim/forms/bulk_import.py:450 -#: netbox/dcim/forms/bulk_import.py:604 netbox/dcim/forms/bulk_import.py:1155 -#: netbox/dcim/forms/bulk_import.py:1322 netbox/dcim/forms/bulk_import.py:1386 -#: netbox/dcim/forms/filtersets.py:171 netbox/dcim/forms/filtersets.py:230 -#: netbox/dcim/forms/filtersets.py:282 netbox/dcim/forms/filtersets.py:728 -#: netbox/dcim/forms/filtersets.py:843 netbox/dcim/forms/filtersets.py:877 -#: netbox/dcim/forms/filtersets.py:978 netbox/dcim/forms/filtersets.py:1089 +#: netbox/dcim/forms/bulk_import.py:199 netbox/dcim/forms/bulk_import.py:444 +#: netbox/dcim/forms/bulk_import.py:598 netbox/dcim/forms/bulk_import.py:1146 +#: netbox/dcim/forms/bulk_import.py:1310 netbox/dcim/forms/bulk_import.py:1374 +#: netbox/dcim/forms/filtersets.py:172 netbox/dcim/forms/filtersets.py:231 +#: netbox/dcim/forms/filtersets.py:283 netbox/dcim/forms/filtersets.py:730 +#: netbox/dcim/forms/filtersets.py:855 netbox/dcim/forms/filtersets.py:889 +#: netbox/dcim/forms/filtersets.py:990 netbox/dcim/forms/filtersets.py:1101 #: netbox/dcim/tables/devices.py:137 netbox/dcim/tables/devices.py:800 #: netbox/dcim/tables/devices.py:1028 netbox/dcim/tables/modules.py:69 #: netbox/dcim/tables/power.py:74 netbox/dcim/tables/racks.py:66 @@ -636,20 +636,20 @@ msgid "Status" msgstr "" #: netbox/circuits/forms/bulk_edit.py:140 -#: netbox/circuits/forms/bulk_import.py:100 +#: netbox/circuits/forms/bulk_import.py:97 #: netbox/circuits/forms/filtersets.py:117 netbox/dcim/forms/bulk_edit.py:121 #: netbox/dcim/forms/bulk_edit.py:186 netbox/dcim/forms/bulk_edit.py:256 #: netbox/dcim/forms/bulk_edit.py:368 netbox/dcim/forms/bulk_edit.py:588 #: netbox/dcim/forms/bulk_edit.py:692 netbox/dcim/forms/bulk_edit.py:1599 #: netbox/dcim/forms/bulk_import.py:106 netbox/dcim/forms/bulk_import.py:151 -#: netbox/dcim/forms/bulk_import.py:195 netbox/dcim/forms/bulk_import.py:282 -#: netbox/dcim/forms/bulk_import.py:424 netbox/dcim/forms/bulk_import.py:1167 -#: netbox/dcim/forms/bulk_import.py:1379 netbox/dcim/forms/filtersets.py:166 -#: netbox/dcim/forms/filtersets.py:198 netbox/dcim/forms/filtersets.py:249 -#: netbox/dcim/forms/filtersets.py:334 netbox/dcim/forms/filtersets.py:355 -#: netbox/dcim/forms/filtersets.py:652 netbox/dcim/forms/filtersets.py:835 -#: netbox/dcim/forms/filtersets.py:897 netbox/dcim/forms/filtersets.py:927 -#: netbox/dcim/forms/filtersets.py:1049 netbox/dcim/tables/power.py:88 +#: netbox/dcim/forms/bulk_import.py:192 netbox/dcim/forms/bulk_import.py:279 +#: netbox/dcim/forms/bulk_import.py:418 netbox/dcim/forms/bulk_import.py:1158 +#: netbox/dcim/forms/bulk_import.py:1367 netbox/dcim/forms/filtersets.py:167 +#: netbox/dcim/forms/filtersets.py:199 netbox/dcim/forms/filtersets.py:250 +#: netbox/dcim/forms/filtersets.py:335 netbox/dcim/forms/filtersets.py:356 +#: netbox/dcim/forms/filtersets.py:653 netbox/dcim/forms/filtersets.py:847 +#: netbox/dcim/forms/filtersets.py:909 netbox/dcim/forms/filtersets.py:939 +#: netbox/dcim/forms/filtersets.py:1061 netbox/dcim/tables/power.py:88 #: netbox/extras/filtersets.py:564 netbox/extras/forms/filtersets.py:332 #: netbox/extras/forms/filtersets.py:405 netbox/ipam/forms/bulk_edit.py:41 #: netbox/ipam/forms/bulk_edit.py:66 netbox/ipam/forms/bulk_edit.py:110 @@ -787,28 +787,22 @@ msgstr "" #: netbox/circuits/forms/bulk_import.py:38 #: netbox/circuits/forms/bulk_import.py:53 -#: netbox/circuits/forms/bulk_import.py:79 +#: netbox/circuits/forms/bulk_import.py:76 msgid "Assigned provider" msgstr "" -#: netbox/circuits/forms/bulk_import.py:70 netbox/dcim/forms/bulk_import.py:178 -#: netbox/dcim/forms/bulk_import.py:388 netbox/dcim/forms/bulk_import.py:1108 -#: netbox/dcim/forms/bulk_import.py:1187 netbox/extras/forms/bulk_import.py:232 -msgid "RGB color in hexadecimal. Example:" -msgstr "" - -#: netbox/circuits/forms/bulk_import.py:85 +#: netbox/circuits/forms/bulk_import.py:82 msgid "Assigned provider account" msgstr "" -#: netbox/circuits/forms/bulk_import.py:92 +#: netbox/circuits/forms/bulk_import.py:89 msgid "Type of circuit" msgstr "" -#: netbox/circuits/forms/bulk_import.py:97 netbox/dcim/forms/bulk_import.py:89 -#: netbox/dcim/forms/bulk_import.py:148 netbox/dcim/forms/bulk_import.py:204 -#: netbox/dcim/forms/bulk_import.py:452 netbox/dcim/forms/bulk_import.py:606 -#: netbox/dcim/forms/bulk_import.py:1324 netbox/ipam/forms/bulk_import.py:193 +#: netbox/circuits/forms/bulk_import.py:94 netbox/dcim/forms/bulk_import.py:89 +#: netbox/dcim/forms/bulk_import.py:148 netbox/dcim/forms/bulk_import.py:201 +#: netbox/dcim/forms/bulk_import.py:446 netbox/dcim/forms/bulk_import.py:600 +#: netbox/dcim/forms/bulk_import.py:1312 netbox/ipam/forms/bulk_import.py:193 #: netbox/ipam/forms/bulk_import.py:258 netbox/ipam/forms/bulk_import.py:294 #: netbox/ipam/forms/bulk_import.py:460 #: netbox/virtualization/forms/bulk_import.py:56 @@ -817,11 +811,11 @@ msgstr "" msgid "Operational status" msgstr "" -#: netbox/circuits/forms/bulk_import.py:104 +#: netbox/circuits/forms/bulk_import.py:101 #: netbox/dcim/forms/bulk_import.py:110 netbox/dcim/forms/bulk_import.py:155 -#: netbox/dcim/forms/bulk_import.py:286 netbox/dcim/forms/bulk_import.py:428 -#: netbox/dcim/forms/bulk_import.py:1171 netbox/dcim/forms/bulk_import.py:1319 -#: netbox/dcim/forms/bulk_import.py:1383 netbox/ipam/forms/bulk_import.py:41 +#: netbox/dcim/forms/bulk_import.py:283 netbox/dcim/forms/bulk_import.py:422 +#: netbox/dcim/forms/bulk_import.py:1162 netbox/dcim/forms/bulk_import.py:1307 +#: netbox/dcim/forms/bulk_import.py:1371 netbox/ipam/forms/bulk_import.py:41 #: netbox/ipam/forms/bulk_import.py:70 netbox/ipam/forms/bulk_import.py:98 #: netbox/ipam/forms/bulk_import.py:118 netbox/ipam/forms/bulk_import.py:138 #: netbox/ipam/forms/bulk_import.py:167 netbox/ipam/forms/bulk_import.py:253 @@ -833,7 +827,7 @@ msgstr "" msgid "Assigned tenant" msgstr "" -#: netbox/circuits/forms/bulk_import.py:122 +#: netbox/circuits/forms/bulk_import.py:119 #: netbox/templates/circuits/inc/circuit_termination.html:6 #: netbox/templates/circuits/inc/circuit_termination_fields.html:15 #: netbox/templates/dcim/cable.html:68 netbox/templates/dcim/cable.html:72 @@ -841,7 +835,7 @@ msgstr "" msgid "Termination" msgstr "" -#: netbox/circuits/forms/bulk_import.py:132 +#: netbox/circuits/forms/bulk_import.py:129 #: netbox/circuits/forms/filtersets.py:145 #: netbox/circuits/forms/filtersets.py:225 #: netbox/circuits/forms/model_forms.py:142 @@ -853,20 +847,20 @@ msgstr "" #: netbox/circuits/forms/filtersets.py:198 netbox/dcim/forms/bulk_edit.py:248 #: netbox/dcim/forms/bulk_edit.py:346 netbox/dcim/forms/bulk_edit.py:580 #: netbox/dcim/forms/bulk_edit.py:627 netbox/dcim/forms/bulk_edit.py:780 -#: netbox/dcim/forms/bulk_import.py:189 netbox/dcim/forms/bulk_import.py:263 -#: netbox/dcim/forms/bulk_import.py:491 netbox/dcim/forms/bulk_import.py:1268 -#: netbox/dcim/forms/bulk_import.py:1302 netbox/dcim/forms/filtersets.py:93 -#: netbox/dcim/forms/filtersets.py:246 netbox/dcim/forms/filtersets.py:279 -#: netbox/dcim/forms/filtersets.py:331 netbox/dcim/forms/filtersets.py:382 -#: netbox/dcim/forms/filtersets.py:649 netbox/dcim/forms/filtersets.py:691 -#: netbox/dcim/forms/filtersets.py:896 netbox/dcim/forms/filtersets.py:925 -#: netbox/dcim/forms/filtersets.py:945 netbox/dcim/forms/filtersets.py:1009 -#: netbox/dcim/forms/filtersets.py:1039 netbox/dcim/forms/filtersets.py:1048 -#: netbox/dcim/forms/filtersets.py:1159 netbox/dcim/forms/filtersets.py:1181 -#: netbox/dcim/forms/filtersets.py:1203 netbox/dcim/forms/filtersets.py:1220 -#: netbox/dcim/forms/filtersets.py:1240 netbox/dcim/forms/filtersets.py:1348 -#: netbox/dcim/forms/filtersets.py:1370 netbox/dcim/forms/filtersets.py:1391 -#: netbox/dcim/forms/filtersets.py:1406 netbox/dcim/forms/filtersets.py:1420 +#: netbox/dcim/forms/bulk_import.py:186 netbox/dcim/forms/bulk_import.py:260 +#: netbox/dcim/forms/bulk_import.py:485 netbox/dcim/forms/bulk_import.py:1256 +#: netbox/dcim/forms/bulk_import.py:1290 netbox/dcim/forms/filtersets.py:94 +#: netbox/dcim/forms/filtersets.py:247 netbox/dcim/forms/filtersets.py:280 +#: netbox/dcim/forms/filtersets.py:332 netbox/dcim/forms/filtersets.py:383 +#: netbox/dcim/forms/filtersets.py:650 netbox/dcim/forms/filtersets.py:693 +#: netbox/dcim/forms/filtersets.py:908 netbox/dcim/forms/filtersets.py:937 +#: netbox/dcim/forms/filtersets.py:957 netbox/dcim/forms/filtersets.py:1021 +#: netbox/dcim/forms/filtersets.py:1051 netbox/dcim/forms/filtersets.py:1060 +#: netbox/dcim/forms/filtersets.py:1171 netbox/dcim/forms/filtersets.py:1193 +#: netbox/dcim/forms/filtersets.py:1215 netbox/dcim/forms/filtersets.py:1232 +#: netbox/dcim/forms/filtersets.py:1252 netbox/dcim/forms/filtersets.py:1360 +#: netbox/dcim/forms/filtersets.py:1382 netbox/dcim/forms/filtersets.py:1403 +#: netbox/dcim/forms/filtersets.py:1418 netbox/dcim/forms/filtersets.py:1432 #: netbox/dcim/forms/model_forms.py:179 netbox/dcim/forms/model_forms.py:211 #: netbox/dcim/forms/model_forms.py:411 netbox/dcim/forms/model_forms.py:676 #: netbox/dcim/tables/devices.py:154 netbox/dcim/tables/power.py:30 @@ -889,11 +883,11 @@ msgid "Location" msgstr "" #: netbox/circuits/forms/filtersets.py:30 -#: netbox/circuits/forms/filtersets.py:118 netbox/dcim/forms/filtersets.py:137 -#: netbox/dcim/forms/filtersets.py:151 netbox/dcim/forms/filtersets.py:167 -#: netbox/dcim/forms/filtersets.py:199 netbox/dcim/forms/filtersets.py:250 -#: netbox/dcim/forms/filtersets.py:335 netbox/dcim/forms/filtersets.py:406 -#: netbox/dcim/forms/filtersets.py:653 netbox/dcim/forms/filtersets.py:1010 +#: netbox/circuits/forms/filtersets.py:118 netbox/dcim/forms/filtersets.py:138 +#: netbox/dcim/forms/filtersets.py:152 netbox/dcim/forms/filtersets.py:168 +#: netbox/dcim/forms/filtersets.py:200 netbox/dcim/forms/filtersets.py:251 +#: netbox/dcim/forms/filtersets.py:336 netbox/dcim/forms/filtersets.py:407 +#: netbox/dcim/forms/filtersets.py:654 netbox/dcim/forms/filtersets.py:1022 #: netbox/netbox/navigation/menu.py:44 netbox/netbox/navigation/menu.py:46 #: netbox/tenancy/forms/filtersets.py:42 netbox/tenancy/tables/columns.py:70 #: netbox/tenancy/tables/contacts.py:25 netbox/tenancy/views.py:18 @@ -906,13 +900,13 @@ msgstr "" #: netbox/circuits/forms/filtersets.py:35 #: netbox/circuits/forms/filtersets.py:155 netbox/dcim/forms/bulk_edit.py:111 #: netbox/dcim/forms/bulk_edit.py:223 netbox/dcim/forms/bulk_edit.py:755 -#: netbox/dcim/forms/bulk_import.py:92 netbox/dcim/forms/filtersets.py:71 -#: netbox/dcim/forms/filtersets.py:178 netbox/dcim/forms/filtersets.py:204 -#: netbox/dcim/forms/filtersets.py:257 netbox/dcim/forms/filtersets.py:360 -#: netbox/dcim/forms/filtersets.py:668 netbox/dcim/forms/filtersets.py:902 -#: netbox/dcim/forms/filtersets.py:932 netbox/dcim/forms/filtersets.py:1016 -#: netbox/dcim/forms/filtersets.py:1055 netbox/dcim/forms/filtersets.py:1468 -#: netbox/dcim/forms/filtersets.py:1492 netbox/dcim/forms/filtersets.py:1516 +#: netbox/dcim/forms/bulk_import.py:92 netbox/dcim/forms/filtersets.py:72 +#: netbox/dcim/forms/filtersets.py:179 netbox/dcim/forms/filtersets.py:205 +#: netbox/dcim/forms/filtersets.py:258 netbox/dcim/forms/filtersets.py:361 +#: netbox/dcim/forms/filtersets.py:670 netbox/dcim/forms/filtersets.py:914 +#: netbox/dcim/forms/filtersets.py:944 netbox/dcim/forms/filtersets.py:1028 +#: netbox/dcim/forms/filtersets.py:1067 netbox/dcim/forms/filtersets.py:1480 +#: netbox/dcim/forms/filtersets.py:1504 netbox/dcim/forms/filtersets.py:1528 #: netbox/dcim/forms/model_forms.py:111 netbox/dcim/forms/object_create.py:375 #: netbox/dcim/tables/devices.py:140 netbox/dcim/tables/sites.py:85 #: netbox/extras/filtersets.py:455 netbox/ipam/forms/bulk_edit.py:206 @@ -933,11 +927,11 @@ msgstr "" #: netbox/circuits/forms/filtersets.py:40 #: netbox/circuits/forms/filtersets.py:160 netbox/dcim/forms/bulk_edit.py:231 -#: netbox/dcim/forms/bulk_edit.py:763 netbox/dcim/forms/filtersets.py:76 -#: netbox/dcim/forms/filtersets.py:183 netbox/dcim/forms/filtersets.py:209 -#: netbox/dcim/forms/filtersets.py:270 netbox/dcim/forms/filtersets.py:365 -#: netbox/dcim/forms/filtersets.py:673 netbox/dcim/forms/filtersets.py:907 -#: netbox/dcim/forms/filtersets.py:1021 netbox/dcim/forms/filtersets.py:1060 +#: netbox/dcim/forms/bulk_edit.py:763 netbox/dcim/forms/filtersets.py:77 +#: netbox/dcim/forms/filtersets.py:184 netbox/dcim/forms/filtersets.py:210 +#: netbox/dcim/forms/filtersets.py:271 netbox/dcim/forms/filtersets.py:366 +#: netbox/dcim/forms/filtersets.py:675 netbox/dcim/forms/filtersets.py:919 +#: netbox/dcim/forms/filtersets.py:1033 netbox/dcim/forms/filtersets.py:1072 #: netbox/dcim/forms/object_create.py:383 netbox/extras/filtersets.py:472 #: netbox/ipam/forms/bulk_edit.py:211 netbox/ipam/forms/bulk_edit.py:445 #: netbox/ipam/forms/bulk_edit.py:517 netbox/ipam/forms/filtersets.py:222 @@ -954,14 +948,14 @@ msgstr "" #: netbox/circuits/forms/filtersets.py:81 #: netbox/circuits/forms/filtersets.py:100 #: netbox/circuits/forms/filtersets.py:115 netbox/core/forms/filtersets.py:64 -#: netbox/dcim/forms/bulk_edit.py:726 netbox/dcim/forms/filtersets.py:165 -#: netbox/dcim/forms/filtersets.py:197 netbox/dcim/forms/filtersets.py:834 -#: netbox/dcim/forms/filtersets.py:926 netbox/dcim/forms/filtersets.py:1050 -#: netbox/dcim/forms/filtersets.py:1158 netbox/dcim/forms/filtersets.py:1180 -#: netbox/dcim/forms/filtersets.py:1202 netbox/dcim/forms/filtersets.py:1219 -#: netbox/dcim/forms/filtersets.py:1236 netbox/dcim/forms/filtersets.py:1347 -#: netbox/dcim/forms/filtersets.py:1369 netbox/dcim/forms/filtersets.py:1390 -#: netbox/dcim/forms/filtersets.py:1405 netbox/dcim/forms/filtersets.py:1418 +#: netbox/dcim/forms/bulk_edit.py:726 netbox/dcim/forms/filtersets.py:166 +#: netbox/dcim/forms/filtersets.py:198 netbox/dcim/forms/filtersets.py:846 +#: netbox/dcim/forms/filtersets.py:938 netbox/dcim/forms/filtersets.py:1062 +#: netbox/dcim/forms/filtersets.py:1170 netbox/dcim/forms/filtersets.py:1192 +#: netbox/dcim/forms/filtersets.py:1214 netbox/dcim/forms/filtersets.py:1231 +#: netbox/dcim/forms/filtersets.py:1248 netbox/dcim/forms/filtersets.py:1359 +#: netbox/dcim/forms/filtersets.py:1381 netbox/dcim/forms/filtersets.py:1402 +#: netbox/dcim/forms/filtersets.py:1417 netbox/dcim/forms/filtersets.py:1430 #: netbox/extras/forms/filtersets.py:43 netbox/extras/forms/filtersets.py:112 #: netbox/extras/forms/filtersets.py:143 netbox/extras/forms/filtersets.py:183 #: netbox/extras/forms/filtersets.py:199 netbox/extras/forms/filtersets.py:230 @@ -1207,7 +1201,7 @@ msgstr "" #: netbox/circuits/tables/providers.py:99 netbox/core/tables/data.py:16 #: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:13 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 -#: netbox/dcim/forms/filtersets.py:61 netbox/dcim/forms/object_create.py:43 +#: netbox/dcim/forms/filtersets.py:62 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:52 netbox/dcim/tables/devices.py:89 #: netbox/dcim/tables/devices.py:131 netbox/dcim/tables/devices.py:286 #: netbox/dcim/tables/devices.py:380 netbox/dcim/tables/devices.py:421 @@ -1515,7 +1509,7 @@ msgstr "" #: netbox/core/forms/bulk_edit.py:25 netbox/core/forms/filtersets.py:40 #: netbox/core/tables/data.py:26 netbox/dcim/forms/bulk_edit.py:1020 -#: netbox/dcim/forms/bulk_edit.py:1293 netbox/dcim/forms/filtersets.py:1276 +#: netbox/dcim/forms/bulk_edit.py:1293 netbox/dcim/forms/filtersets.py:1288 #: netbox/dcim/tables/devices.py:541 netbox/dcim/tables/devicetypes.py:221 #: netbox/extras/forms/bulk_edit.py:98 netbox/extras/forms/bulk_edit.py:162 #: netbox/extras/forms/bulk_edit.py:221 netbox/extras/forms/filtersets.py:120 @@ -1618,7 +1612,7 @@ msgid "Completed before" msgstr "" #: netbox/core/forms/filtersets.py:123 netbox/dcim/forms/bulk_edit.py:361 -#: netbox/dcim/forms/filtersets.py:353 netbox/dcim/forms/filtersets.py:397 +#: netbox/dcim/forms/filtersets.py:354 netbox/dcim/forms/filtersets.py:398 #: netbox/dcim/forms/model_forms.py:258 netbox/extras/forms/filtersets.py:465 #: netbox/extras/forms/filtersets.py:505 #: netbox/templates/dcim/rackreservation.html:58 @@ -1700,7 +1694,7 @@ msgstr "" msgid "User Preferences" msgstr "" -#: netbox/core/forms/model_forms.py:167 netbox/dcim/forms/filtersets.py:661 +#: netbox/core/forms/model_forms.py:167 netbox/dcim/forms/filtersets.py:663 #: netbox/templates/core/inc/config_data.html:127 #: netbox/users/forms/model_forms.py:65 msgid "Miscellaneous" @@ -2160,8 +2154,8 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:86 netbox/dcim/forms/bulk_edit.py:172 #: netbox/dcim/forms/bulk_edit.py:1298 netbox/dcim/forms/bulk_import.py:59 #: netbox/dcim/forms/bulk_import.py:73 netbox/dcim/forms/bulk_import.py:136 -#: netbox/dcim/forms/bulk_import.py:511 netbox/dcim/forms/bulk_import.py:778 -#: netbox/dcim/forms/bulk_import.py:1033 netbox/dcim/forms/filtersets.py:227 +#: netbox/dcim/forms/bulk_import.py:505 netbox/dcim/forms/bulk_import.py:772 +#: netbox/dcim/forms/bulk_import.py:1027 netbox/dcim/forms/filtersets.py:228 #: netbox/dcim/forms/model_forms.py:73 netbox/dcim/forms/model_forms.py:92 #: netbox/dcim/forms/model_forms.py:169 netbox/dcim/forms/model_forms.py:1010 #: netbox/dcim/forms/model_forms.py:1449 netbox/dcim/forms/object_import.py:176 @@ -2286,7 +2280,7 @@ msgid "Virtual" msgstr "" #: netbox/dcim/choices.py:814 netbox/dcim/choices.py:1051 -#: netbox/dcim/forms/bulk_edit.py:1408 netbox/dcim/forms/filtersets.py:1239 +#: netbox/dcim/forms/bulk_edit.py:1408 netbox/dcim/forms/filtersets.py:1251 #: netbox/dcim/forms/model_forms.py:936 netbox/dcim/forms/model_forms.py:1344 #: netbox/netbox/navigation/menu.py:127 netbox/netbox/navigation/menu.py:131 #: netbox/templates/dcim/interface.html:210 @@ -2298,7 +2292,7 @@ msgid "Virtual interfaces" msgstr "" #: netbox/dcim/choices.py:979 netbox/dcim/forms/bulk_edit.py:1303 -#: netbox/dcim/forms/bulk_import.py:785 netbox/dcim/forms/model_forms.py:922 +#: netbox/dcim/forms/bulk_import.py:779 netbox/dcim/forms/model_forms.py:922 #: netbox/dcim/tables/devices.py:644 netbox/templates/dcim/interface.html:106 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:212 @@ -2327,9 +2321,9 @@ msgstr "" msgid "Cellular" msgstr "" -#: netbox/dcim/choices.py:1117 netbox/dcim/forms/filtersets.py:303 -#: netbox/dcim/forms/filtersets.py:738 netbox/dcim/forms/filtersets.py:882 -#: netbox/dcim/forms/filtersets.py:1434 +#: netbox/dcim/choices.py:1117 netbox/dcim/forms/filtersets.py:304 +#: netbox/dcim/forms/filtersets.py:740 netbox/dcim/forms/filtersets.py:894 +#: netbox/dcim/forms/filtersets.py:1446 #: netbox/templates/dcim/inventoryitem.html:52 #: netbox/templates/dcim/virtualchassis_edit.html:54 msgid "Serial" @@ -2402,7 +2396,7 @@ msgstr "" msgid "Fiber" msgstr "" -#: netbox/dcim/choices.py:1458 netbox/dcim/forms/filtersets.py:1146 +#: netbox/dcim/choices.py:1458 netbox/dcim/forms/filtersets.py:1158 msgid "Connected" msgstr "" @@ -2508,25 +2502,25 @@ msgstr "" #: netbox/dcim/filtersets.py:257 netbox/dcim/filtersets.py:333 #: netbox/dcim/filtersets.py:432 netbox/dcim/filtersets.py:1005 -#: netbox/dcim/filtersets.py:1341 netbox/dcim/filtersets.py:2111 +#: netbox/dcim/filtersets.py:1352 netbox/dcim/filtersets.py:2122 msgid "Location (ID)" msgstr "" #: netbox/dcim/filtersets.py:264 netbox/dcim/filtersets.py:340 -#: netbox/dcim/filtersets.py:439 netbox/dcim/filtersets.py:1347 +#: netbox/dcim/filtersets.py:439 netbox/dcim/filtersets.py:1358 #: netbox/extras/filtersets.py:494 msgid "Location (slug)" msgstr "" #: netbox/dcim/filtersets.py:354 netbox/dcim/filtersets.py:840 -#: netbox/dcim/filtersets.py:942 netbox/dcim/filtersets.py:1779 +#: netbox/dcim/filtersets.py:942 netbox/dcim/filtersets.py:1790 #: netbox/ipam/filtersets.py:381 netbox/ipam/filtersets.py:493 #: netbox/ipam/filtersets.py:989 netbox/virtualization/filtersets.py:210 msgid "Role (ID)" msgstr "" #: netbox/dcim/filtersets.py:360 netbox/dcim/filtersets.py:846 -#: netbox/dcim/filtersets.py:948 netbox/dcim/filtersets.py:1785 +#: netbox/dcim/filtersets.py:948 netbox/dcim/filtersets.py:1796 #: netbox/extras/filtersets.py:510 netbox/ipam/filtersets.py:387 #: netbox/ipam/filtersets.py:499 netbox/ipam/filtersets.py:995 #: netbox/virtualization/filtersets.py:216 @@ -2534,7 +2528,7 @@ msgid "Role (slug)" msgstr "" #: netbox/dcim/filtersets.py:389 netbox/dcim/filtersets.py:1010 -#: netbox/dcim/filtersets.py:1352 netbox/dcim/filtersets.py:2173 +#: netbox/dcim/filtersets.py:1363 netbox/dcim/filtersets.py:2184 msgid "Rack (ID)" msgstr "" @@ -2552,15 +2546,15 @@ msgstr "" #: netbox/dcim/filtersets.py:481 netbox/dcim/filtersets.py:620 #: netbox/dcim/filtersets.py:830 netbox/dcim/filtersets.py:881 -#: netbox/dcim/filtersets.py:921 netbox/dcim/filtersets.py:1243 -#: netbox/dcim/filtersets.py:1769 +#: netbox/dcim/filtersets.py:921 netbox/dcim/filtersets.py:1254 +#: netbox/dcim/filtersets.py:1780 msgid "Manufacturer (ID)" msgstr "" #: netbox/dcim/filtersets.py:487 netbox/dcim/filtersets.py:626 #: netbox/dcim/filtersets.py:836 netbox/dcim/filtersets.py:887 -#: netbox/dcim/filtersets.py:927 netbox/dcim/filtersets.py:1249 -#: netbox/dcim/filtersets.py:1775 +#: netbox/dcim/filtersets.py:927 netbox/dcim/filtersets.py:1260 +#: netbox/dcim/filtersets.py:1786 msgid "Manufacturer (slug)" msgstr "" @@ -2572,83 +2566,83 @@ msgstr "" msgid "Default platform (slug)" msgstr "" -#: netbox/dcim/filtersets.py:500 netbox/dcim/forms/filtersets.py:452 +#: netbox/dcim/filtersets.py:500 netbox/dcim/forms/filtersets.py:453 msgid "Has a front image" msgstr "" -#: netbox/dcim/filtersets.py:504 netbox/dcim/forms/filtersets.py:459 +#: netbox/dcim/filtersets.py:504 netbox/dcim/forms/filtersets.py:460 msgid "Has a rear image" msgstr "" #: netbox/dcim/filtersets.py:509 netbox/dcim/filtersets.py:630 -#: netbox/dcim/filtersets.py:1068 netbox/dcim/forms/filtersets.py:466 -#: netbox/dcim/forms/filtersets.py:562 netbox/dcim/forms/filtersets.py:777 +#: netbox/dcim/filtersets.py:1079 netbox/dcim/forms/filtersets.py:467 +#: netbox/dcim/forms/filtersets.py:563 netbox/dcim/forms/filtersets.py:779 msgid "Has console ports" msgstr "" #: netbox/dcim/filtersets.py:513 netbox/dcim/filtersets.py:634 -#: netbox/dcim/filtersets.py:1072 netbox/dcim/forms/filtersets.py:473 -#: netbox/dcim/forms/filtersets.py:569 netbox/dcim/forms/filtersets.py:784 +#: netbox/dcim/filtersets.py:1083 netbox/dcim/forms/filtersets.py:474 +#: netbox/dcim/forms/filtersets.py:570 netbox/dcim/forms/filtersets.py:786 msgid "Has console server ports" msgstr "" #: netbox/dcim/filtersets.py:517 netbox/dcim/filtersets.py:638 -#: netbox/dcim/filtersets.py:1076 netbox/dcim/forms/filtersets.py:480 -#: netbox/dcim/forms/filtersets.py:576 netbox/dcim/forms/filtersets.py:791 +#: netbox/dcim/filtersets.py:1087 netbox/dcim/forms/filtersets.py:481 +#: netbox/dcim/forms/filtersets.py:577 netbox/dcim/forms/filtersets.py:793 msgid "Has power ports" msgstr "" #: netbox/dcim/filtersets.py:521 netbox/dcim/filtersets.py:642 -#: netbox/dcim/filtersets.py:1080 netbox/dcim/forms/filtersets.py:487 -#: netbox/dcim/forms/filtersets.py:583 netbox/dcim/forms/filtersets.py:798 +#: netbox/dcim/filtersets.py:1091 netbox/dcim/forms/filtersets.py:488 +#: netbox/dcim/forms/filtersets.py:584 netbox/dcim/forms/filtersets.py:800 msgid "Has power outlets" msgstr "" #: netbox/dcim/filtersets.py:525 netbox/dcim/filtersets.py:646 -#: netbox/dcim/filtersets.py:1084 netbox/dcim/forms/filtersets.py:494 -#: netbox/dcim/forms/filtersets.py:590 netbox/dcim/forms/filtersets.py:805 +#: netbox/dcim/filtersets.py:1095 netbox/dcim/forms/filtersets.py:495 +#: netbox/dcim/forms/filtersets.py:591 netbox/dcim/forms/filtersets.py:807 msgid "Has interfaces" msgstr "" #: netbox/dcim/filtersets.py:529 netbox/dcim/filtersets.py:650 -#: netbox/dcim/filtersets.py:1088 netbox/dcim/forms/filtersets.py:501 -#: netbox/dcim/forms/filtersets.py:597 netbox/dcim/forms/filtersets.py:812 +#: netbox/dcim/filtersets.py:1099 netbox/dcim/forms/filtersets.py:502 +#: netbox/dcim/forms/filtersets.py:598 netbox/dcim/forms/filtersets.py:814 msgid "Has pass-through ports" msgstr "" -#: netbox/dcim/filtersets.py:533 netbox/dcim/filtersets.py:1092 -#: netbox/dcim/forms/filtersets.py:515 +#: netbox/dcim/filtersets.py:533 netbox/dcim/filtersets.py:1103 +#: netbox/dcim/forms/filtersets.py:516 msgid "Has module bays" msgstr "" -#: netbox/dcim/filtersets.py:537 netbox/dcim/filtersets.py:1096 -#: netbox/dcim/forms/filtersets.py:508 +#: netbox/dcim/filtersets.py:537 netbox/dcim/filtersets.py:1107 +#: netbox/dcim/forms/filtersets.py:509 msgid "Has device bays" msgstr "" -#: netbox/dcim/filtersets.py:541 netbox/dcim/forms/filtersets.py:522 +#: netbox/dcim/filtersets.py:541 netbox/dcim/forms/filtersets.py:523 msgid "Has inventory items" msgstr "" #: netbox/dcim/filtersets.py:698 netbox/dcim/filtersets.py:937 -#: netbox/dcim/filtersets.py:1373 +#: netbox/dcim/filtersets.py:1384 msgid "Device type (ID)" msgstr "" -#: netbox/dcim/filtersets.py:717 netbox/dcim/filtersets.py:1254 +#: netbox/dcim/filtersets.py:717 netbox/dcim/filtersets.py:1265 msgid "Module type (ID)" msgstr "" -#: netbox/dcim/filtersets.py:752 netbox/dcim/filtersets.py:1524 +#: netbox/dcim/filtersets.py:752 netbox/dcim/filtersets.py:1535 msgid "Power port (ID)" msgstr "" -#: netbox/dcim/filtersets.py:826 netbox/dcim/filtersets.py:1765 +#: netbox/dcim/filtersets.py:826 netbox/dcim/filtersets.py:1776 msgid "Parent inventory item (ID)" msgstr "" #: netbox/dcim/filtersets.py:869 netbox/dcim/filtersets.py:895 -#: netbox/dcim/filtersets.py:1064 netbox/virtualization/filtersets.py:238 +#: netbox/dcim/filtersets.py:1075 netbox/virtualization/filtersets.py:238 msgid "Config template (ID)" msgstr "" @@ -2669,9 +2663,9 @@ msgstr "" msgid "Platform (slug)" msgstr "" -#: netbox/dcim/filtersets.py:999 netbox/dcim/filtersets.py:1336 -#: netbox/dcim/filtersets.py:1863 netbox/dcim/filtersets.py:2105 -#: netbox/dcim/filtersets.py:2164 +#: netbox/dcim/filtersets.py:999 netbox/dcim/filtersets.py:1347 +#: netbox/dcim/filtersets.py:1874 netbox/dcim/filtersets.py:2116 +#: netbox/dcim/filtersets.py:2175 msgid "Site name (slug)" msgstr "" @@ -2683,16 +2677,25 @@ msgstr "" msgid "VM cluster (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1025 +#: netbox/dcim/filtersets.py:1025 netbox/extras/filtersets.py:543 +#: netbox/virtualization/filtersets.py:136 +msgid "Cluster group (slug)" +msgstr "" + +#: netbox/dcim/filtersets.py:1030 netbox/virtualization/filtersets.py:130 +msgid "Cluster group (ID)" +msgstr "" + +#: netbox/dcim/filtersets.py:1036 msgid "Device model (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1036 netbox/dcim/forms/bulk_edit.py:423 +#: netbox/dcim/filtersets.py:1047 netbox/dcim/forms/bulk_edit.py:423 msgid "Is full depth" msgstr "" -#: netbox/dcim/filtersets.py:1040 netbox/dcim/forms/common.py:18 -#: netbox/dcim/forms/filtersets.py:747 netbox/dcim/forms/filtersets.py:1291 +#: netbox/dcim/filtersets.py:1051 netbox/dcim/forms/common.py:18 +#: netbox/dcim/forms/filtersets.py:749 netbox/dcim/forms/filtersets.py:1303 #: netbox/dcim/models/device_components.py:519 #: netbox/virtualization/filtersets.py:230 #: netbox/virtualization/filtersets.py:297 @@ -2701,88 +2704,88 @@ msgstr "" msgid "MAC address" msgstr "" -#: netbox/dcim/filtersets.py:1047 netbox/dcim/filtersets.py:1211 -#: netbox/dcim/forms/filtersets.py:756 netbox/dcim/forms/filtersets.py:849 +#: netbox/dcim/filtersets.py:1058 netbox/dcim/filtersets.py:1222 +#: netbox/dcim/forms/filtersets.py:758 netbox/dcim/forms/filtersets.py:861 #: netbox/virtualization/filtersets.py:234 #: netbox/virtualization/forms/filtersets.py:176 msgid "Has a primary IP" msgstr "" -#: netbox/dcim/filtersets.py:1051 +#: netbox/dcim/filtersets.py:1062 msgid "Has an out-of-band IP" msgstr "" -#: netbox/dcim/filtersets.py:1056 +#: netbox/dcim/filtersets.py:1067 msgid "Virtual chassis (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1060 +#: netbox/dcim/filtersets.py:1071 msgid "Is a virtual chassis member" msgstr "" -#: netbox/dcim/filtersets.py:1101 +#: netbox/dcim/filtersets.py:1112 msgid "OOB IP (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1105 +#: netbox/dcim/filtersets.py:1116 msgid "Has virtual device context" msgstr "" -#: netbox/dcim/filtersets.py:1194 +#: netbox/dcim/filtersets.py:1205 msgid "VDC (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1199 +#: netbox/dcim/filtersets.py:1210 msgid "Device model" msgstr "" -#: netbox/dcim/filtersets.py:1204 netbox/ipam/filtersets.py:632 +#: netbox/dcim/filtersets.py:1215 netbox/ipam/filtersets.py:632 #: netbox/vpn/filtersets.py:102 netbox/vpn/filtersets.py:420 msgid "Interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1260 +#: netbox/dcim/filtersets.py:1271 msgid "Module type (model)" msgstr "" -#: netbox/dcim/filtersets.py:1266 +#: netbox/dcim/filtersets.py:1277 msgid "Module Bay (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1270 netbox/dcim/filtersets.py:1362 +#: netbox/dcim/filtersets.py:1281 netbox/dcim/filtersets.py:1373 #: netbox/ipam/filtersets.py:611 netbox/ipam/filtersets.py:851 #: netbox/ipam/filtersets.py:1075 netbox/virtualization/filtersets.py:161 #: netbox/vpn/filtersets.py:398 msgid "Device (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1358 +#: netbox/dcim/filtersets.py:1369 msgid "Rack (name)" msgstr "" -#: netbox/dcim/filtersets.py:1368 netbox/ipam/filtersets.py:606 +#: netbox/dcim/filtersets.py:1379 netbox/ipam/filtersets.py:606 #: netbox/ipam/filtersets.py:846 netbox/ipam/filtersets.py:1081 #: netbox/vpn/filtersets.py:393 msgid "Device (name)" msgstr "" -#: netbox/dcim/filtersets.py:1379 +#: netbox/dcim/filtersets.py:1390 msgid "Device type (model)" msgstr "" -#: netbox/dcim/filtersets.py:1384 +#: netbox/dcim/filtersets.py:1395 msgid "Device role (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1390 +#: netbox/dcim/filtersets.py:1401 msgid "Device role (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1395 +#: netbox/dcim/filtersets.py:1406 msgid "Virtual Chassis (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1401 netbox/dcim/forms/filtersets.py:107 +#: netbox/dcim/filtersets.py:1412 netbox/dcim/forms/filtersets.py:108 #: netbox/dcim/tables/devices.py:203 netbox/netbox/navigation/menu.py:66 #: netbox/templates/dcim/device.html:120 #: netbox/templates/dcim/device_edit.html:93 @@ -2792,25 +2795,25 @@ msgstr "" msgid "Virtual Chassis" msgstr "" -#: netbox/dcim/filtersets.py:1421 +#: netbox/dcim/filtersets.py:1432 msgid "Module (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1428 +#: netbox/dcim/filtersets.py:1439 msgid "Cable (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1537 netbox/ipam/forms/bulk_import.py:188 +#: netbox/dcim/filtersets.py:1548 netbox/ipam/forms/bulk_import.py:188 #: netbox/vpn/forms/bulk_import.py:308 msgid "Assigned VLAN" msgstr "" -#: netbox/dcim/filtersets.py:1541 +#: netbox/dcim/filtersets.py:1552 msgid "Assigned VID" msgstr "" -#: netbox/dcim/filtersets.py:1546 netbox/dcim/forms/bulk_edit.py:1382 -#: netbox/dcim/forms/bulk_import.py:836 netbox/dcim/forms/filtersets.py:1334 +#: netbox/dcim/filtersets.py:1557 netbox/dcim/forms/bulk_edit.py:1382 +#: netbox/dcim/forms/bulk_import.py:830 netbox/dcim/forms/filtersets.py:1346 #: netbox/dcim/forms/model_forms.py:1325 #: netbox/dcim/models/device_components.py:712 #: netbox/dcim/tables/devices.py:610 netbox/ipam/filtersets.py:316 @@ -2842,18 +2845,18 @@ msgstr "" msgid "VRF" msgstr "" -#: netbox/dcim/filtersets.py:1552 netbox/ipam/filtersets.py:322 +#: netbox/dcim/filtersets.py:1563 netbox/ipam/filtersets.py:322 #: netbox/ipam/filtersets.py:333 netbox/ipam/filtersets.py:489 #: netbox/ipam/filtersets.py:590 netbox/ipam/filtersets.py:601 msgid "VRF (RD)" msgstr "" -#: netbox/dcim/filtersets.py:1557 netbox/ipam/filtersets.py:1016 +#: netbox/dcim/filtersets.py:1568 netbox/ipam/filtersets.py:1016 #: netbox/vpn/filtersets.py:361 msgid "L2VPN (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1563 netbox/dcim/forms/filtersets.py:1339 +#: netbox/dcim/filtersets.py:1574 netbox/dcim/forms/filtersets.py:1351 #: netbox/dcim/tables/devices.py:558 netbox/ipam/filtersets.py:1022 #: netbox/ipam/forms/filtersets.py:525 netbox/ipam/tables/vlans.py:133 #: netbox/templates/dcim/interface.html:93 netbox/templates/ipam/vlan.html:66 @@ -2865,83 +2868,83 @@ msgstr "" msgid "L2VPN" msgstr "" -#: netbox/dcim/filtersets.py:1595 +#: netbox/dcim/filtersets.py:1606 msgid "Virtual Chassis Interfaces for Device" msgstr "" -#: netbox/dcim/filtersets.py:1600 +#: netbox/dcim/filtersets.py:1611 msgid "Virtual Chassis Interfaces for Device (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1604 +#: netbox/dcim/filtersets.py:1615 msgid "Kind of interface" msgstr "" -#: netbox/dcim/filtersets.py:1609 netbox/virtualization/filtersets.py:289 +#: netbox/dcim/filtersets.py:1620 netbox/virtualization/filtersets.py:289 msgid "Parent interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1614 netbox/virtualization/filtersets.py:294 +#: netbox/dcim/filtersets.py:1625 netbox/virtualization/filtersets.py:294 msgid "Bridged interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1619 +#: netbox/dcim/filtersets.py:1630 msgid "LAG interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1646 netbox/dcim/filtersets.py:1658 -#: netbox/dcim/forms/filtersets.py:1251 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/filtersets.py:1657 netbox/dcim/filtersets.py:1669 +#: netbox/dcim/forms/filtersets.py:1263 netbox/dcim/forms/model_forms.py:1637 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "" -#: netbox/dcim/filtersets.py:1652 +#: netbox/dcim/filtersets.py:1663 msgid "Virtual Device Context (Identifier)" msgstr "" -#: netbox/dcim/filtersets.py:1663 netbox/templates/wireless/wirelesslan.html:11 +#: netbox/dcim/filtersets.py:1674 netbox/templates/wireless/wirelesslan.html:11 #: netbox/wireless/forms/model_forms.py:53 msgid "Wireless LAN" msgstr "" -#: netbox/dcim/filtersets.py:1667 netbox/dcim/tables/devices.py:597 +#: netbox/dcim/filtersets.py:1678 netbox/dcim/tables/devices.py:597 msgid "Wireless link" msgstr "" -#: netbox/dcim/filtersets.py:1737 +#: netbox/dcim/filtersets.py:1748 msgid "Installed module (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1748 +#: netbox/dcim/filtersets.py:1759 msgid "Installed device (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1754 +#: netbox/dcim/filtersets.py:1765 msgid "Installed device (name)" msgstr "" -#: netbox/dcim/filtersets.py:1820 +#: netbox/dcim/filtersets.py:1831 msgid "Master (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1826 +#: netbox/dcim/filtersets.py:1837 msgid "Master (name)" msgstr "" -#: netbox/dcim/filtersets.py:1868 netbox/tenancy/filtersets.py:246 +#: netbox/dcim/filtersets.py:1879 netbox/tenancy/filtersets.py:246 msgid "Tenant (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1874 netbox/extras/filtersets.py:570 +#: netbox/dcim/filtersets.py:1885 netbox/extras/filtersets.py:570 #: netbox/tenancy/filtersets.py:252 msgid "Tenant (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1910 netbox/dcim/forms/filtersets.py:996 +#: netbox/dcim/filtersets.py:1921 netbox/dcim/forms/filtersets.py:1008 msgid "Unterminated" msgstr "" -#: netbox/dcim/filtersets.py:2168 +#: netbox/dcim/filtersets.py:2179 msgid "Power panel (ID)" msgstr "" @@ -2956,7 +2959,7 @@ msgstr "" msgid "Tags" msgstr "" -#: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1396 +#: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1408 #: netbox/dcim/forms/model_forms.py:431 netbox/dcim/forms/model_forms.py:489 #: netbox/dcim/forms/object_create.py:197 #: netbox/dcim/forms/object_create.py:353 netbox/dcim/tables/devices.py:162 @@ -3032,9 +3035,9 @@ msgid "Time zone" msgstr "" #: netbox/dcim/forms/bulk_edit.py:267 netbox/dcim/forms/bulk_edit.py:1160 -#: netbox/dcim/forms/bulk_edit.py:1548 netbox/dcim/forms/bulk_import.py:207 -#: netbox/dcim/forms/bulk_import.py:1021 netbox/dcim/forms/filtersets.py:300 -#: netbox/dcim/forms/filtersets.py:706 netbox/dcim/forms/filtersets.py:1426 +#: netbox/dcim/forms/bulk_edit.py:1548 netbox/dcim/forms/bulk_import.py:204 +#: netbox/dcim/forms/bulk_import.py:1015 netbox/dcim/forms/filtersets.py:301 +#: netbox/dcim/forms/filtersets.py:708 netbox/dcim/forms/filtersets.py:1438 #: netbox/dcim/forms/model_forms.py:219 netbox/dcim/forms/model_forms.py:1018 #: netbox/dcim/forms/model_forms.py:1457 netbox/dcim/forms/object_import.py:181 #: netbox/dcim/tables/devices.py:166 netbox/dcim/tables/devices.py:792 @@ -3081,14 +3084,14 @@ msgstr "" msgid "Serial Number" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:277 netbox/dcim/forms/filtersets.py:307 -#: netbox/dcim/forms/filtersets.py:742 netbox/dcim/forms/filtersets.py:886 -#: netbox/dcim/forms/filtersets.py:1438 +#: netbox/dcim/forms/bulk_edit.py:277 netbox/dcim/forms/filtersets.py:308 +#: netbox/dcim/forms/filtersets.py:744 netbox/dcim/forms/filtersets.py:898 +#: netbox/dcim/forms/filtersets.py:1450 msgid "Asset tag" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:287 netbox/dcim/forms/bulk_import.py:220 -#: netbox/dcim/forms/filtersets.py:292 netbox/templates/dcim/rack.html:86 +#: netbox/dcim/forms/bulk_edit.py:287 netbox/dcim/forms/bulk_import.py:217 +#: netbox/dcim/forms/filtersets.py:293 netbox/templates/dcim/rack.html:86 msgid "Width" msgstr "" @@ -3108,7 +3111,7 @@ msgstr "" msgid "Outer depth" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:311 netbox/dcim/forms/bulk_import.py:225 +#: netbox/dcim/forms/bulk_edit.py:311 netbox/dcim/forms/bulk_import.py:222 msgid "Outer unit" msgstr "" @@ -3119,11 +3122,11 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:321 netbox/dcim/forms/bulk_edit.py:351 #: netbox/dcim/forms/bulk_edit.py:436 netbox/dcim/forms/bulk_edit.py:459 #: netbox/dcim/forms/bulk_edit.py:475 netbox/dcim/forms/bulk_edit.py:495 -#: netbox/dcim/forms/bulk_import.py:332 netbox/dcim/forms/bulk_import.py:358 -#: netbox/dcim/forms/filtersets.py:251 netbox/dcim/forms/filtersets.py:312 -#: netbox/dcim/forms/filtersets.py:336 netbox/dcim/forms/filtersets.py:423 -#: netbox/dcim/forms/filtersets.py:529 netbox/dcim/forms/filtersets.py:548 -#: netbox/dcim/forms/filtersets.py:604 netbox/dcim/forms/model_forms.py:232 +#: netbox/dcim/forms/bulk_import.py:329 netbox/dcim/forms/bulk_import.py:355 +#: netbox/dcim/forms/filtersets.py:252 netbox/dcim/forms/filtersets.py:313 +#: netbox/dcim/forms/filtersets.py:337 netbox/dcim/forms/filtersets.py:424 +#: netbox/dcim/forms/filtersets.py:530 netbox/dcim/forms/filtersets.py:549 +#: netbox/dcim/forms/filtersets.py:605 netbox/dcim/forms/model_forms.py:232 #: netbox/dcim/forms/model_forms.py:346 netbox/dcim/tables/devicetypes.py:103 #: netbox/dcim/tables/modules.py:35 netbox/dcim/tables/racks.py:103 #: netbox/extras/forms/bulk_edit.py:45 netbox/extras/forms/bulk_edit.py:108 @@ -3140,25 +3143,25 @@ msgstr "" msgid "Weight" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:326 netbox/dcim/forms/filtersets.py:317 +#: netbox/dcim/forms/bulk_edit.py:326 netbox/dcim/forms/filtersets.py:318 msgid "Max weight" msgstr "" #: netbox/dcim/forms/bulk_edit.py:331 netbox/dcim/forms/bulk_edit.py:441 -#: netbox/dcim/forms/bulk_edit.py:480 netbox/dcim/forms/bulk_import.py:231 -#: netbox/dcim/forms/bulk_import.py:337 netbox/dcim/forms/bulk_import.py:363 -#: netbox/dcim/forms/filtersets.py:322 netbox/dcim/forms/filtersets.py:533 -#: netbox/dcim/forms/filtersets.py:608 +#: netbox/dcim/forms/bulk_edit.py:480 netbox/dcim/forms/bulk_import.py:228 +#: netbox/dcim/forms/bulk_import.py:334 netbox/dcim/forms/bulk_import.py:360 +#: netbox/dcim/forms/filtersets.py:323 netbox/dcim/forms/filtersets.py:534 +#: netbox/dcim/forms/filtersets.py:609 msgid "Weight unit" msgstr "" #: netbox/dcim/forms/bulk_edit.py:345 netbox/dcim/forms/bulk_edit.py:808 -#: netbox/dcim/forms/bulk_import.py:270 netbox/dcim/forms/bulk_import.py:273 -#: netbox/dcim/forms/bulk_import.py:498 netbox/dcim/forms/bulk_import.py:1309 -#: netbox/dcim/forms/bulk_import.py:1313 netbox/dcim/forms/filtersets.py:102 -#: netbox/dcim/forms/filtersets.py:340 netbox/dcim/forms/filtersets.py:354 -#: netbox/dcim/forms/filtersets.py:392 netbox/dcim/forms/filtersets.py:701 -#: netbox/dcim/forms/filtersets.py:954 netbox/dcim/forms/filtersets.py:1086 +#: netbox/dcim/forms/bulk_import.py:267 netbox/dcim/forms/bulk_import.py:270 +#: netbox/dcim/forms/bulk_import.py:492 netbox/dcim/forms/bulk_import.py:1297 +#: netbox/dcim/forms/bulk_import.py:1301 netbox/dcim/forms/filtersets.py:103 +#: netbox/dcim/forms/filtersets.py:341 netbox/dcim/forms/filtersets.py:355 +#: netbox/dcim/forms/filtersets.py:393 netbox/dcim/forms/filtersets.py:703 +#: netbox/dcim/forms/filtersets.py:966 netbox/dcim/forms/filtersets.py:1098 #: netbox/dcim/forms/model_forms.py:226 netbox/dcim/forms/model_forms.py:248 #: netbox/dcim/forms/model_forms.py:422 netbox/dcim/forms/model_forms.py:703 #: netbox/dcim/forms/object_create.py:400 netbox/dcim/tables/devices.py:158 @@ -3175,9 +3178,9 @@ msgid "Rack" msgstr "" #: netbox/dcim/forms/bulk_edit.py:349 netbox/dcim/forms/bulk_edit.py:628 -#: netbox/dcim/forms/filtersets.py:248 netbox/dcim/forms/filtersets.py:333 -#: netbox/dcim/forms/filtersets.py:416 netbox/dcim/forms/filtersets.py:543 -#: netbox/dcim/forms/filtersets.py:651 netbox/dcim/forms/filtersets.py:861 +#: netbox/dcim/forms/filtersets.py:249 netbox/dcim/forms/filtersets.py:334 +#: netbox/dcim/forms/filtersets.py:417 netbox/dcim/forms/filtersets.py:544 +#: netbox/dcim/forms/filtersets.py:652 netbox/dcim/forms/filtersets.py:873 #: netbox/dcim/forms/model_forms.py:613 netbox/dcim/forms/model_forms.py:1527 #: netbox/templates/dcim/device_edit.html:20 msgid "Hardware" @@ -3186,12 +3189,12 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:402 netbox/dcim/forms/bulk_edit.py:466 #: netbox/dcim/forms/bulk_edit.py:530 netbox/dcim/forms/bulk_edit.py:554 #: netbox/dcim/forms/bulk_edit.py:638 netbox/dcim/forms/bulk_edit.py:1165 -#: netbox/dcim/forms/bulk_edit.py:1553 netbox/dcim/forms/bulk_import.py:319 -#: netbox/dcim/forms/bulk_import.py:353 netbox/dcim/forms/bulk_import.py:395 -#: netbox/dcim/forms/bulk_import.py:431 netbox/dcim/forms/bulk_import.py:1027 -#: netbox/dcim/forms/filtersets.py:429 netbox/dcim/forms/filtersets.py:554 -#: netbox/dcim/forms/filtersets.py:630 netbox/dcim/forms/filtersets.py:711 -#: netbox/dcim/forms/filtersets.py:866 netbox/dcim/forms/filtersets.py:1431 +#: netbox/dcim/forms/bulk_edit.py:1553 netbox/dcim/forms/bulk_import.py:316 +#: netbox/dcim/forms/bulk_import.py:350 netbox/dcim/forms/bulk_import.py:389 +#: netbox/dcim/forms/bulk_import.py:425 netbox/dcim/forms/bulk_import.py:1021 +#: netbox/dcim/forms/filtersets.py:430 netbox/dcim/forms/filtersets.py:555 +#: netbox/dcim/forms/filtersets.py:631 netbox/dcim/forms/filtersets.py:713 +#: netbox/dcim/forms/filtersets.py:878 netbox/dcim/forms/filtersets.py:1443 #: netbox/dcim/forms/model_forms.py:281 netbox/dcim/forms/model_forms.py:293 #: netbox/dcim/forms/model_forms.py:339 netbox/dcim/forms/model_forms.py:379 #: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1462 @@ -3208,13 +3211,13 @@ msgstr "" msgid "Manufacturer" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:407 netbox/dcim/forms/bulk_import.py:325 -#: netbox/dcim/forms/filtersets.py:434 netbox/dcim/forms/model_forms.py:297 +#: netbox/dcim/forms/bulk_edit.py:407 netbox/dcim/forms/bulk_import.py:322 +#: netbox/dcim/forms/filtersets.py:435 netbox/dcim/forms/model_forms.py:297 msgid "Default platform" msgstr "" #: netbox/dcim/forms/bulk_edit.py:412 netbox/dcim/forms/bulk_edit.py:471 -#: netbox/dcim/forms/filtersets.py:437 netbox/dcim/forms/filtersets.py:557 +#: netbox/dcim/forms/filtersets.py:438 netbox/dcim/forms/filtersets.py:558 msgid "Part number" msgstr "" @@ -3227,8 +3230,8 @@ msgid "Exclude from utilization" msgstr "" #: netbox/dcim/forms/bulk_edit.py:431 netbox/dcim/forms/bulk_edit.py:603 -#: netbox/dcim/forms/bulk_import.py:525 netbox/dcim/forms/filtersets.py:446 -#: netbox/dcim/forms/filtersets.py:733 netbox/templates/dcim/device.html:98 +#: netbox/dcim/forms/bulk_import.py:519 netbox/dcim/forms/filtersets.py:447 +#: netbox/dcim/forms/filtersets.py:735 netbox/templates/dcim/device.html:98 #: netbox/templates/dcim/devicetype.html:65 msgid "Airflow" msgstr "" @@ -3251,11 +3254,11 @@ msgid "VM role" msgstr "" #: netbox/dcim/forms/bulk_edit.py:511 netbox/dcim/forms/bulk_edit.py:535 -#: netbox/dcim/forms/bulk_edit.py:618 netbox/dcim/forms/bulk_import.py:376 -#: netbox/dcim/forms/bulk_import.py:380 netbox/dcim/forms/bulk_import.py:402 -#: netbox/dcim/forms/bulk_import.py:406 netbox/dcim/forms/bulk_import.py:531 -#: netbox/dcim/forms/bulk_import.py:535 netbox/dcim/forms/filtersets.py:619 -#: netbox/dcim/forms/filtersets.py:635 netbox/dcim/forms/filtersets.py:752 +#: netbox/dcim/forms/bulk_edit.py:618 netbox/dcim/forms/bulk_import.py:373 +#: netbox/dcim/forms/bulk_import.py:377 netbox/dcim/forms/bulk_import.py:396 +#: netbox/dcim/forms/bulk_import.py:400 netbox/dcim/forms/bulk_import.py:525 +#: netbox/dcim/forms/bulk_import.py:529 netbox/dcim/forms/filtersets.py:620 +#: netbox/dcim/forms/filtersets.py:636 netbox/dcim/forms/filtersets.py:754 #: netbox/dcim/forms/model_forms.py:358 netbox/dcim/forms/model_forms.py:384 #: netbox/dcim/forms/model_forms.py:498 #: netbox/virtualization/forms/bulk_import.py:132 @@ -3266,19 +3269,19 @@ msgid "Config template" msgstr "" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/bulk_edit.py:959 -#: netbox/dcim/forms/bulk_import.py:437 netbox/dcim/forms/filtersets.py:112 +#: netbox/dcim/forms/bulk_import.py:431 netbox/dcim/forms/filtersets.py:113 #: netbox/dcim/forms/model_forms.py:444 netbox/dcim/forms/model_forms.py:820 #: netbox/dcim/forms/model_forms.py:837 netbox/extras/filtersets.py:499 msgid "Device type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:570 netbox/dcim/forms/bulk_import.py:418 -#: netbox/dcim/forms/filtersets.py:117 netbox/dcim/forms/model_forms.py:452 +#: netbox/dcim/forms/bulk_edit.py:570 netbox/dcim/forms/bulk_import.py:412 +#: netbox/dcim/forms/filtersets.py:118 netbox/dcim/forms/model_forms.py:452 msgid "Device role" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:593 netbox/dcim/forms/bulk_import.py:443 -#: netbox/dcim/forms/filtersets.py:725 netbox/dcim/forms/model_forms.py:394 +#: netbox/dcim/forms/bulk_edit.py:593 netbox/dcim/forms/bulk_import.py:437 +#: netbox/dcim/forms/filtersets.py:727 netbox/dcim/forms/model_forms.py:394 #: netbox/dcim/forms/model_forms.py:456 netbox/dcim/tables/devices.py:179 #: netbox/extras/filtersets.py:515 netbox/templates/dcim/device.html:184 #: netbox/templates/dcim/platform.html:26 @@ -3293,21 +3296,21 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:626 netbox/dcim/forms/bulk_edit.py:1179 #: netbox/dcim/forms/bulk_edit.py:1543 netbox/dcim/forms/bulk_edit.py:1589 -#: netbox/dcim/forms/bulk_import.py:586 netbox/dcim/forms/bulk_import.py:648 -#: netbox/dcim/forms/bulk_import.py:674 netbox/dcim/forms/bulk_import.py:700 -#: netbox/dcim/forms/bulk_import.py:720 netbox/dcim/forms/bulk_import.py:773 -#: netbox/dcim/forms/bulk_import.py:891 netbox/dcim/forms/bulk_import.py:939 -#: netbox/dcim/forms/bulk_import.py:956 netbox/dcim/forms/bulk_import.py:968 -#: netbox/dcim/forms/bulk_import.py:1016 netbox/dcim/forms/bulk_import.py:1373 -#: netbox/dcim/forms/connections.py:24 netbox/dcim/forms/filtersets.py:129 -#: netbox/dcim/forms/filtersets.py:840 netbox/dcim/forms/filtersets.py:970 -#: netbox/dcim/forms/filtersets.py:1160 netbox/dcim/forms/filtersets.py:1182 -#: netbox/dcim/forms/filtersets.py:1204 netbox/dcim/forms/filtersets.py:1221 -#: netbox/dcim/forms/filtersets.py:1241 netbox/dcim/forms/filtersets.py:1349 -#: netbox/dcim/forms/filtersets.py:1371 netbox/dcim/forms/filtersets.py:1392 -#: netbox/dcim/forms/filtersets.py:1407 netbox/dcim/forms/filtersets.py:1421 -#: netbox/dcim/forms/filtersets.py:1484 netbox/dcim/forms/filtersets.py:1508 -#: netbox/dcim/forms/filtersets.py:1532 netbox/dcim/forms/model_forms.py:576 +#: netbox/dcim/forms/bulk_import.py:580 netbox/dcim/forms/bulk_import.py:642 +#: netbox/dcim/forms/bulk_import.py:668 netbox/dcim/forms/bulk_import.py:694 +#: netbox/dcim/forms/bulk_import.py:714 netbox/dcim/forms/bulk_import.py:767 +#: netbox/dcim/forms/bulk_import.py:885 netbox/dcim/forms/bulk_import.py:933 +#: netbox/dcim/forms/bulk_import.py:950 netbox/dcim/forms/bulk_import.py:962 +#: netbox/dcim/forms/bulk_import.py:1010 netbox/dcim/forms/bulk_import.py:1361 +#: netbox/dcim/forms/connections.py:24 netbox/dcim/forms/filtersets.py:130 +#: netbox/dcim/forms/filtersets.py:852 netbox/dcim/forms/filtersets.py:982 +#: netbox/dcim/forms/filtersets.py:1172 netbox/dcim/forms/filtersets.py:1194 +#: netbox/dcim/forms/filtersets.py:1216 netbox/dcim/forms/filtersets.py:1233 +#: netbox/dcim/forms/filtersets.py:1253 netbox/dcim/forms/filtersets.py:1361 +#: netbox/dcim/forms/filtersets.py:1383 netbox/dcim/forms/filtersets.py:1404 +#: netbox/dcim/forms/filtersets.py:1419 netbox/dcim/forms/filtersets.py:1433 +#: netbox/dcim/forms/filtersets.py:1496 netbox/dcim/forms/filtersets.py:1520 +#: netbox/dcim/forms/filtersets.py:1544 netbox/dcim/forms/model_forms.py:576 #: netbox/dcim/forms/model_forms.py:797 netbox/dcim/forms/model_forms.py:1156 #: netbox/dcim/forms/model_forms.py:1611 netbox/dcim/forms/object_create.py:257 #: netbox/dcim/tables/connections.py:22 netbox/dcim/tables/connections.py:41 @@ -3363,7 +3366,7 @@ msgstr "" msgid "Configuration" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:643 netbox/dcim/forms/bulk_import.py:598 +#: netbox/dcim/forms/bulk_edit.py:643 netbox/dcim/forms/bulk_import.py:592 #: netbox/dcim/forms/model_forms.py:590 netbox/dcim/forms/model_forms.py:845 msgid "Module type" msgstr "" @@ -3373,7 +3376,7 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:966 netbox/dcim/forms/bulk_edit.py:1010 #: netbox/dcim/forms/bulk_edit.py:1061 netbox/dcim/forms/bulk_edit.py:1088 #: netbox/dcim/forms/bulk_edit.py:1115 netbox/dcim/forms/bulk_edit.py:1133 -#: netbox/dcim/forms/bulk_edit.py:1151 netbox/dcim/forms/filtersets.py:65 +#: netbox/dcim/forms/bulk_edit.py:1151 netbox/dcim/forms/filtersets.py:66 #: netbox/dcim/forms/object_create.py:46 netbox/templates/dcim/cable.html:32 #: netbox/templates/dcim/consoleport.html:32 #: netbox/templates/dcim/consoleserverport.html:32 @@ -3391,13 +3394,13 @@ msgstr "" msgid "Label" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:706 netbox/dcim/forms/filtersets.py:987 +#: netbox/dcim/forms/bulk_edit.py:706 netbox/dcim/forms/filtersets.py:999 #: netbox/templates/dcim/cable.html:50 msgid "Length" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:711 netbox/dcim/forms/bulk_import.py:1174 -#: netbox/dcim/forms/bulk_import.py:1177 netbox/dcim/forms/filtersets.py:991 +#: netbox/dcim/forms/bulk_edit.py:711 netbox/dcim/forms/bulk_import.py:1165 +#: netbox/dcim/forms/bulk_import.py:1168 netbox/dcim/forms/filtersets.py:1003 msgid "Length unit" msgstr "" @@ -3406,32 +3409,32 @@ msgstr "" msgid "Domain" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:803 netbox/dcim/forms/bulk_import.py:1296 -#: netbox/dcim/forms/filtersets.py:1077 netbox/dcim/forms/model_forms.py:698 +#: netbox/dcim/forms/bulk_edit.py:803 netbox/dcim/forms/bulk_import.py:1284 +#: netbox/dcim/forms/filtersets.py:1089 netbox/dcim/forms/model_forms.py:698 msgid "Power panel" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:825 netbox/dcim/forms/bulk_import.py:1332 -#: netbox/dcim/forms/filtersets.py:1099 netbox/templates/dcim/powerfeed.html:83 +#: netbox/dcim/forms/bulk_edit.py:825 netbox/dcim/forms/bulk_import.py:1320 +#: netbox/dcim/forms/filtersets.py:1111 netbox/templates/dcim/powerfeed.html:83 msgid "Supply" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:831 netbox/dcim/forms/bulk_import.py:1337 -#: netbox/dcim/forms/filtersets.py:1104 netbox/templates/dcim/powerfeed.html:95 +#: netbox/dcim/forms/bulk_edit.py:831 netbox/dcim/forms/bulk_import.py:1325 +#: netbox/dcim/forms/filtersets.py:1116 netbox/templates/dcim/powerfeed.html:95 msgid "Phase" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:837 netbox/dcim/forms/filtersets.py:1109 +#: netbox/dcim/forms/bulk_edit.py:837 netbox/dcim/forms/filtersets.py:1121 #: netbox/templates/dcim/powerfeed.html:87 msgid "Voltage" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:841 netbox/dcim/forms/filtersets.py:1113 +#: netbox/dcim/forms/bulk_edit.py:841 netbox/dcim/forms/filtersets.py:1125 #: netbox/templates/dcim/powerfeed.html:91 msgid "Amperage" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:845 netbox/dcim/forms/filtersets.py:1117 +#: netbox/dcim/forms/bulk_edit.py:845 netbox/dcim/forms/filtersets.py:1129 msgid "Max utilization" msgstr "" @@ -3455,13 +3458,13 @@ msgstr "" msgid "Allocated power draw (watts)" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:976 netbox/dcim/forms/bulk_import.py:731 +#: netbox/dcim/forms/bulk_edit.py:976 netbox/dcim/forms/bulk_import.py:725 #: netbox/dcim/forms/model_forms.py:901 netbox/dcim/forms/model_forms.py:1226 #: netbox/dcim/forms/model_forms.py:1514 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:981 netbox/dcim/forms/bulk_import.py:738 +#: netbox/dcim/forms/bulk_edit.py:981 netbox/dcim/forms/bulk_import.py:732 msgid "Feed leg" msgstr "" @@ -3470,7 +3473,7 @@ msgid "Management only" msgstr "" #: netbox/dcim/forms/bulk_edit.py:1037 netbox/dcim/forms/bulk_edit.py:1339 -#: netbox/dcim/forms/bulk_import.py:821 netbox/dcim/forms/filtersets.py:1300 +#: netbox/dcim/forms/bulk_import.py:815 netbox/dcim/forms/filtersets.py:1312 #: netbox/dcim/forms/object_import.py:90 #: netbox/dcim/models/device_component_templates.py:411 #: netbox/dcim/models/device_components.py:671 @@ -3478,14 +3481,14 @@ msgid "PoE mode" msgstr "" #: netbox/dcim/forms/bulk_edit.py:1043 netbox/dcim/forms/bulk_edit.py:1345 -#: netbox/dcim/forms/bulk_import.py:827 netbox/dcim/forms/filtersets.py:1305 +#: netbox/dcim/forms/bulk_import.py:821 netbox/dcim/forms/filtersets.py:1317 #: netbox/dcim/forms/object_import.py:95 #: netbox/dcim/models/device_component_templates.py:417 #: netbox/dcim/models/device_components.py:677 msgid "PoE type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1049 netbox/dcim/forms/filtersets.py:1310 +#: netbox/dcim/forms/bulk_edit.py:1049 netbox/dcim/forms/filtersets.py:1322 #: netbox/dcim/forms/object_import.py:100 msgid "Wireless role" msgstr "" @@ -3512,9 +3515,9 @@ msgstr "" msgid "Virtual device contexts" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1324 netbox/dcim/forms/bulk_import.py:659 -#: netbox/dcim/forms/bulk_import.py:685 netbox/dcim/forms/filtersets.py:1169 -#: netbox/dcim/forms/filtersets.py:1191 netbox/dcim/forms/filtersets.py:1264 +#: netbox/dcim/forms/bulk_edit.py:1324 netbox/dcim/forms/bulk_import.py:653 +#: netbox/dcim/forms/bulk_import.py:679 netbox/dcim/forms/filtersets.py:1181 +#: netbox/dcim/forms/filtersets.py:1203 netbox/dcim/forms/filtersets.py:1276 #: netbox/dcim/tables/devices.py:594 #: netbox/templates/circuits/inc/circuit_termination_fields.html:67 #: netbox/templates/dcim/consoleport.html:40 @@ -3522,7 +3525,7 @@ msgstr "" msgid "Speed" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1353 netbox/dcim/forms/bulk_import.py:830 +#: netbox/dcim/forms/bulk_edit.py:1353 netbox/dcim/forms/bulk_import.py:824 #: netbox/templates/vpn/ikepolicy.html:25 #: netbox/templates/vpn/ipsecprofile.html:21 #: netbox/templates/vpn/ipsecprofile.html:48 @@ -3568,7 +3571,7 @@ msgstr "" msgid "Wireless LANs" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1401 netbox/dcim/forms/filtersets.py:1237 +#: netbox/dcim/forms/bulk_edit.py:1401 netbox/dcim/forms/filtersets.py:1249 #: netbox/dcim/forms/model_forms.py:1337 netbox/ipam/forms/bulk_edit.py:271 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:169 #: netbox/templates/dcim/interface.html:122 @@ -3577,13 +3580,13 @@ msgstr "" msgid "Addressing" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1402 netbox/dcim/forms/filtersets.py:650 +#: netbox/dcim/forms/bulk_edit.py:1402 netbox/dcim/forms/filtersets.py:651 #: netbox/dcim/forms/model_forms.py:1338 #: netbox/virtualization/forms/model_forms.py:350 msgid "Operation" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1403 netbox/dcim/forms/filtersets.py:1238 +#: netbox/dcim/forms/bulk_edit.py:1403 netbox/dcim/forms/filtersets.py:1250 #: netbox/dcim/forms/model_forms.py:935 netbox/dcim/forms/model_forms.py:1340 msgid "PoE" msgstr "" @@ -3631,8 +3634,8 @@ msgstr "" msgid "available options" msgstr "" -#: netbox/dcim/forms/bulk_import.py:133 netbox/dcim/forms/bulk_import.py:488 -#: netbox/dcim/forms/bulk_import.py:1293 netbox/ipam/forms/bulk_import.py:174 +#: netbox/dcim/forms/bulk_import.py:133 netbox/dcim/forms/bulk_import.py:482 +#: netbox/dcim/forms/bulk_import.py:1281 netbox/ipam/forms/bulk_import.py:174 #: netbox/ipam/forms/bulk_import.py:441 #: netbox/virtualization/forms/bulk_import.py:63 #: netbox/virtualization/forms/bulk_import.py:89 @@ -3647,101 +3650,102 @@ msgstr "" msgid "Location not found." msgstr "" -#: netbox/dcim/forms/bulk_import.py:199 +#: netbox/dcim/forms/bulk_import.py:196 msgid "Name of assigned tenant" msgstr "" -#: netbox/dcim/forms/bulk_import.py:211 +#: netbox/dcim/forms/bulk_import.py:208 msgid "Name of assigned role" msgstr "" -#: netbox/dcim/forms/bulk_import.py:217 +#: netbox/dcim/forms/bulk_import.py:214 msgid "Rack type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:222 +#: netbox/dcim/forms/bulk_import.py:219 msgid "Rail-to-rail width (in inches)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:228 +#: netbox/dcim/forms/bulk_import.py:225 msgid "Unit for outer dimensions" msgstr "" -#: netbox/dcim/forms/bulk_import.py:234 +#: netbox/dcim/forms/bulk_import.py:231 msgid "Unit for rack weights" msgstr "" -#: netbox/dcim/forms/bulk_import.py:260 +#: netbox/dcim/forms/bulk_import.py:257 msgid "Parent site" msgstr "" -#: netbox/dcim/forms/bulk_import.py:267 netbox/dcim/forms/bulk_import.py:1306 +#: netbox/dcim/forms/bulk_import.py:264 netbox/dcim/forms/bulk_import.py:1294 msgid "Rack's location (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:276 netbox/dcim/forms/model_forms.py:253 +#: netbox/dcim/forms/bulk_import.py:273 netbox/dcim/forms/model_forms.py:253 #: netbox/dcim/tables/racks.py:153 #: netbox/templates/dcim/rackreservation.html:12 #: netbox/templates/dcim/rackreservation.html:45 msgid "Units" msgstr "" -#: netbox/dcim/forms/bulk_import.py:279 +#: netbox/dcim/forms/bulk_import.py:276 msgid "Comma-separated list of individual unit numbers" msgstr "" -#: netbox/dcim/forms/bulk_import.py:322 +#: netbox/dcim/forms/bulk_import.py:319 msgid "The manufacturer which produces this device type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:329 +#: netbox/dcim/forms/bulk_import.py:326 msgid "The default platform for devices of this type (optional)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:334 +#: netbox/dcim/forms/bulk_import.py:331 msgid "Device weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:340 +#: netbox/dcim/forms/bulk_import.py:337 msgid "Unit for device weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:360 +#: netbox/dcim/forms/bulk_import.py:357 msgid "Module weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:366 +#: netbox/dcim/forms/bulk_import.py:363 msgid "Unit for module weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:399 +#: netbox/dcim/forms/bulk_import.py:393 msgid "Limit platform assignments to this manufacturer" msgstr "" -#: netbox/dcim/forms/bulk_import.py:421 netbox/dcim/forms/bulk_import.py:1376 +#: netbox/dcim/forms/bulk_import.py:415 netbox/dcim/forms/bulk_import.py:1364 #: netbox/tenancy/forms/bulk_import.py:106 msgid "Assigned role" msgstr "" -#: netbox/dcim/forms/bulk_import.py:434 +#: netbox/dcim/forms/bulk_import.py:428 msgid "Device type manufacturer" msgstr "" -#: netbox/dcim/forms/bulk_import.py:440 +#: netbox/dcim/forms/bulk_import.py:434 msgid "Device type model" msgstr "" -#: netbox/dcim/forms/bulk_import.py:447 +#: netbox/dcim/forms/bulk_import.py:441 #: netbox/virtualization/forms/bulk_import.py:126 msgid "Assigned platform" msgstr "" -#: netbox/dcim/forms/bulk_import.py:455 netbox/dcim/forms/bulk_import.py:459 +#: netbox/dcim/forms/bulk_import.py:449 netbox/dcim/forms/bulk_import.py:453 #: netbox/dcim/forms/model_forms.py:479 msgid "Virtual chassis" msgstr "" -#: netbox/dcim/forms/bulk_import.py:462 netbox/dcim/forms/model_forms.py:465 +#: netbox/dcim/forms/bulk_import.py:456 netbox/dcim/forms/filtersets.py:659 +#: netbox/dcim/forms/filtersets.py:829 netbox/dcim/forms/model_forms.py:465 #: netbox/dcim/tables/devices.py:199 netbox/extras/filtersets.py:548 #: netbox/extras/forms/filtersets.py:331 netbox/ipam/forms/bulk_edit.py:479 #: netbox/ipam/forms/filtersets.py:415 netbox/ipam/forms/filtersets.py:459 @@ -3762,147 +3766,147 @@ msgstr "" msgid "Cluster" msgstr "" -#: netbox/dcim/forms/bulk_import.py:466 +#: netbox/dcim/forms/bulk_import.py:460 msgid "Virtualization cluster" msgstr "" -#: netbox/dcim/forms/bulk_import.py:495 +#: netbox/dcim/forms/bulk_import.py:489 msgid "Assigned location (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:502 +#: netbox/dcim/forms/bulk_import.py:496 msgid "Assigned rack (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:505 +#: netbox/dcim/forms/bulk_import.py:499 msgid "Face" msgstr "" -#: netbox/dcim/forms/bulk_import.py:508 +#: netbox/dcim/forms/bulk_import.py:502 msgid "Mounted rack face" msgstr "" -#: netbox/dcim/forms/bulk_import.py:515 +#: netbox/dcim/forms/bulk_import.py:509 msgid "Parent device (for child devices)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:518 +#: netbox/dcim/forms/bulk_import.py:512 msgid "Device bay" msgstr "" -#: netbox/dcim/forms/bulk_import.py:522 +#: netbox/dcim/forms/bulk_import.py:516 msgid "Device bay in which this device is installed (for child devices)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:528 +#: netbox/dcim/forms/bulk_import.py:522 msgid "Airflow direction" msgstr "" -#: netbox/dcim/forms/bulk_import.py:589 +#: netbox/dcim/forms/bulk_import.py:583 msgid "The device in which this module is installed" msgstr "" -#: netbox/dcim/forms/bulk_import.py:592 netbox/dcim/forms/model_forms.py:583 +#: netbox/dcim/forms/bulk_import.py:586 netbox/dcim/forms/model_forms.py:583 msgid "Module bay" msgstr "" -#: netbox/dcim/forms/bulk_import.py:595 +#: netbox/dcim/forms/bulk_import.py:589 msgid "The module bay in which this module is installed" msgstr "" -#: netbox/dcim/forms/bulk_import.py:601 +#: netbox/dcim/forms/bulk_import.py:595 msgid "The type of module" msgstr "" -#: netbox/dcim/forms/bulk_import.py:609 netbox/dcim/forms/model_forms.py:599 +#: netbox/dcim/forms/bulk_import.py:603 netbox/dcim/forms/model_forms.py:599 msgid "Replicate components" msgstr "" -#: netbox/dcim/forms/bulk_import.py:611 +#: netbox/dcim/forms/bulk_import.py:605 msgid "" "Automatically populate components associated with this module type (enabled " "by default)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:614 netbox/dcim/forms/model_forms.py:605 +#: netbox/dcim/forms/bulk_import.py:608 netbox/dcim/forms/model_forms.py:605 msgid "Adopt components" msgstr "" -#: netbox/dcim/forms/bulk_import.py:616 netbox/dcim/forms/model_forms.py:608 +#: netbox/dcim/forms/bulk_import.py:610 netbox/dcim/forms/model_forms.py:608 msgid "Adopt already existing components" msgstr "" -#: netbox/dcim/forms/bulk_import.py:656 netbox/dcim/forms/bulk_import.py:682 -#: netbox/dcim/forms/bulk_import.py:708 +#: netbox/dcim/forms/bulk_import.py:650 netbox/dcim/forms/bulk_import.py:676 +#: netbox/dcim/forms/bulk_import.py:702 msgid "Port type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:664 netbox/dcim/forms/bulk_import.py:690 +#: netbox/dcim/forms/bulk_import.py:658 netbox/dcim/forms/bulk_import.py:684 msgid "Port speed in bps" msgstr "" -#: netbox/dcim/forms/bulk_import.py:728 +#: netbox/dcim/forms/bulk_import.py:722 msgid "Outlet type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:735 +#: netbox/dcim/forms/bulk_import.py:729 msgid "Local power port which feeds this outlet" msgstr "" -#: netbox/dcim/forms/bulk_import.py:741 +#: netbox/dcim/forms/bulk_import.py:735 msgid "Electrical phase (for three-phase circuits)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:782 netbox/dcim/forms/model_forms.py:1264 +#: netbox/dcim/forms/bulk_import.py:776 netbox/dcim/forms/model_forms.py:1264 #: netbox/virtualization/forms/bulk_import.py:155 #: netbox/virtualization/forms/model_forms.py:305 msgid "Parent interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:789 netbox/dcim/forms/model_forms.py:1272 +#: netbox/dcim/forms/bulk_import.py:783 netbox/dcim/forms/model_forms.py:1272 #: netbox/virtualization/forms/bulk_import.py:162 #: netbox/virtualization/forms/model_forms.py:313 msgid "Bridged interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:792 +#: netbox/dcim/forms/bulk_import.py:786 msgid "Lag" msgstr "" -#: netbox/dcim/forms/bulk_import.py:796 +#: netbox/dcim/forms/bulk_import.py:790 msgid "Parent LAG interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:799 +#: netbox/dcim/forms/bulk_import.py:793 msgid "Vdcs" msgstr "" -#: netbox/dcim/forms/bulk_import.py:804 +#: netbox/dcim/forms/bulk_import.py:798 msgid "VDC names separated by commas, encased with double quotes. Example:" msgstr "" -#: netbox/dcim/forms/bulk_import.py:810 +#: netbox/dcim/forms/bulk_import.py:804 msgid "Physical medium" msgstr "" -#: netbox/dcim/forms/bulk_import.py:813 netbox/dcim/forms/filtersets.py:1271 +#: netbox/dcim/forms/bulk_import.py:807 netbox/dcim/forms/filtersets.py:1283 msgid "Duplex" msgstr "" -#: netbox/dcim/forms/bulk_import.py:818 +#: netbox/dcim/forms/bulk_import.py:812 msgid "Poe mode" msgstr "" -#: netbox/dcim/forms/bulk_import.py:824 +#: netbox/dcim/forms/bulk_import.py:818 msgid "Poe type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:833 +#: netbox/dcim/forms/bulk_import.py:827 #: netbox/virtualization/forms/bulk_import.py:168 msgid "IEEE 802.1Q operational mode (for L2 interfaces)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:840 netbox/ipam/forms/bulk_import.py:160 +#: netbox/dcim/forms/bulk_import.py:834 netbox/ipam/forms/bulk_import.py:160 #: netbox/ipam/forms/bulk_import.py:246 netbox/ipam/forms/bulk_import.py:282 #: netbox/ipam/forms/filtersets.py:201 netbox/ipam/forms/filtersets.py:277 #: netbox/ipam/forms/filtersets.py:336 @@ -3910,149 +3914,149 @@ msgstr "" msgid "Assigned VRF" msgstr "" -#: netbox/dcim/forms/bulk_import.py:843 +#: netbox/dcim/forms/bulk_import.py:837 msgid "Rf role" msgstr "" -#: netbox/dcim/forms/bulk_import.py:846 +#: netbox/dcim/forms/bulk_import.py:840 msgid "Wireless role (AP/station)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:882 +#: netbox/dcim/forms/bulk_import.py:876 #, python-brace-format msgid "VDC {vdc} is not assigned to device {device}" msgstr "" -#: netbox/dcim/forms/bulk_import.py:896 netbox/dcim/forms/model_forms.py:948 +#: netbox/dcim/forms/bulk_import.py:890 netbox/dcim/forms/model_forms.py:948 #: netbox/dcim/forms/model_forms.py:1522 netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "" -#: netbox/dcim/forms/bulk_import.py:899 +#: netbox/dcim/forms/bulk_import.py:893 msgid "Corresponding rear port" msgstr "" -#: netbox/dcim/forms/bulk_import.py:904 netbox/dcim/forms/bulk_import.py:945 -#: netbox/dcim/forms/bulk_import.py:1164 +#: netbox/dcim/forms/bulk_import.py:898 netbox/dcim/forms/bulk_import.py:939 +#: netbox/dcim/forms/bulk_import.py:1155 msgid "Physical medium classification" msgstr "" -#: netbox/dcim/forms/bulk_import.py:973 netbox/dcim/tables/devices.py:805 +#: netbox/dcim/forms/bulk_import.py:967 netbox/dcim/tables/devices.py:805 msgid "Installed device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:977 +#: netbox/dcim/forms/bulk_import.py:971 msgid "Child device installed within this bay" msgstr "" -#: netbox/dcim/forms/bulk_import.py:979 +#: netbox/dcim/forms/bulk_import.py:973 msgid "Child device not found." msgstr "" -#: netbox/dcim/forms/bulk_import.py:1037 +#: netbox/dcim/forms/bulk_import.py:1031 msgid "Parent inventory item" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1040 +#: netbox/dcim/forms/bulk_import.py:1034 msgid "Component type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1044 +#: netbox/dcim/forms/bulk_import.py:1038 msgid "Component Type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1047 +#: netbox/dcim/forms/bulk_import.py:1041 msgid "Compnent name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1049 +#: netbox/dcim/forms/bulk_import.py:1043 msgid "Component Name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1091 +#: netbox/dcim/forms/bulk_import.py:1085 #, python-brace-format msgid "Component not found: {device} - {component_name}" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1119 +#: netbox/dcim/forms/bulk_import.py:1110 msgid "Side A device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1122 netbox/dcim/forms/bulk_import.py:1140 +#: netbox/dcim/forms/bulk_import.py:1113 netbox/dcim/forms/bulk_import.py:1131 msgid "Device name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1125 +#: netbox/dcim/forms/bulk_import.py:1116 msgid "Side A type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1128 netbox/dcim/forms/bulk_import.py:1146 +#: netbox/dcim/forms/bulk_import.py:1119 netbox/dcim/forms/bulk_import.py:1137 msgid "Termination type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1131 +#: netbox/dcim/forms/bulk_import.py:1122 msgid "Side A name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1132 netbox/dcim/forms/bulk_import.py:1150 +#: netbox/dcim/forms/bulk_import.py:1123 netbox/dcim/forms/bulk_import.py:1141 msgid "Termination name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1137 +#: netbox/dcim/forms/bulk_import.py:1128 msgid "Side B device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1143 +#: netbox/dcim/forms/bulk_import.py:1134 msgid "Side B type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1149 +#: netbox/dcim/forms/bulk_import.py:1140 msgid "Side B name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1158 +#: netbox/dcim/forms/bulk_import.py:1149 #: netbox/wireless/forms/bulk_import.py:86 msgid "Connection status" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1213 +#: netbox/dcim/forms/bulk_import.py:1201 #, python-brace-format msgid "Side {side_upper}: {device} {termination_object} is already connected" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1219 +#: netbox/dcim/forms/bulk_import.py:1207 #, python-brace-format msgid "{side_upper} side termination not found: {device} {name}" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1244 netbox/dcim/forms/model_forms.py:733 +#: netbox/dcim/forms/bulk_import.py:1232 netbox/dcim/forms/model_forms.py:733 #: netbox/dcim/tables/devices.py:992 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/virtualchassis.html:27 #: netbox/templates/dcim/virtualchassis.html:67 msgid "Master" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1248 +#: netbox/dcim/forms/bulk_import.py:1236 msgid "Master device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1265 +#: netbox/dcim/forms/bulk_import.py:1253 msgid "Name of parent site" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1299 +#: netbox/dcim/forms/bulk_import.py:1287 msgid "Upstream power panel" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1329 +#: netbox/dcim/forms/bulk_import.py:1317 msgid "Primary or redundant" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1334 +#: netbox/dcim/forms/bulk_import.py:1322 msgid "Supply type (AC/DC)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1339 +#: netbox/dcim/forms/bulk_import.py:1327 msgid "Single or three-phase" msgstr "" @@ -4105,11 +4109,11 @@ msgstr "" msgid "Side" msgstr "" -#: netbox/dcim/forms/filtersets.py:142 +#: netbox/dcim/forms/filtersets.py:143 msgid "Parent region" msgstr "" -#: netbox/dcim/forms/filtersets.py:156 netbox/tenancy/forms/bulk_import.py:28 +#: netbox/dcim/forms/filtersets.py:157 netbox/tenancy/forms/bulk_import.py:28 #: netbox/tenancy/forms/bulk_import.py:62 netbox/tenancy/forms/filtersets.py:33 #: netbox/tenancy/forms/filtersets.py:62 #: netbox/wireless/forms/bulk_import.py:25 @@ -4117,51 +4121,58 @@ msgstr "" msgid "Parent group" msgstr "" -#: netbox/dcim/forms/filtersets.py:247 netbox/dcim/forms/filtersets.py:332 +#: netbox/dcim/forms/filtersets.py:248 netbox/dcim/forms/filtersets.py:333 msgid "Function" msgstr "" -#: netbox/dcim/forms/filtersets.py:418 netbox/dcim/forms/model_forms.py:317 +#: netbox/dcim/forms/filtersets.py:419 netbox/dcim/forms/model_forms.py:317 #: netbox/templates/inc/panels/image_attachments.html:6 msgid "Images" msgstr "" -#: netbox/dcim/forms/filtersets.py:421 netbox/dcim/forms/filtersets.py:546 -#: netbox/dcim/forms/filtersets.py:656 +#: netbox/dcim/forms/filtersets.py:422 netbox/dcim/forms/filtersets.py:547 +#: netbox/dcim/forms/filtersets.py:657 msgid "Components" msgstr "" -#: netbox/dcim/forms/filtersets.py:441 +#: netbox/dcim/forms/filtersets.py:442 msgid "Subdevice role" msgstr "" -#: netbox/dcim/forms/filtersets.py:719 +#: netbox/dcim/forms/filtersets.py:721 msgid "Model" msgstr "" -#: netbox/dcim/forms/filtersets.py:763 +#: netbox/dcim/forms/filtersets.py:765 msgid "Has an OOB IP" msgstr "" -#: netbox/dcim/forms/filtersets.py:770 +#: netbox/dcim/forms/filtersets.py:772 msgid "Virtual chassis member" msgstr "" -#: netbox/dcim/forms/filtersets.py:819 +#: netbox/dcim/forms/filtersets.py:821 msgid "Has virtual device contexts" msgstr "" -#: netbox/dcim/forms/filtersets.py:1129 +#: netbox/dcim/forms/filtersets.py:834 netbox/extras/filtersets.py:537 +#: netbox/ipam/forms/bulk_edit.py:476 netbox/ipam/forms/filtersets.py:464 +#: netbox/ipam/forms/model_forms.py:624 +#: netbox/virtualization/forms/filtersets.py:112 +msgid "Cluster group" +msgstr "" + +#: netbox/dcim/forms/filtersets.py:1141 msgid "Cabled" msgstr "" -#: netbox/dcim/forms/filtersets.py:1136 +#: netbox/dcim/forms/filtersets.py:1148 msgid "Occupied" msgstr "" -#: netbox/dcim/forms/filtersets.py:1161 netbox/dcim/forms/filtersets.py:1183 -#: netbox/dcim/forms/filtersets.py:1205 netbox/dcim/forms/filtersets.py:1222 -#: netbox/dcim/forms/filtersets.py:1242 netbox/dcim/tables/devices.py:352 +#: netbox/dcim/forms/filtersets.py:1173 netbox/dcim/forms/filtersets.py:1195 +#: netbox/dcim/forms/filtersets.py:1217 netbox/dcim/forms/filtersets.py:1234 +#: netbox/dcim/forms/filtersets.py:1254 netbox/dcim/tables/devices.py:352 #: netbox/templates/dcim/consoleport.html:55 #: netbox/templates/dcim/consoleserverport.html:55 #: netbox/templates/dcim/frontport.html:69 @@ -4173,40 +4184,40 @@ msgstr "" msgid "Connection" msgstr "" -#: netbox/dcim/forms/filtersets.py:1254 netbox/extras/forms/bulk_edit.py:316 -#: netbox/extras/forms/bulk_import.py:242 netbox/extras/forms/filtersets.py:473 +#: netbox/dcim/forms/filtersets.py:1266 netbox/extras/forms/bulk_edit.py:316 +#: netbox/extras/forms/bulk_import.py:239 netbox/extras/forms/filtersets.py:473 #: netbox/extras/forms/model_forms.py:551 netbox/extras/tables/tables.py:513 #: netbox/templates/extras/journalentry.html:30 msgid "Kind" msgstr "" -#: netbox/dcim/forms/filtersets.py:1283 +#: netbox/dcim/forms/filtersets.py:1295 msgid "Mgmt only" msgstr "" -#: netbox/dcim/forms/filtersets.py:1295 netbox/dcim/forms/model_forms.py:1330 +#: netbox/dcim/forms/filtersets.py:1307 netbox/dcim/forms/model_forms.py:1330 #: netbox/dcim/models/device_components.py:630 #: netbox/templates/dcim/interface.html:129 msgid "WWN" msgstr "" -#: netbox/dcim/forms/filtersets.py:1315 +#: netbox/dcim/forms/filtersets.py:1327 msgid "Wireless channel" msgstr "" -#: netbox/dcim/forms/filtersets.py:1319 +#: netbox/dcim/forms/filtersets.py:1331 msgid "Channel frequency (MHz)" msgstr "" -#: netbox/dcim/forms/filtersets.py:1323 +#: netbox/dcim/forms/filtersets.py:1335 msgid "Channel width (MHz)" msgstr "" -#: netbox/dcim/forms/filtersets.py:1327 netbox/templates/dcim/interface.html:85 +#: netbox/dcim/forms/filtersets.py:1339 netbox/templates/dcim/interface.html:85 msgid "Transmit power (dBm)" msgstr "" -#: netbox/dcim/forms/filtersets.py:1350 netbox/dcim/forms/filtersets.py:1372 +#: netbox/dcim/forms/filtersets.py:1362 netbox/dcim/forms/filtersets.py:1384 #: netbox/dcim/tables/devices.py:316 netbox/templates/dcim/cable.html:12 #: netbox/templates/dcim/cable_trace.html:46 #: netbox/templates/dcim/frontport.html:77 @@ -4217,7 +4228,7 @@ msgstr "" msgid "Cable" msgstr "" -#: netbox/dcim/forms/filtersets.py:1442 netbox/dcim/tables/devices.py:915 +#: netbox/dcim/forms/filtersets.py:1454 netbox/dcim/tables/devices.py:915 msgid "Discovered" msgstr "" @@ -6807,16 +6818,6 @@ msgstr "" msgid "Cluster type (slug)" msgstr "" -#: netbox/extras/filtersets.py:537 netbox/ipam/forms/bulk_edit.py:476 -#: netbox/ipam/forms/filtersets.py:464 netbox/ipam/forms/model_forms.py:624 -#: netbox/virtualization/forms/filtersets.py:112 -msgid "Cluster group" -msgstr "" - -#: netbox/extras/filtersets.py:543 netbox/virtualization/filtersets.py:136 -msgid "Cluster group (slug)" -msgstr "" - #: netbox/extras/filtersets.py:553 netbox/tenancy/forms/forms.py:16 #: netbox/tenancy/forms/forms.py:39 msgid "Tenant group" @@ -7032,11 +7033,11 @@ msgstr "" msgid "Script {name} not found" msgstr "" -#: netbox/extras/forms/bulk_import.py:239 +#: netbox/extras/forms/bulk_import.py:236 msgid "Assigned object type" msgstr "" -#: netbox/extras/forms/bulk_import.py:244 +#: netbox/extras/forms/bulk_import.py:241 msgid "The classification of entry" msgstr "" @@ -9933,6 +9934,10 @@ msgstr "" msgid "Object type(s)" msgstr "" +#: netbox/netbox/forms/__init__.py:40 +msgid "Lookup" +msgstr "" + #: netbox/netbox/forms/base.py:88 msgid "" "Tag slugs separated by commas, encased with double quotes (e.g. \"tag1,tag2," @@ -13780,14 +13785,18 @@ msgstr "" msgid "More than 50" msgstr "" -#: netbox/utilities/fields.py:157 +#: netbox/utilities/fields.py:30 +msgid "RGB color in hexadecimal. Example: " +msgstr "" + +#: netbox/utilities/fields.py:159 #, python-format msgid "" "%s(%r) is invalid. to_model parameter to CounterCacheField must be a string " "in the format 'app.model'" msgstr "" -#: netbox/utilities/fields.py:167 +#: netbox/utilities/fields.py:169 #, python-format msgid "" "%s(%r) is invalid. to_field parameter to CounterCacheField must be a string " @@ -14120,10 +14129,6 @@ msgstr "" msgid "Cluster type (ID)" msgstr "" -#: netbox/virtualization/filtersets.py:130 -msgid "Cluster group (ID)" -msgstr "" - #: netbox/virtualization/filtersets.py:151 #: netbox/virtualization/filtersets.py:267 msgid "Cluster (ID)" From 00d23a0cffa19c491c9219c847f206eec4d054a9 Mon Sep 17 00:00:00 2001 From: Julio Oliveira at Encora <149191228+Julio-Oliveira-Encora@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:30:39 -0300 Subject: [PATCH 08/42] 16725 - The admin section should always come last in the navigation menu (#16762) * I replaced `append` with `insert` into menu.py to make the admin section appear last in the navigation menu. * Clean up ordering logic --------- Co-authored-by: Jeremy Stretch --- netbox/netbox/navigation/menu.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 002dfd98a9c..cae9cb3215d 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -462,16 +462,13 @@ PROVISIONING_MENU, CUSTOMIZATION_MENU, OPERATIONS_MENU, - ADMIN_MENU, ] -# -# Add plugin menus -# - +# Add top-level plugin menus for menu in registry['plugins']['menus']: MENUS.append(menu) +# Add the default "plugins" menu if registry['plugins']['menu_items']: # Build the default plugins menu @@ -485,3 +482,6 @@ groups=groups ) MENUS.append(plugins_menu) + +# Add the admin menu last +MENUS.append(ADMIN_MENU) From 96338c002b3d3e797f29b6cfa7ecde595d0387ea Mon Sep 17 00:00:00 2001 From: Peter Eckel Date: Thu, 27 Jun 2024 17:19:14 +0000 Subject: [PATCH 09/42] Updated the documentation section about removing plugins --- docs/plugins/removal.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/plugins/removal.md b/docs/plugins/removal.md index f5e81bdc083..37228a939b6 100644 --- a/docs/plugins/removal.md +++ b/docs/plugins/removal.md @@ -70,3 +70,19 @@ DROP TABLE netbox=> DROP TABLE pluginname_bar; DROP TABLE ``` + +### Remove the Django Migration Records + +After removing the tables created by a plugin, the migrations that created the tables need to be removed from Django's migration history as well. This is necessary to make it possible to reinstall the plugin at a later time. If the migration history were left in place, Django would skip all migrations that were executed in the course of a previous installation, which would cause the plugin to fail after reinstallation. + +```no-highlight +netbox=> SELECT * FROM django_migrations WHERE app='pluginname'; + id | app | name | applied +-----+------------+------------------------+------------------------------- + 492 | pluginname | 0001_initial | 2023-12-21 11:59:59.325995+00 + 493 | pluginname | 0002_add_foo | 2023-12-21 11:59:59.330026+00 +netbox=> DELETE FROM django_migrations WHERE app='pluginname'; +``` + +!!! warning + Exercise extreme caution when altering Django system tables. Users are strongly encouraged to perform a backup of their database immediately before taking these actions. From 2c64a52d7db3e307b8e67860393d3fd7e4a79994 Mon Sep 17 00:00:00 2001 From: Julio-Oliveira-Encora Date: Thu, 27 Jun 2024 11:57:07 -0300 Subject: [PATCH 10/42] Added default:"0" to total_count in object_list.html --- netbox/templates/generic/object_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/generic/object_list.html b/netbox/templates/generic/object_list.html index aa1f482bfd3..74e70f4765e 100644 --- a/netbox/templates/generic/object_list.html +++ b/netbox/templates/generic/object_list.html @@ -48,7 +48,7 @@ {% if filter_form %} From a896b14c087873222007d1d3fb2b6960fe8ff2b3 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Thu, 27 Jun 2024 14:49:21 +0200 Subject: [PATCH 11/42] Fixes #16689: Load correct configuration Loads the the current configuration if no ConfigRevisions are saved to the database. --- netbox/core/views.py | 2 +- netbox/templates/core/system.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/core/views.py b/netbox/core/views.py index e454f109e3f..9697a73524d 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -555,7 +555,7 @@ def get(self, request): config = ConfigRevision.objects.get(pk=cache.get('config_version')) except ConfigRevision.DoesNotExist: # Fall back to using the active config data if no record is found - config = ConfigRevision(data=get_config().defaults) + config = get_config() # Raw data export if 'export' in request.GET: diff --git a/netbox/templates/core/system.html b/netbox/templates/core/system.html index 320038ac661..4ffeb5025e2 100644 --- a/netbox/templates/core/system.html +++ b/netbox/templates/core/system.html @@ -88,7 +88,7 @@
{% trans "Plugins" %}
{% trans "Current Configuration" %}
- {% include 'core/inc/config_data.html' with config=config.data %} + {% include 'core/inc/config_data.html' %}
From a00ed4b74d9ce54c4c0bbaabe73afc23e7d77eb9 Mon Sep 17 00:00:00 2001 From: prryplatypus <25409753+prryplatypus@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:56:55 +0200 Subject: [PATCH 12/42] Quote `VIRTUALENV` --- upgrade.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/upgrade.sh b/upgrade.sh index 0b2cd383b40..74b4cb23932 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -33,7 +33,7 @@ echo "Using ${PYTHON_VERSION}" # Remove the existing virtual environment (if any) if [ -d "$VIRTUALENV" ]; then - COMMAND="rm -rf ${VIRTUALENV}" + COMMAND="rm -rf \"${VIRTUALENV}\"" echo "Removing old virtual environment..." eval $COMMAND else @@ -41,7 +41,7 @@ else fi # Create a new virtual environment -COMMAND="${PYTHON} -m venv ${VIRTUALENV}" +COMMAND="${PYTHON} -m venv \"${VIRTUALENV}\"" echo "Creating a new virtual environment at ${VIRTUALENV}..." eval $COMMAND || { echo "--------------------------------------------------------------------" From 67983c6a75a667d79141348f7a03f48e0c8664b5 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 05:02:10 +0000 Subject: [PATCH 13/42] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index ebbf64e448d..df6350f9d3f 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-27 05:01+0000\n" +"POT-Creation-Date: 2024-07-01 05:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -10334,7 +10334,7 @@ msgstr "" msgid "Background Tasks" msgstr "" -#: netbox/netbox/navigation/menu.py:483 netbox/templates/500.html:35 +#: netbox/netbox/navigation/menu.py:480 netbox/templates/500.html:35 #: netbox/templates/account/preferences.html:22 #: netbox/templates/core/system.html:80 msgid "Plugins" From b5d8e657ade77630a7d3e0f89847342a61228db4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Sun, 30 Jun 2024 15:37:11 -0400 Subject: [PATCH 14/42] Fixes #16523: Restore highlighting of current device in virtual chassis members panel --- netbox/templates/dcim/device.html | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 50136f7a93c..e3ac944bc0e 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -125,28 +125,30 @@
- - - - - + + + + + + + + {% for vc_member in vc_members %} - - - - - - + + + + + + {% endfor %} +
{% trans "Device" %}{% trans "Position" %}{% trans "Master" %}{% trans "Priority" %}
{% trans "Device" %}{% trans "Position" %}{% trans "Master" %}{% trans "Priority" %}
- {{ vc_member|linkify }} - - {% badge vc_member.vc_position show_empty=True %} - - {% if object.virtual_chassis.master == vc_member %}{% endif %} - - {{ vc_member.vc_priority|placeholder }} -
{{ vc_member|linkify }}{% badge vc_member.vc_position show_empty=True %} + {% if object.virtual_chassis.master == vc_member %} + {% checkmark True %} + {% else %} + {{ ''|placeholder }} + {% endif %} + {{ vc_member.vc_priority|placeholder }}
{% endif %} From 753ba5d3f4bd3b5bf96aebd5770e15f6f6eda8dd Mon Sep 17 00:00:00 2001 From: Peter Eckel <6815386+peteeckel@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:12:02 +0200 Subject: [PATCH 15/42] Do not delete all search indexes when reindexing specific models (#16755) * Do not delete all search indexes when reindexing specific models * Clear all indexes only if neither --lazy nor a list of models are specified for "manage.py reindex" * Otherwise, clear the index for a model immediately before rebuilding it * Separated clearing from re-indexing the search cache --- netbox/extras/management/commands/reindex.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/netbox/extras/management/commands/reindex.py b/netbox/extras/management/commands/reindex.py index e20fad0ceb8..5aab745115a 100644 --- a/netbox/extras/management/commands/reindex.py +++ b/netbox/extras/management/commands/reindex.py @@ -66,11 +66,16 @@ def handle(self, *model_labels, **kwargs): raise CommandError(_("No indexers found!")) self.stdout.write(f'Reindexing {len(indexers)} models.') - # Clear all cached values for the specified models (if not being lazy) + # Clear cached values for the specified models (if not being lazy) if not kwargs['lazy']: + if model_labels: + content_types = [ContentType.objects.get_for_model(model) for model in indexers.keys()] + else: + content_types = None + self.stdout.write('Clearing cached values... ', ending='') self.stdout.flush() - deleted_count = search_backend.clear() + deleted_count = search_backend.clear(object_types=content_types) self.stdout.write(f'{deleted_count} entries deleted.') # Index models From a12259fae7d1a9aacde09e5c6c6c9d14f38a8b36 Mon Sep 17 00:00:00 2001 From: siku4 Date: Fri, 21 Jun 2024 23:16:52 +0200 Subject: [PATCH 16/42] fix: add missing parent field to inventory item import form --- netbox/dcim/forms/bulk_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 1c537512c3a..e49d14dd01a 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -1046,7 +1046,7 @@ class InventoryItemImportForm(NetBoxModelImportForm): class Meta: model = InventoryItem fields = ( - 'device', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', + 'device', 'name', 'label', 'role', 'manufacturer', 'parent', 'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'tags', 'component_type', 'component_name', ) From e2596587faa5a006f98b517c28ef204199e7bb13 Mon Sep 17 00:00:00 2001 From: Elliott Balsley <3991046+llamafilm@users.noreply.github.com> Date: Mon, 1 Jul 2024 06:22:09 -0700 Subject: [PATCH 17/42] fix: allow cloning field value of 0 (#16741) * fix: allow cloning field value of 0 * Fix evaluation of False value --------- Co-authored-by: Jeremy Stretch --- netbox/utilities/querydict.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/utilities/querydict.py b/netbox/utilities/querydict.py index a2296cb1a58..78395758af3 100644 --- a/netbox/utilities/querydict.py +++ b/netbox/utilities/querydict.py @@ -55,7 +55,7 @@ def prepare_cloned_fields(instance): for key, value in attrs.items(): if type(value) in (list, tuple): params.extend([(key, v) for v in value]) - elif value not in (False, None): + elif value is not False and value is not None: params.append((key, value)) else: params.append((key, '')) From d3d27d81113c3f4e6986f860f57593d8a5b5e8e2 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 1 Jul 2024 09:40:21 -0400 Subject: [PATCH 18/42] Changelog for #16424, #16523, #16654, #16657, #16689, #16714, #16723, #16725, #16735, #16747 --- docs/release-notes/version-4.0.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/release-notes/version-4.0.md b/docs/release-notes/version-4.0.md index 4537a8800f9..028381f683c 100644 --- a/docs/release-notes/version-4.0.md +++ b/docs/release-notes/version-4.0.md @@ -2,6 +2,22 @@ ## v4.0.7 (FUTURE) +### Enhancements + +* [#16424](https://github.com/netbox-community/netbox/issues/16424) - Enable filtering of devices by cluster and cluster group +* [#16725](https://github.com/netbox-community/netbox/issues/16725) - Always position the admin section last in the navigation menu + +### Bug Fixes + +* [#16523](https://github.com/netbox-community/netbox/issues/16523) - Restore highlighting of current device in virtual chassis members panel +* [#16654](https://github.com/netbox-community/netbox/issues/16654) - Fix parent item assignment for inventory item bulk import +* [#16657](https://github.com/netbox-community/netbox/issues/16657) - Fix translation of object types in global search +* [#16689](https://github.com/netbox-community/netbox/issues/16689) - System configuration view should reflect static parameters when no config revisions exist +* [#16714](https://github.com/netbox-community/netbox/issues/16714) - Fix cloning of device types with 0U height +* [#16723](https://github.com/netbox-community/netbox/issues/16723) - Fix escaping of path to virtual environment in `upgrade.sh` +* [#16735](https://github.com/netbox-community/netbox/issues/16735) - Object list "results" tab should show a count of zero when empty +* [#16747](https://github.com/netbox-community/netbox/issues/16747) - Avoid clearing entire search cache when manually reindexing specific apps/models + --- ## v4.0.6 (2024-06-24) From 4857a87be5bd357b3d583ecb99a7841d75c9da35 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 05:02:15 +0000 Subject: [PATCH 19/42] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 66 ++++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index df6350f9d3f..21d35116d61 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-07-01 05:01+0000\n" +"POT-Creation-Date: 2024-07-02 05:02+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -601,7 +601,7 @@ msgstr "" #: netbox/templates/circuits/circuit.html:34 #: netbox/templates/core/datasource.html:46 netbox/templates/core/job.html:30 #: netbox/templates/core/rq_task.html:81 netbox/templates/core/system.html:18 -#: netbox/templates/dcim/cable.html:19 netbox/templates/dcim/device.html:176 +#: netbox/templates/dcim/cable.html:19 netbox/templates/dcim/device.html:178 #: netbox/templates/dcim/location.html:45 netbox/templates/dcim/module.html:66 #: netbox/templates/dcim/powerfeed.html:36 netbox/templates/dcim/rack.html:43 #: netbox/templates/dcim/site.html:43 @@ -2132,7 +2132,7 @@ msgstr "" msgid "Reserved" msgstr "" -#: netbox/dcim/choices.py:101 netbox/templates/dcim/device.html:252 +#: netbox/dcim/choices.py:101 netbox/templates/dcim/device.html:254 msgid "Available" msgstr "" @@ -2189,14 +2189,14 @@ msgstr "" msgid "Child" msgstr "" -#: netbox/dcim/choices.py:155 netbox/templates/dcim/device.html:332 +#: netbox/dcim/choices.py:155 netbox/templates/dcim/device.html:334 #: netbox/templates/dcim/rack.html:175 #: netbox/templates/dcim/rack_elevation_list.html:20 #: netbox/templates/dcim/rackreservation.html:76 msgid "Front" msgstr "" -#: netbox/dcim/choices.py:156 netbox/templates/dcim/device.html:338 +#: netbox/dcim/choices.py:156 netbox/templates/dcim/device.html:340 #: netbox/templates/dcim/rack.html:181 #: netbox/templates/dcim/rack_elevation_list.html:21 #: netbox/templates/dcim/rackreservation.html:82 @@ -2420,7 +2420,7 @@ msgstr "" msgid "Feet" msgstr "" -#: netbox/dcim/choices.py:1497 netbox/templates/dcim/device.html:320 +#: netbox/dcim/choices.py:1497 netbox/templates/dcim/device.html:322 #: netbox/templates/dcim/rack.html:152 msgid "Kilograms" msgstr "" @@ -2964,7 +2964,7 @@ msgstr "" #: netbox/dcim/forms/object_create.py:197 #: netbox/dcim/forms/object_create.py:353 netbox/dcim/tables/devices.py:162 #: netbox/dcim/tables/devices.py:690 netbox/dcim/tables/devicetypes.py:242 -#: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:130 +#: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:34 #: netbox/templates/dcim/virtualchassis.html:66 #: netbox/templates/dcim/virtualchassis_edit.html:55 @@ -3053,7 +3053,7 @@ msgstr "" #: netbox/ipam/forms/model_forms.py:248 netbox/ipam/forms/model_forms.py:689 #: netbox/ipam/tables/ip.py:257 netbox/ipam/tables/ip.py:313 #: netbox/ipam/tables/ip.py:363 netbox/ipam/tables/vlans.py:126 -#: netbox/ipam/tables/vlans.py:230 netbox/templates/dcim/device.html:180 +#: netbox/ipam/tables/vlans.py:230 netbox/templates/dcim/device.html:182 #: netbox/templates/dcim/inc/panels/inventory_items.html:20 #: netbox/templates/dcim/interface.html:223 #: netbox/templates/dcim/inventoryitem.html:36 @@ -3133,7 +3133,7 @@ msgstr "" #: netbox/extras/forms/bulk_edit.py:158 netbox/extras/forms/bulk_edit.py:278 #: netbox/extras/forms/filtersets.py:61 netbox/extras/forms/filtersets.py:134 #: netbox/extras/forms/filtersets.py:221 netbox/ipam/forms/bulk_edit.py:188 -#: netbox/templates/dcim/device.html:317 +#: netbox/templates/dcim/device.html:319 #: netbox/templates/dcim/devicetype.html:49 #: netbox/templates/dcim/moduletype.html:30 #: netbox/templates/extras/configcontext.html:17 @@ -3283,7 +3283,7 @@ msgstr "" #: netbox/dcim/forms/bulk_edit.py:593 netbox/dcim/forms/bulk_import.py:437 #: netbox/dcim/forms/filtersets.py:727 netbox/dcim/forms/model_forms.py:394 #: netbox/dcim/forms/model_forms.py:456 netbox/dcim/tables/devices.py:179 -#: netbox/extras/filtersets.py:515 netbox/templates/dcim/device.html:184 +#: netbox/extras/filtersets.py:515 netbox/templates/dcim/device.html:186 #: netbox/templates/dcim/platform.html:26 #: netbox/templates/virtualization/virtualmachine.html:27 #: netbox/virtualization/forms/bulk_edit.py:160 @@ -3327,7 +3327,7 @@ msgstr "" #: netbox/ipam/forms/model_forms.py:758 netbox/ipam/forms/model_forms.py:784 #: netbox/ipam/tables/vlans.py:176 netbox/templates/dcim/consoleport.html:20 #: netbox/templates/dcim/consoleserverport.html:20 -#: netbox/templates/dcim/device.html:15 netbox/templates/dcim/device.html:129 +#: netbox/templates/dcim/device.html:15 netbox/templates/dcim/device.html:130 #: netbox/templates/dcim/device_edit.html:10 #: netbox/templates/dcim/devicebay.html:20 #: netbox/templates/dcim/devicebay.html:48 @@ -3749,7 +3749,7 @@ msgstr "" #: netbox/dcim/tables/devices.py:199 netbox/extras/filtersets.py:548 #: netbox/extras/forms/filtersets.py:331 netbox/ipam/forms/bulk_edit.py:479 #: netbox/ipam/forms/filtersets.py:415 netbox/ipam/forms/filtersets.py:459 -#: netbox/ipam/forms/model_forms.py:627 netbox/templates/dcim/device.html:232 +#: netbox/ipam/forms/model_forms.py:627 netbox/templates/dcim/device.html:234 #: netbox/templates/virtualization/cluster.html:10 #: netbox/templates/virtualization/virtualmachine.html:88 #: netbox/templates/virtualization/virtualmachine.html:97 @@ -4030,7 +4030,7 @@ msgid "{side_upper} side termination not found: {device} {name}" msgstr "" #: netbox/dcim/forms/bulk_import.py:1232 netbox/dcim/forms/model_forms.py:733 -#: netbox/dcim/tables/devices.py:992 netbox/templates/dcim/device.html:131 +#: netbox/dcim/tables/devices.py:992 netbox/templates/dcim/device.html:132 #: netbox/templates/dcim/virtualchassis.html:27 #: netbox/templates/dcim/virtualchassis.html:67 msgid "Master" @@ -4253,7 +4253,7 @@ msgstr "" msgid "Outer Dimensions" msgstr "" -#: netbox/dcim/forms/model_forms.py:233 netbox/templates/dcim/device.html:308 +#: netbox/dcim/forms/model_forms.py:233 netbox/templates/dcim/device.html:310 #: netbox/templates/dcim/rack.html:73 msgid "Dimensions" msgstr "" @@ -4291,7 +4291,7 @@ msgstr "" msgid "The position in the virtual chassis this device is identified by" msgstr "" -#: netbox/dcim/forms/model_forms.py:494 netbox/templates/dcim/device.html:132 +#: netbox/dcim/forms/model_forms.py:494 netbox/templates/dcim/device.html:133 #: netbox/templates/dcim/virtualchassis.html:68 #: netbox/templates/dcim/virtualchassis_edit.html:56 #: netbox/templates/ipam/inc/panels/fhrp_groups.html:26 @@ -4467,13 +4467,13 @@ msgstr "" msgid "Inventory Item Role" msgstr "" -#: netbox/dcim/forms/model_forms.py:1617 netbox/templates/dcim/device.html:188 +#: netbox/dcim/forms/model_forms.py:1617 netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:48 msgid "Primary IPv4" msgstr "" -#: netbox/dcim/forms/model_forms.py:1626 netbox/templates/dcim/device.html:204 +#: netbox/dcim/forms/model_forms.py:1626 netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:64 msgid "Primary IPv6" @@ -6330,7 +6330,7 @@ msgstr "" msgid "Racks" msgstr "" -#: netbox/dcim/tables/racks.py:73 netbox/templates/dcim/device.html:311 +#: netbox/dcim/tables/racks.py:73 netbox/templates/dcim/device.html:313 #: netbox/templates/dcim/rack.html:90 msgid "Height" msgstr "" @@ -9498,7 +9498,7 @@ msgstr "" #: netbox/ipam/tables/ip.py:130 netbox/ipam/tables/ip.py:267 #: netbox/ipam/tables/ip.py:320 netbox/ipam/tables/vlans.py:82 -#: netbox/templates/dcim/device.html:253 +#: netbox/templates/dcim/device.html:255 #: netbox/templates/ipam/aggregate.html:24 #: netbox/templates/ipam/iprange.html:29 netbox/templates/ipam/prefix.html:106 msgid "Utilization" @@ -10038,7 +10038,7 @@ msgstr "" msgid "Modules" msgstr "" -#: netbox/netbox/navigation/menu.py:67 netbox/templates/dcim/device.html:158 +#: netbox/netbox/navigation/menu.py:67 netbox/templates/dcim/device.html:160 #: netbox/templates/dcim/virtualdevicecontext.html:8 msgid "Virtual Device Contexts" msgstr "" @@ -10100,7 +10100,7 @@ msgstr "" msgid "Service Templates" msgstr "" -#: netbox/netbox/navigation/menu.py:191 netbox/templates/dcim/device.html:295 +#: netbox/netbox/navigation/menu.py:191 netbox/templates/dcim/device.html:297 #: netbox/templates/ipam/ipaddress.html:118 #: netbox/templates/virtualization/virtualmachine.html:150 msgid "Services" @@ -11370,56 +11370,56 @@ msgstr "" msgid "View Virtual Chassis" msgstr "" -#: netbox/templates/dcim/device.html:162 +#: netbox/templates/dcim/device.html:164 msgid "Create VDC" msgstr "" -#: netbox/templates/dcim/device.html:173 +#: netbox/templates/dcim/device.html:175 #: netbox/templates/dcim/device_edit.html:64 #: netbox/virtualization/forms/model_forms.py:223 msgid "Management" msgstr "" -#: netbox/templates/dcim/device.html:193 netbox/templates/dcim/device.html:209 +#: netbox/templates/dcim/device.html:195 netbox/templates/dcim/device.html:211 #: netbox/templates/virtualization/virtualmachine.html:53 #: netbox/templates/virtualization/virtualmachine.html:69 msgid "NAT for" msgstr "" -#: netbox/templates/dcim/device.html:195 netbox/templates/dcim/device.html:211 +#: netbox/templates/dcim/device.html:197 netbox/templates/dcim/device.html:213 #: netbox/templates/virtualization/virtualmachine.html:55 #: netbox/templates/virtualization/virtualmachine.html:71 msgid "NAT" msgstr "" -#: netbox/templates/dcim/device.html:245 netbox/templates/dcim/rack.html:67 +#: netbox/templates/dcim/device.html:247 netbox/templates/dcim/rack.html:67 msgid "Power Utilization" msgstr "" -#: netbox/templates/dcim/device.html:249 +#: netbox/templates/dcim/device.html:251 msgid "Input" msgstr "" -#: netbox/templates/dcim/device.html:250 +#: netbox/templates/dcim/device.html:252 msgid "Outlets" msgstr "" -#: netbox/templates/dcim/device.html:251 +#: netbox/templates/dcim/device.html:253 msgid "Allocated" msgstr "" -#: netbox/templates/dcim/device.html:261 netbox/templates/dcim/device.html:263 -#: netbox/templates/dcim/device.html:279 +#: netbox/templates/dcim/device.html:263 netbox/templates/dcim/device.html:265 +#: netbox/templates/dcim/device.html:281 #: netbox/templates/dcim/powerfeed.html:67 msgid "VA" msgstr "" -#: netbox/templates/dcim/device.html:273 +#: netbox/templates/dcim/device.html:275 msgctxt "Leg of a power feed" msgid "Leg" msgstr "" -#: netbox/templates/dcim/device.html:299 +#: netbox/templates/dcim/device.html:301 #: netbox/templates/virtualization/virtualmachine.html:154 msgid "Add a service" msgstr "" From 94c2e7582e1df592e32134b7e115777334984335 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Jul 2024 08:28:12 -0400 Subject: [PATCH 20/42] Closes #16791: Add 200 & 400 Gbps selections for circuit termination port speed --- netbox/circuits/choices.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/circuits/choices.py b/netbox/circuits/choices.py index 5d0065edc7a..6648352a1e6 100644 --- a/netbox/circuits/choices.py +++ b/netbox/circuits/choices.py @@ -69,6 +69,8 @@ class CircuitTerminationPortSpeedChoices(ChoiceSet): (25000000, '25 Gbps'), (40000000, '40 Gbps'), (100000000, '100 Gbps'), + (200000000, '200 Gbps'), + (400000000, '400 Gbps'), (1544, 'T1 (1.544 Mbps)'), (2048, 'E1 (2.048 Mbps)'), ] From 224f157b7562845772916104536dbad88fe3df4c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Jul 2024 08:31:25 -0400 Subject: [PATCH 21/42] Fixes #16807: Fix layout of VLAN edit form when custom fields are present --- netbox/templates/ipam/vlan_edit.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/netbox/templates/ipam/vlan_edit.html b/netbox/templates/ipam/vlan_edit.html index 3ee04980e7e..2db461dcede 100644 --- a/netbox/templates/ipam/vlan_edit.html +++ b/netbox/templates/ipam/vlan_edit.html @@ -53,10 +53,6 @@
{% trans "Assignment" %}
{% endwith %} -
- {% render_field form.comments %} -
- {% if form.custom_fields %}
@@ -65,4 +61,8 @@
{% trans "Custom Fields" %}
{% render_custom_fields form %}
{% endif %} + +
+ {% render_field form.comments %} +
{% endblock %} From a704708caa7214b39ba5b1b8ec70c8f98eb963f7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 2 Jul 2024 10:15:34 -0400 Subject: [PATCH 22/42] Fixes #16679: Avoid overwriting custom JSON fields during bulk edit --- netbox/extras/models/customfields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index d2397fee830..d8f02ec6cd4 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -490,7 +490,7 @@ def to_form_field(self, set_initial=True, enforce_required=True, enforce_visibil # JSON elif self.type == CustomFieldTypeChoices.TYPE_JSON: - field = JSONField(required=required, initial=json.dumps(initial) if initial else '') + field = JSONField(required=required, initial=json.dumps(initial) if initial else None) # Object elif self.type == CustomFieldTypeChoices.TYPE_OBJECT: From 98748d901bbd2f3560e96c96c653f608565ed749 Mon Sep 17 00:00:00 2001 From: RobertH1993 Date: Wed, 3 Jul 2024 11:27:19 +0200 Subject: [PATCH 23/42] Closes #16716, add NAT IP to device view for OOB IP --- netbox/templates/dcim/device.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index e3ac944bc0e..d9da53de285 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -223,6 +223,11 @@
{% trans "Management" %}
{% if object.oob_ip %} {{ object.oob_ip.address.ip }} + {% if object.oob_ip.nat_inside %} + ({% trans "NAT for" %} {{ object.oob_ip.nat_inside.address.ip }}) + {% elif object.oob_ip.nat_outside.exists %} + ({% trans "NAT" %}: {% for nat in object.oob_ip.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) + {% endif %} {% copy_content "oob_ip" %} {% else %} {{ ''|placeholder }} From b18a6b7c599d41e4c1015f2c227beaccc0666a9a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Jul 2024 08:51:30 -0400 Subject: [PATCH 24/42] Fixes #16779: Fix saved filter selection for child object lists (#16789) * Fixes #16779: Fix saved filter selection for child object lists * Omit label_suffix --- netbox/dcim/views.py | 15 +++++++++++++++ netbox/ipam/views.py | 12 ++++++++++++ netbox/netbox/views/generic/bulk_views.py | 2 +- netbox/netbox/views/generic/object_views.py | 3 +++ netbox/templates/inc/table_controls_htmx.html | 14 ++++++++------ netbox/tenancy/views.py | 1 + netbox/virtualization/views.py | 5 +++++ 7 files changed, 45 insertions(+), 7 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 87f351e4d07..b3d8d298e7e 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -31,6 +31,7 @@ GetRelatedModelsMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view ) from virtualization.filtersets import VirtualMachineFilterSet +from virtualization.forms import VirtualMachineFilterForm from virtualization.models import VirtualMachine from virtualization.tables import VirtualMachineTable from . import filtersets, forms, tables @@ -679,6 +680,7 @@ class RackRackReservationsView(generic.ObjectChildrenView): child_model = RackReservation table = tables.RackReservationTable filterset = filtersets.RackReservationFilterSet + filterset_form = forms.RackReservationFilterForm template_name = 'dcim/rack/reservations.html' tab = ViewTab( label=_('Reservations'), @@ -697,6 +699,7 @@ class RackNonRackedView(generic.ObjectChildrenView): child_model = Device table = tables.DeviceTable filterset = filtersets.DeviceFilterSet + filterset_form = forms.DeviceFilterForm template_name = 'dcim/rack/non_racked_devices.html' tab = ViewTab( label=_('Non-Racked Devices'), @@ -1835,6 +1838,7 @@ class DeviceConsolePortsView(DeviceComponentsView): child_model = ConsolePort table = tables.DeviceConsolePortTable filterset = filtersets.ConsolePortFilterSet + filterset_form = forms.ConsolePortFilterForm template_name = 'dcim/device/consoleports.html', tab = ViewTab( label=_('Console Ports'), @@ -1850,6 +1854,7 @@ class DeviceConsoleServerPortsView(DeviceComponentsView): child_model = ConsoleServerPort table = tables.DeviceConsoleServerPortTable filterset = filtersets.ConsoleServerPortFilterSet + filterset_form = forms.ConsoleServerPortFilterForm template_name = 'dcim/device/consoleserverports.html' tab = ViewTab( label=_('Console Server Ports'), @@ -1865,6 +1870,7 @@ class DevicePowerPortsView(DeviceComponentsView): child_model = PowerPort table = tables.DevicePowerPortTable filterset = filtersets.PowerPortFilterSet + filterset_form = forms.PowerPortFilterForm template_name = 'dcim/device/powerports.html' tab = ViewTab( label=_('Power Ports'), @@ -1880,6 +1886,7 @@ class DevicePowerOutletsView(DeviceComponentsView): child_model = PowerOutlet table = tables.DevicePowerOutletTable filterset = filtersets.PowerOutletFilterSet + filterset_form = forms.PowerOutletFilterForm template_name = 'dcim/device/poweroutlets.html' tab = ViewTab( label=_('Power Outlets'), @@ -1895,6 +1902,7 @@ class DeviceInterfacesView(DeviceComponentsView): child_model = Interface table = tables.DeviceInterfaceTable filterset = filtersets.InterfaceFilterSet + filterset_form = forms.InterfaceFilterForm template_name = 'dcim/device/interfaces.html' tab = ViewTab( label=_('Interfaces'), @@ -1916,6 +1924,7 @@ class DeviceFrontPortsView(DeviceComponentsView): child_model = FrontPort table = tables.DeviceFrontPortTable filterset = filtersets.FrontPortFilterSet + filterset_form = forms.FrontPortFilterForm template_name = 'dcim/device/frontports.html' tab = ViewTab( label=_('Front Ports'), @@ -1931,6 +1940,7 @@ class DeviceRearPortsView(DeviceComponentsView): child_model = RearPort table = tables.DeviceRearPortTable filterset = filtersets.RearPortFilterSet + filterset_form = forms.RearPortFilterForm template_name = 'dcim/device/rearports.html' tab = ViewTab( label=_('Rear Ports'), @@ -1946,6 +1956,7 @@ class DeviceModuleBaysView(DeviceComponentsView): child_model = ModuleBay table = tables.DeviceModuleBayTable filterset = filtersets.ModuleBayFilterSet + filterset_form = forms.ModuleBayFilterForm template_name = 'dcim/device/modulebays.html' actions = { **DEFAULT_ACTION_PERMISSIONS, @@ -1965,6 +1976,7 @@ class DeviceDeviceBaysView(DeviceComponentsView): child_model = DeviceBay table = tables.DeviceDeviceBayTable filterset = filtersets.DeviceBayFilterSet + filterset_form = forms.DeviceBayFilterForm template_name = 'dcim/device/devicebays.html' actions = { **DEFAULT_ACTION_PERMISSIONS, @@ -1984,6 +1996,7 @@ class DeviceInventoryView(DeviceComponentsView): child_model = InventoryItem table = tables.DeviceInventoryItemTable filterset = filtersets.InventoryItemFilterSet + filterset_form = forms.InventoryItemFilterForm template_name = 'dcim/device/inventory.html' actions = { **DEFAULT_ACTION_PERMISSIONS, @@ -2062,6 +2075,7 @@ class DeviceVirtualMachinesView(generic.ObjectChildrenView): child_model = VirtualMachine table = VirtualMachineTable filterset = VirtualMachineFilterSet + filterset_form = VirtualMachineFilterForm tab = ViewTab( label=_('Virtual Machines'), badge=lambda obj: VirtualMachine.objects.filter(cluster=obj.cluster, device=obj).count(), @@ -2944,6 +2958,7 @@ class InventoryItemChildrenView(generic.ObjectChildrenView): child_model = InventoryItem table = tables.InventoryItemTable filterset = filtersets.InventoryItemFilterSet + filterset_form = forms.InventoryItemFilterForm tab = ViewTab( label=_('Children'), badge=lambda obj: obj.child_items.count(), diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 12c86c53315..e0087f5d102 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -7,6 +7,7 @@ from circuits.models import Provider from dcim.filtersets import InterfaceFilterSet +from dcim.forms import InterfaceFilterForm from dcim.models import Interface, Site from netbox.views import generic from tenancy.views import ObjectContactsView @@ -14,6 +15,7 @@ from utilities.tables import get_table_ordering from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view from virtualization.filtersets import VMInterfaceFilterSet +from virtualization.forms import VMInterfaceFilterForm from virtualization.models import VMInterface from . import filtersets, forms, tables from .choices import PrefixStatusChoices @@ -206,6 +208,7 @@ class ASNRangeASNsView(generic.ObjectChildrenView): child_model = ASN table = tables.ASNTable filterset = filtersets.ASNFilterSet + filterset_form = forms.ASNFilterForm tab = ViewTab( label=_('ASNs'), badge=lambda x: x.get_child_asns().count(), @@ -337,6 +340,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView): child_model = Prefix table = tables.PrefixTable filterset = filtersets.PrefixFilterSet + filterset_form = forms.PrefixFilterForm template_name = 'ipam/aggregate/prefixes.html' tab = ViewTab( label=_('Prefixes'), @@ -523,6 +527,7 @@ class PrefixPrefixesView(generic.ObjectChildrenView): child_model = Prefix table = tables.PrefixTable filterset = filtersets.PrefixFilterSet + filterset_form = forms.PrefixFilterForm template_name = 'ipam/prefix/prefixes.html' tab = ViewTab( label=_('Child Prefixes'), @@ -558,6 +563,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView): child_model = IPRange table = tables.IPRangeTable filterset = filtersets.IPRangeFilterSet + filterset_form = forms.IPRangeFilterForm template_name = 'ipam/prefix/ip_ranges.html' tab = ViewTab( label=_('Child Ranges'), @@ -584,6 +590,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView): child_model = IPAddress table = tables.IPAddressTable filterset = filtersets.IPAddressFilterSet + filterset_form = forms.IPAddressFilterForm template_name = 'ipam/prefix/ip_addresses.html' tab = ViewTab( label=_('IP Addresses'), @@ -683,6 +690,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView): child_model = IPAddress table = tables.IPAddressTable filterset = filtersets.IPAddressFilterSet + filterset_form = forms.IPRangeFilterForm template_name = 'ipam/iprange/ip_addresses.html' tab = ViewTab( label=_('IP Addresses'), @@ -885,6 +893,7 @@ class IPAddressRelatedIPsView(generic.ObjectChildrenView): child_model = IPAddress table = tables.IPAddressTable filterset = filtersets.IPAddressFilterSet + filterset_form = forms.IPAddressFilterForm tab = ViewTab( label=_('Related IPs'), badge=lambda x: x.get_related_ips().count(), @@ -957,6 +966,7 @@ class VLANGroupVLANsView(generic.ObjectChildrenView): child_model = VLAN table = tables.VLANTable filterset = filtersets.VLANFilterSet + filterset_form = forms.VLANFilterForm tab = ViewTab( label=_('VLANs'), badge=lambda x: x.get_child_vlans().count(), @@ -1112,6 +1122,7 @@ class VLANInterfacesView(generic.ObjectChildrenView): child_model = Interface table = tables.VLANDevicesTable filterset = InterfaceFilterSet + filterset_form = InterfaceFilterForm tab = ViewTab( label=_('Device Interfaces'), badge=lambda x: x.get_interfaces().count(), @@ -1129,6 +1140,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView): child_model = VMInterface table = tables.VLANVirtualMachinesTable filterset = VMInterfaceFilterSet + filterset_form = VMInterfaceFilterForm tab = ViewTab( label=_('VM Interfaces'), badge=lambda x: x.get_vminterfaces().count(), diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 87e352710ca..71ce411ba7b 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -176,7 +176,7 @@ def get(self, request): 'model': model, 'table': table, 'actions': actions, - 'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None, + 'filter_form': self.filterset_form(request.GET) if self.filterset_form else None, 'prerequisite_model': get_prerequisite_model(self.queryset), **self.get_extra_context(request), } diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 243ae2547d8..cad7facd35b 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -87,12 +87,14 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin): child_model: The model class which represents the child objects table: The django-tables2 Table class used to render the child objects list filterset: A django-filter FilterSet that is applied to the queryset + filterset_form: The form class used to render filter options actions: A mapping of supported actions to their required permissions. When adding custom actions, bulk action names must be prefixed with `bulk_`. (See ActionsMixin.) """ child_model = None table = None filterset = None + filterset_form = None template_name = 'generic/object_children.html' def get_children(self, request, parent): @@ -152,6 +154,7 @@ def get(self, request, *args, **kwargs): 'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html', 'table': table, 'table_config': f'{table.name}_config', + 'filter_form': self.filterset_form(request.GET) if self.filterset_form else None, 'actions': actions, 'tab': self.tab, 'return_url': request.get_full_path(), diff --git a/netbox/templates/inc/table_controls_htmx.html b/netbox/templates/inc/table_controls_htmx.html index ec913d32af0..a07b319efe2 100644 --- a/netbox/templates/inc/table_controls_htmx.html +++ b/netbox/templates/inc/table_controls_htmx.html @@ -13,14 +13,16 @@
-
-
-
- + {% if filter_form %} +
+
+
+ +
+ {{ filter_form.filter_id }}
- {{ filter_form.filter_id }}
-
+ {% endif %}
{% if request.user.is_authenticated and table_modal %} diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index 06fbcc57581..c1b3c1b267a 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -13,6 +13,7 @@ class ObjectContactsView(generic.ObjectChildrenView): child_model = ContactAssignment table = tables.ContactAssignmentTable filterset = filtersets.ContactAssignmentFilterSet + filterset_form = forms.ContactAssignmentFilterForm template_name = 'tenancy/object_contacts.html' tab = ViewTab( label=_('Contacts'), diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index c143fff85bc..1030fed04a4 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -10,6 +10,7 @@ from jinja2.exceptions import TemplateError from dcim.filtersets import DeviceFilterSet +from dcim.forms import DeviceFilterForm from dcim.models import Device from dcim.tables import DeviceTable from extras.views import ObjectConfigContextView @@ -173,6 +174,7 @@ class ClusterVirtualMachinesView(generic.ObjectChildrenView): child_model = VirtualMachine table = tables.VirtualMachineTable filterset = filtersets.VirtualMachineFilterSet + filterset_form = forms.VirtualMachineFilterForm tab = ViewTab( label=_('Virtual Machines'), badge=lambda obj: obj.virtual_machines.count(), @@ -190,6 +192,7 @@ class ClusterDevicesView(generic.ObjectChildrenView): child_model = Device table = DeviceTable filterset = DeviceFilterSet + filterset_form = DeviceFilterForm template_name = 'virtualization/cluster/devices.html' actions = { 'add': {'add'}, @@ -350,6 +353,7 @@ class VirtualMachineInterfacesView(generic.ObjectChildrenView): child_model = VMInterface table = tables.VirtualMachineVMInterfaceTable filterset = filtersets.VMInterfaceFilterSet + filterset_form = forms.VMInterfaceFilterForm template_name = 'virtualization/virtualmachine/interfaces.html' actions = { **DEFAULT_ACTION_PERMISSIONS, @@ -375,6 +379,7 @@ class VirtualMachineVirtualDisksView(generic.ObjectChildrenView): child_model = VirtualDisk table = tables.VirtualMachineVirtualDiskTable filterset = filtersets.VirtualDiskFilterSet + filterset_form = forms.VirtualDiskFilterForm template_name = 'virtualization/virtualmachine/virtual_disks.html' tab = ViewTab( label=_('Virtual Disks'), From cf38c7724e81aaf90c47e7b3b5ba64b538f4770e Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Jul 2024 05:02:06 +0000 Subject: [PATCH 25/42] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 136 ++++++++++--------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index 21d35116d61..b251563e234 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-07-02 05:02+0000\n" +"POT-Creation-Date: 2024-07-04 05:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -326,7 +326,7 @@ msgstr "" #: netbox/circuits/tables/providers.py:33 netbox/dcim/forms/bulk_edit.py:127 #: netbox/dcim/forms/filtersets.py:189 netbox/dcim/forms/model_forms.py:122 #: netbox/dcim/tables/sites.py:94 netbox/ipam/models/asns.py:126 -#: netbox/ipam/tables/asn.py:27 netbox/ipam/views.py:210 +#: netbox/ipam/tables/asn.py:27 netbox/ipam/views.py:213 #: netbox/netbox/navigation/menu.py:159 netbox/netbox/navigation/menu.py:162 #: netbox/templates/circuits/provider.html:23 msgid "ASNs" @@ -890,7 +890,7 @@ msgstr "" #: netbox/dcim/forms/filtersets.py:654 netbox/dcim/forms/filtersets.py:1022 #: netbox/netbox/navigation/menu.py:44 netbox/netbox/navigation/menu.py:46 #: netbox/tenancy/forms/filtersets.py:42 netbox/tenancy/tables/columns.py:70 -#: netbox/tenancy/tables/contacts.py:25 netbox/tenancy/views.py:18 +#: netbox/tenancy/tables/contacts.py:25 netbox/tenancy/views.py:19 #: netbox/virtualization/forms/filtersets.py:37 #: netbox/virtualization/forms/filtersets.py:48 #: netbox/virtualization/forms/filtersets.py:106 @@ -2132,7 +2132,7 @@ msgstr "" msgid "Reserved" msgstr "" -#: netbox/dcim/choices.py:101 netbox/templates/dcim/device.html:254 +#: netbox/dcim/choices.py:101 netbox/templates/dcim/device.html:259 msgid "Available" msgstr "" @@ -2189,14 +2189,14 @@ msgstr "" msgid "Child" msgstr "" -#: netbox/dcim/choices.py:155 netbox/templates/dcim/device.html:334 +#: netbox/dcim/choices.py:155 netbox/templates/dcim/device.html:339 #: netbox/templates/dcim/rack.html:175 #: netbox/templates/dcim/rack_elevation_list.html:20 #: netbox/templates/dcim/rackreservation.html:76 msgid "Front" msgstr "" -#: netbox/dcim/choices.py:156 netbox/templates/dcim/device.html:340 +#: netbox/dcim/choices.py:156 netbox/templates/dcim/device.html:345 #: netbox/templates/dcim/rack.html:181 #: netbox/templates/dcim/rack_elevation_list.html:21 #: netbox/templates/dcim/rackreservation.html:82 @@ -2420,7 +2420,7 @@ msgstr "" msgid "Feet" msgstr "" -#: netbox/dcim/choices.py:1497 netbox/templates/dcim/device.html:322 +#: netbox/dcim/choices.py:1497 netbox/templates/dcim/device.html:327 #: netbox/templates/dcim/rack.html:152 msgid "Kilograms" msgstr "" @@ -3133,7 +3133,7 @@ msgstr "" #: netbox/extras/forms/bulk_edit.py:158 netbox/extras/forms/bulk_edit.py:278 #: netbox/extras/forms/filtersets.py:61 netbox/extras/forms/filtersets.py:134 #: netbox/extras/forms/filtersets.py:221 netbox/ipam/forms/bulk_edit.py:188 -#: netbox/templates/dcim/device.html:319 +#: netbox/templates/dcim/device.html:324 #: netbox/templates/dcim/devicetype.html:49 #: netbox/templates/dcim/moduletype.html:30 #: netbox/templates/extras/configcontext.html:17 @@ -3749,7 +3749,7 @@ msgstr "" #: netbox/dcim/tables/devices.py:199 netbox/extras/filtersets.py:548 #: netbox/extras/forms/filtersets.py:331 netbox/ipam/forms/bulk_edit.py:479 #: netbox/ipam/forms/filtersets.py:415 netbox/ipam/forms/filtersets.py:459 -#: netbox/ipam/forms/model_forms.py:627 netbox/templates/dcim/device.html:234 +#: netbox/ipam/forms/model_forms.py:627 netbox/templates/dcim/device.html:239 #: netbox/templates/virtualization/cluster.html:10 #: netbox/templates/virtualization/virtualmachine.html:88 #: netbox/templates/virtualization/virtualmachine.html:97 @@ -4253,7 +4253,7 @@ msgstr "" msgid "Outer Dimensions" msgstr "" -#: netbox/dcim/forms/model_forms.py:233 netbox/templates/dcim/device.html:310 +#: netbox/dcim/forms/model_forms.py:233 netbox/templates/dcim/device.html:315 #: netbox/templates/dcim/rack.html:73 msgid "Dimensions" msgstr "" @@ -5987,7 +5987,7 @@ msgstr "" #: netbox/netbox/navigation/menu.py:60 netbox/netbox/navigation/menu.py:62 #: netbox/virtualization/forms/model_forms.py:122 #: netbox/virtualization/tables/clusters.py:83 -#: netbox/virtualization/views.py:202 +#: netbox/virtualization/views.py:205 msgid "Devices" msgstr "" @@ -6067,8 +6067,8 @@ msgid "Power outlets" msgstr "" #: netbox/dcim/tables/devices.py:243 netbox/dcim/tables/devices.py:1046 -#: netbox/dcim/tables/devicetypes.py:125 netbox/dcim/views.py:985 -#: netbox/dcim/views.py:1224 netbox/dcim/views.py:1900 +#: netbox/dcim/tables/devicetypes.py:125 netbox/dcim/views.py:988 +#: netbox/dcim/views.py:1227 netbox/dcim/views.py:1908 #: netbox/netbox/navigation/menu.py:81 netbox/netbox/navigation/menu.py:237 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -6080,7 +6080,7 @@ msgstr "" #: netbox/templates/virtualization/virtualmachine/base.html:27 #: netbox/templates/virtualization/virtualmachine_list.html:14 #: netbox/virtualization/tables/virtualmachines.py:100 -#: netbox/virtualization/views.py:359 netbox/wireless/tables/wirelesslan.py:55 +#: netbox/virtualization/views.py:363 netbox/wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "" @@ -6106,8 +6106,8 @@ msgid "Module Bay" msgstr "" #: netbox/dcim/tables/devices.py:310 netbox/dcim/tables/devicetypes.py:48 -#: netbox/dcim/tables/devicetypes.py:140 netbox/dcim/views.py:1060 -#: netbox/dcim/views.py:1993 netbox/netbox/navigation/menu.py:90 +#: netbox/dcim/tables/devicetypes.py:140 netbox/dcim/views.py:1063 +#: netbox/dcim/views.py:2006 netbox/netbox/navigation/menu.py:90 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -6137,8 +6137,8 @@ msgid "Allocated draw (W)" msgstr "" #: netbox/dcim/tables/devices.py:546 netbox/ipam/forms/model_forms.py:747 -#: netbox/ipam/tables/fhrp.py:28 netbox/ipam/views.py:589 -#: netbox/ipam/views.py:688 netbox/netbox/navigation/menu.py:145 +#: netbox/ipam/tables/fhrp.py:28 netbox/ipam/views.py:596 +#: netbox/ipam/views.py:696 netbox/netbox/navigation/menu.py:145 #: netbox/netbox/navigation/menu.py:147 #: netbox/templates/dcim/interface.html:339 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 @@ -6231,8 +6231,8 @@ msgstr "" msgid "Instances" msgstr "" -#: netbox/dcim/tables/devicetypes.py:113 netbox/dcim/views.py:925 -#: netbox/dcim/views.py:1164 netbox/dcim/views.py:1840 +#: netbox/dcim/tables/devicetypes.py:113 netbox/dcim/views.py:928 +#: netbox/dcim/views.py:1167 netbox/dcim/views.py:1844 #: netbox/netbox/navigation/menu.py:84 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -6242,8 +6242,8 @@ msgstr "" msgid "Console Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:116 netbox/dcim/views.py:940 -#: netbox/dcim/views.py:1179 netbox/dcim/views.py:1855 +#: netbox/dcim/tables/devicetypes.py:116 netbox/dcim/views.py:943 +#: netbox/dcim/views.py:1182 netbox/dcim/views.py:1860 #: netbox/netbox/navigation/menu.py:85 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -6253,8 +6253,8 @@ msgstr "" msgid "Console Server Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:119 netbox/dcim/views.py:955 -#: netbox/dcim/views.py:1194 netbox/dcim/views.py:1870 +#: netbox/dcim/tables/devicetypes.py:119 netbox/dcim/views.py:958 +#: netbox/dcim/views.py:1197 netbox/dcim/views.py:1876 #: netbox/netbox/navigation/menu.py:86 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -6264,8 +6264,8 @@ msgstr "" msgid "Power Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:122 netbox/dcim/views.py:970 -#: netbox/dcim/views.py:1209 netbox/dcim/views.py:1885 +#: netbox/dcim/tables/devicetypes.py:122 netbox/dcim/views.py:973 +#: netbox/dcim/views.py:1212 netbox/dcim/views.py:1892 #: netbox/netbox/navigation/menu.py:87 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -6275,8 +6275,8 @@ msgstr "" msgid "Power Outlets" msgstr "" -#: netbox/dcim/tables/devicetypes.py:128 netbox/dcim/views.py:1000 -#: netbox/dcim/views.py:1239 netbox/dcim/views.py:1921 +#: netbox/dcim/tables/devicetypes.py:128 netbox/dcim/views.py:1003 +#: netbox/dcim/views.py:1242 netbox/dcim/views.py:1930 #: netbox/netbox/navigation/menu.py:82 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -6285,8 +6285,8 @@ msgstr "" msgid "Front Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:131 netbox/dcim/views.py:1015 -#: netbox/dcim/views.py:1254 netbox/dcim/views.py:1936 +#: netbox/dcim/tables/devicetypes.py:131 netbox/dcim/views.py:1018 +#: netbox/dcim/views.py:1257 netbox/dcim/views.py:1946 #: netbox/netbox/navigation/menu.py:83 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -6296,16 +6296,16 @@ msgstr "" msgid "Rear Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:134 netbox/dcim/views.py:1045 -#: netbox/dcim/views.py:1974 netbox/netbox/navigation/menu.py:89 +#: netbox/dcim/tables/devicetypes.py:134 netbox/dcim/views.py:1048 +#: netbox/dcim/views.py:1986 netbox/netbox/navigation/menu.py:89 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "" -#: netbox/dcim/tables/devicetypes.py:137 netbox/dcim/views.py:1030 -#: netbox/dcim/views.py:1955 netbox/netbox/navigation/menu.py:88 +#: netbox/dcim/tables/devicetypes.py:137 netbox/dcim/views.py:1033 +#: netbox/dcim/views.py:1966 netbox/netbox/navigation/menu.py:88 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 #: netbox/templates/dcim/devicetype/base.html:43 @@ -6330,7 +6330,7 @@ msgstr "" msgid "Racks" msgstr "" -#: netbox/dcim/tables/racks.py:73 netbox/templates/dcim/device.html:313 +#: netbox/dcim/tables/racks.py:73 netbox/templates/dcim/device.html:318 #: netbox/templates/dcim/rack.html:90 msgid "Height" msgstr "" @@ -6363,38 +6363,38 @@ msgstr "" msgid "Test case must set peer_termination_type" msgstr "" -#: netbox/dcim/views.py:139 +#: netbox/dcim/views.py:140 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "" -#: netbox/dcim/views.py:684 netbox/netbox/navigation/menu.py:28 +#: netbox/dcim/views.py:686 netbox/netbox/navigation/menu.py:28 msgid "Reservations" msgstr "" -#: netbox/dcim/views.py:702 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:705 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "" -#: netbox/dcim/views.py:2006 netbox/extras/forms/model_forms.py:453 +#: netbox/dcim/views.py:2019 netbox/extras/forms/model_forms.py:453 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:225 -#: netbox/virtualization/views.py:399 +#: netbox/virtualization/views.py:404 msgid "Config Context" msgstr "" -#: netbox/dcim/views.py:2016 netbox/virtualization/views.py:409 +#: netbox/dcim/views.py:2029 netbox/virtualization/views.py:414 msgid "Render Config" msgstr "" -#: netbox/dcim/views.py:2066 netbox/extras/tables/tables.py:441 +#: netbox/dcim/views.py:2080 netbox/extras/tables/tables.py:441 #: netbox/netbox/navigation/menu.py:234 netbox/netbox/navigation/menu.py:236 -#: netbox/virtualization/views.py:177 +#: netbox/virtualization/views.py:179 msgid "Virtual Machines" msgstr "" -#: netbox/dcim/views.py:2948 netbox/ipam/tables/ip.py:233 +#: netbox/dcim/views.py:2963 netbox/ipam/tables/ip.py:233 msgid "Children" msgstr "" @@ -9418,7 +9418,7 @@ msgid "The primary function of this VLAN" msgstr "" #: netbox/ipam/models/vlans.py:215 netbox/ipam/tables/ip.py:175 -#: netbox/ipam/tables/vlans.py:78 netbox/ipam/views.py:961 +#: netbox/ipam/tables/vlans.py:78 netbox/ipam/views.py:971 #: netbox/netbox/navigation/menu.py:180 netbox/netbox/navigation/menu.py:182 msgid "VLANs" msgstr "" @@ -9490,7 +9490,7 @@ msgid "Added" msgstr "" #: netbox/ipam/tables/ip.py:127 netbox/ipam/tables/ip.py:165 -#: netbox/ipam/tables/vlans.py:138 netbox/ipam/views.py:342 +#: netbox/ipam/tables/vlans.py:138 netbox/ipam/views.py:346 #: netbox/netbox/navigation/menu.py:152 netbox/netbox/navigation/menu.py:154 #: netbox/templates/ipam/vlan.html:84 msgid "Prefixes" @@ -9498,7 +9498,7 @@ msgstr "" #: netbox/ipam/tables/ip.py:130 netbox/ipam/tables/ip.py:267 #: netbox/ipam/tables/ip.py:320 netbox/ipam/tables/vlans.py:82 -#: netbox/templates/dcim/device.html:255 +#: netbox/templates/dcim/device.html:260 #: netbox/templates/ipam/aggregate.html:24 #: netbox/templates/ipam/iprange.html:29 netbox/templates/ipam/prefix.html:106 msgid "Utilization" @@ -9591,23 +9591,23 @@ msgid "" "are allowed in DNS names" msgstr "" -#: netbox/ipam/views.py:528 +#: netbox/ipam/views.py:533 msgid "Child Prefixes" msgstr "" -#: netbox/ipam/views.py:563 +#: netbox/ipam/views.py:569 msgid "Child Ranges" msgstr "" -#: netbox/ipam/views.py:889 +#: netbox/ipam/views.py:898 msgid "Related IPs" msgstr "" -#: netbox/ipam/views.py:1116 +#: netbox/ipam/views.py:1127 msgid "Device Interfaces" msgstr "" -#: netbox/ipam/views.py:1133 +#: netbox/ipam/views.py:1145 msgid "VM Interfaces" msgstr "" @@ -10100,7 +10100,7 @@ msgstr "" msgid "Service Templates" msgstr "" -#: netbox/netbox/navigation/menu.py:191 netbox/templates/dcim/device.html:297 +#: netbox/netbox/navigation/menu.py:191 netbox/templates/dcim/device.html:302 #: netbox/templates/ipam/ipaddress.html:118 #: netbox/templates/virtualization/virtualmachine.html:150 msgid "Services" @@ -10166,7 +10166,7 @@ msgstr "" #: netbox/templates/virtualization/virtualmachine/base.html:32 #: netbox/templates/virtualization/virtualmachine_list.html:21 #: netbox/virtualization/tables/virtualmachines.py:103 -#: netbox/virtualization/views.py:380 +#: netbox/virtualization/views.py:385 msgid "Virtual Disks" msgstr "" @@ -10228,7 +10228,7 @@ msgstr "" #: netbox/templates/htmx/form.html:19 netbox/templates/inc/filter_list.html:30 #: netbox/templates/inc/panels/custom_fields.html:7 #: netbox/templates/ipam/ipaddress_bulk_add.html:35 -#: netbox/templates/ipam/vlan_edit.html:63 +#: netbox/templates/ipam/vlan_edit.html:59 msgid "Custom Fields" msgstr "" @@ -10548,7 +10548,7 @@ msgstr "" msgid "Journal" msgstr "" -#: netbox/netbox/views/generic/object_views.py:106 +#: netbox/netbox/views/generic/object_views.py:108 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "" @@ -11111,8 +11111,8 @@ msgstr "" #: netbox/templates/core/rq_worker_list.html:45 #: netbox/templates/extras/script_result.html:49 #: netbox/templates/extras/script_result.html:51 -#: netbox/templates/inc/table_controls_htmx.html:28 -#: netbox/templates/inc/table_controls_htmx.html:31 +#: netbox/templates/inc/table_controls_htmx.html:30 +#: netbox/templates/inc/table_controls_htmx.html:33 msgid "Configure Table" msgstr "" @@ -11381,45 +11381,47 @@ msgid "Management" msgstr "" #: netbox/templates/dcim/device.html:195 netbox/templates/dcim/device.html:211 +#: netbox/templates/dcim/device.html:227 #: netbox/templates/virtualization/virtualmachine.html:53 #: netbox/templates/virtualization/virtualmachine.html:69 msgid "NAT for" msgstr "" #: netbox/templates/dcim/device.html:197 netbox/templates/dcim/device.html:213 +#: netbox/templates/dcim/device.html:229 #: netbox/templates/virtualization/virtualmachine.html:55 #: netbox/templates/virtualization/virtualmachine.html:71 msgid "NAT" msgstr "" -#: netbox/templates/dcim/device.html:247 netbox/templates/dcim/rack.html:67 +#: netbox/templates/dcim/device.html:252 netbox/templates/dcim/rack.html:67 msgid "Power Utilization" msgstr "" -#: netbox/templates/dcim/device.html:251 +#: netbox/templates/dcim/device.html:256 msgid "Input" msgstr "" -#: netbox/templates/dcim/device.html:252 +#: netbox/templates/dcim/device.html:257 msgid "Outlets" msgstr "" -#: netbox/templates/dcim/device.html:253 +#: netbox/templates/dcim/device.html:258 msgid "Allocated" msgstr "" -#: netbox/templates/dcim/device.html:263 netbox/templates/dcim/device.html:265 -#: netbox/templates/dcim/device.html:281 +#: netbox/templates/dcim/device.html:268 netbox/templates/dcim/device.html:270 +#: netbox/templates/dcim/device.html:286 #: netbox/templates/dcim/powerfeed.html:67 msgid "VA" msgstr "" -#: netbox/templates/dcim/device.html:275 +#: netbox/templates/dcim/device.html:280 msgctxt "Leg of a power feed" msgid "Leg" msgstr "" -#: netbox/templates/dcim/device.html:301 +#: netbox/templates/dcim/device.html:306 #: netbox/templates/virtualization/virtualmachine.html:154 msgid "Add a service" msgstr "" @@ -12740,7 +12742,7 @@ msgstr "" msgid "Quick search" msgstr "" -#: netbox/templates/inc/table_controls_htmx.html:19 +#: netbox/templates/inc/table_controls_htmx.html:20 msgid "Saved filter" msgstr "" From 8026f79cbba9678cc16cefb307f9e04782848c46 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Jul 2024 08:23:05 -0400 Subject: [PATCH 26/42] Fixes #16813: Fix ordering of bookmarks in dashboard widget when filtering by object type --- netbox/extras/dashboard/widgets.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py index c4710468b5e..edf6df2e448 100644 --- a/netbox/extras/dashboard/widgets.py +++ b/netbox/extras/dashboard/widgets.py @@ -381,17 +381,17 @@ def render(self, request): if request.user.is_anonymous: bookmarks = list() else: - user_bookmarks = Bookmark.objects.filter(user=request.user) - if self.config['order_by'] == BookmarkOrderingChoices.ORDERING_ALPHABETICAL_AZ: - bookmarks = sorted(user_bookmarks, key=lambda bookmark: bookmark.__str__().lower()) - elif self.config['order_by'] == BookmarkOrderingChoices.ORDERING_ALPHABETICAL_ZA: - bookmarks = sorted(user_bookmarks, key=lambda bookmark: bookmark.__str__().lower(), reverse=True) - else: - bookmarks = user_bookmarks.order_by(self.config['order_by']) + bookmarks = Bookmark.objects.filter(user=request.user) if object_types := self.config.get('object_types'): models = get_models_from_content_types(object_types) content_types = ObjectType.objects.get_for_models(*models).values() bookmarks = bookmarks.filter(object_type__in=content_types) + if self.config['order_by'] == BookmarkOrderingChoices.ORDERING_ALPHABETICAL_AZ: + bookmarks = sorted(bookmarks, key=lambda bookmark: bookmark.__str__().lower()) + elif self.config['order_by'] == BookmarkOrderingChoices.ORDERING_ALPHABETICAL_ZA: + bookmarks = sorted(bookmarks, key=lambda bookmark: bookmark.__str__().lower(), reverse=True) + else: + bookmarks = bookmarks.order_by(self.config['order_by']) if max_items := self.config.get('max_items'): bookmarks = bookmarks[:max_items] From e9dd5aa17b0e1e1b312204594f16ef2313bd741b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Jul 2024 08:50:22 -0400 Subject: [PATCH 27/42] Fixes #16806: Fix redirect URL when creating contact assignments with "add another" button --- netbox/tenancy/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index c1b3c1b267a..12bf4baab0a 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -365,7 +365,7 @@ def alter_object(self, instance, request, args, kwargs): def get_extra_addanother_params(self, request): return { - 'content_type': request.GET.get('content_type'), + 'object_type': request.GET.get('object_type'), 'object_id': request.GET.get('object_id'), } From a5185799161ca64822f44ac93dc77a6b34077f7a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Jul 2024 09:14:07 -0400 Subject: [PATCH 28/42] Fixes #16796: Allow assignment of VM with no site to a cluster with a site --- netbox/virtualization/models/virtualmachines.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py index 2ca1599bfe4..9deebe3ef0b 100644 --- a/netbox/virtualization/models/virtualmachines.py +++ b/netbox/virtualization/models/virtualmachines.py @@ -179,8 +179,8 @@ def clean(self): 'cluster': _('A virtual machine must be assigned to a site and/or cluster.') }) - # Validate site for cluster & device - if self.cluster and self.cluster.site is not None and self.cluster.site != self.site: + # Validate site for cluster & VM + if self.cluster and self.site and self.cluster.site and self.cluster.site != self.site: raise ValidationError({ 'cluster': _( 'The selected cluster ({cluster}) is not assigned to this site ({site}).' From 7a88810a23bac96e42988c73fc4965b9fdf86450 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Jul 2024 09:32:01 -0400 Subject: [PATCH 29/42] Fixes #16780: IKE proposal created via REST API should not require authentication_algorithm --- netbox/vpn/api/serializers_/crypto.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/vpn/api/serializers_/crypto.py b/netbox/vpn/api/serializers_/crypto.py index 3ee30b754eb..d7a2777c6a9 100644 --- a/netbox/vpn/api/serializers_/crypto.py +++ b/netbox/vpn/api/serializers_/crypto.py @@ -25,7 +25,8 @@ class IKEProposalSerializer(NetBoxModelSerializer): choices=EncryptionAlgorithmChoices ) authentication_algorithm = ChoiceField( - choices=AuthenticationAlgorithmChoices + choices=AuthenticationAlgorithmChoices, + required=False ) group = ChoiceField( choices=DHGroupChoices From 9ab7960a668aceb6d0ac96cf845ccc50c6692424 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Jul 2024 10:00:50 -0400 Subject: [PATCH 30/42] Changelog for #16679, #16716, #16779, #16780, #16791, #16796, #16806, #16807, #16813 --- docs/release-notes/version-4.0.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/release-notes/version-4.0.md b/docs/release-notes/version-4.0.md index 028381f683c..9e57e716211 100644 --- a/docs/release-notes/version-4.0.md +++ b/docs/release-notes/version-4.0.md @@ -5,18 +5,27 @@ ### Enhancements * [#16424](https://github.com/netbox-community/netbox/issues/16424) - Enable filtering of devices by cluster and cluster group +* [#16716](https://github.com/netbox-community/netbox/issues/16716) - Display NAT address (if any) for OOB IP address under device view * [#16725](https://github.com/netbox-community/netbox/issues/16725) - Always position the admin section last in the navigation menu +* [#16791](https://github.com/netbox-community/netbox/issues/16791) - Add 200 & 400 Gbps selections for circuit termination port speed ### Bug Fixes * [#16523](https://github.com/netbox-community/netbox/issues/16523) - Restore highlighting of current device in virtual chassis members panel * [#16654](https://github.com/netbox-community/netbox/issues/16654) - Fix parent item assignment for inventory item bulk import * [#16657](https://github.com/netbox-community/netbox/issues/16657) - Fix translation of object types in global search +* [#16679](https://github.com/netbox-community/netbox/issues/16679) - Avoid overwriting custom JSON fields during bulk edit * [#16689](https://github.com/netbox-community/netbox/issues/16689) - System configuration view should reflect static parameters when no config revisions exist * [#16714](https://github.com/netbox-community/netbox/issues/16714) - Fix cloning of device types with 0U height * [#16723](https://github.com/netbox-community/netbox/issues/16723) - Fix escaping of path to virtual environment in `upgrade.sh` * [#16735](https://github.com/netbox-community/netbox/issues/16735) - Object list "results" tab should show a count of zero when empty * [#16747](https://github.com/netbox-community/netbox/issues/16747) - Avoid clearing entire search cache when manually reindexing specific apps/models +* [#16779](https://github.com/netbox-community/netbox/issues/16779) - Fix saved filter selection for child object lists +* [#16780](https://github.com/netbox-community/netbox/issues/16780) - IKE proposal created via REST API should not require authentication_algorithm +* [#16796](https://github.com/netbox-community/netbox/issues/16796) - Allow assignment of VM with no site to a cluster with a site +* [#16806](https://github.com/netbox-community/netbox/issues/16806) - Fix redirect URL when creating contact assignments with "add another" button +* [#16807](https://github.com/netbox-community/netbox/issues/16807) - Fix layout of VLAN edit form when custom fields are present +* [#16813](https://github.com/netbox-community/netbox/issues/16813) - Fix AttributeError exception when filtering bookmarks in dashboard widget by object type --- From e02796a64ea4cd0345a755e5f7829d5326d17579 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Jul 2024 10:28:52 -0400 Subject: [PATCH 31/42] Fixes #16721: Fix errant API request after deselecting a rack in device edit form --- netbox/project-static/dist/netbox.js | 2 +- netbox/project-static/dist/netbox.js.map | 2 +- .../src/select/classes/dynamicTomSelect.ts | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 27db4718bed..618fb33d15e 100644 --- a/netbox/project-static/dist/netbox.js +++ b/netbox/project-static/dist/netbox.js @@ -1,6 +1,6 @@ (()=>{var uh=Object.create;var _a=Object.defineProperty,dh=Object.defineProperties,fh=Object.getOwnPropertyDescriptor,hh=Object.getOwnPropertyDescriptors,ph=Object.getOwnPropertyNames,cu=Object.getOwnPropertySymbols,mh=Object.getPrototypeOf,uu=Object.prototype.hasOwnProperty,gh=Object.prototype.propertyIsEnumerable;var Gl=(ii,ti,ei)=>ti in ii?_a(ii,ti,{enumerable:!0,configurable:!0,writable:!0,value:ei}):ii[ti]=ei,Ui=(ii,ti)=>{for(var ei in ti||(ti={}))uu.call(ti,ei)&&Gl(ii,ei,ti[ei]);if(cu)for(var ei of cu(ti))gh.call(ti,ei)&&Gl(ii,ei,ti[ei]);return ii},Fn=(ii,ti)=>dh(ii,hh(ti)),du=ii=>_a(ii,"__esModule",{value:!0});var Ga=(ii,ti)=>()=>(ti||ii((ti={exports:{}}).exports,ti),ti.exports),fu=(ii,ti)=>{du(ii);for(var ei in ti)_a(ii,ei,{get:ti[ei],enumerable:!0})},vh=(ii,ti,ei)=>{if(ti&&typeof ti=="object"||typeof ti=="function")for(let ni of ph(ti))!uu.call(ii,ni)&&ni!=="default"&&_a(ii,ni,{get:()=>ti[ni],enumerable:!(ei=fh(ti,ni))||ei.enumerable});return ii},zo=ii=>vh(du(_a(ii!=null?uh(mh(ii)):{},"default",ii&&ii.__esModule&&"default"in ii?{get:()=>ii.default,enumerable:!0}:{value:ii,enumerable:!0})),ii);var Pn=(ii,ti,ei)=>(Gl(ii,typeof ti!="symbol"?ti+"":ti,ei),ei);var $s=(ii,ti,ei)=>new Promise((ni,ri)=>{var si=di=>{try{li(ei.next(di))}catch(yi){ri(yi)}},ai=di=>{try{li(ei.throw(di))}catch(yi){ri(yi)}},li=di=>di.done?ni(di.value):Promise.resolve(di.value).then(si,ai);li((ei=ei.apply(ii,ti)).next())});var Rd=Ga((exports,module)=>{(function(ii,ti){typeof define=="function"&&define.amd?define([],ti):typeof module=="object"&&module.exports?module.exports=ti():ii.htmx=ii.htmx||ti()})(typeof self!="undefined"?self:exports,function(){return function(){"use strict";var Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(ii,ti){var ei=dr(ii,ti||"post");return ei.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags:!0,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:!1,scrollBehavior:"smooth",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get"],selfRequestsOnly:!1,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(ii){return new EventSource(ii,{withCredentials:!0})},createWebSocket:function(ii){var ti=new WebSocket(ii,[]);return ti.binaryType=Q.config.wsBinaryType,ti},version:"1.9.12"},r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R},w=["get","post","put","delete","patch"],i=w.map(function(ii){return"[hx-"+ii+"], [data-hx-"+ii+"]"}).join(", "),S=e("head"),q=e("title"),H=e("svg",!0);function e(ii,ti){return new RegExp("<"+ii+"(\\s[^>]*>|>)([\\s\\S]*?)<\\/"+ii+">",ti?"gim":"im")}function d(ii){if(ii==null)return;let ti=NaN;return ii.slice(-2)=="ms"?ti=parseFloat(ii.slice(0,-2)):ii.slice(-1)=="s"?ti=parseFloat(ii.slice(0,-1))*1e3:ii.slice(-1)=="m"?ti=parseFloat(ii.slice(0,-1))*1e3*60:ti=parseFloat(ii),isNaN(ti)?void 0:ti}function ee(ii,ti){return ii.getAttribute&&ii.getAttribute(ti)}function o(ii,ti){return ii.hasAttribute&&(ii.hasAttribute(ti)||ii.hasAttribute("data-"+ti))}function te(ii,ti){return ee(ii,ti)||ee(ii,"data-"+ti)}function u(ii){return ii.parentElement}function re(){return document}function c(ii,ti){for(;ii&&!ti(ii);)ii=u(ii);return ii||null}function L(ii,ti,ei){var ni=te(ti,ei),ri=te(ti,"hx-disinherit");return ii!==ti&&ri&&(ri==="*"||ri.split(" ").indexOf(ei)>=0)?"unset":ni}function ne(ii,ti){var ei=null;if(c(ii,function(ni){return ei=L(ii,ni,ti)}),ei!=="unset")return ei}function h(ii,ti){var ei=ii.matches||ii.matchesSelector||ii.msMatchesSelector||ii.mozMatchesSelector||ii.webkitMatchesSelector||ii.oMatchesSelector;return ei&&ei.call(ii,ti)}function A(ii){var ti=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,ei=ti.exec(ii);return ei?ei[1].toLowerCase():""}function s(ii,ti){for(var ei=new DOMParser,ni=ei.parseFromString(ii,"text/html"),ri=ni.body;ti>0;)ti--,ri=ri.firstChild;return ri==null&&(ri=re().createDocumentFragment()),ri}function N(ii){return/",0),si=ri.querySelector("template").content;return Q.config.allowScriptTags?oe(si.querySelectorAll("script"),function(ai){Q.config.inlineScriptNonce&&(ai.nonce=Q.config.inlineScriptNonce),ai.htmxExecuted=navigator.userAgent.indexOf("Firefox")===-1}):oe(si.querySelectorAll("script"),function(ai){_(ai)}),si}switch(ei){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return s(""+ni+"
",1);case"col":return s(""+ni+"
",2);case"tr":return s(""+ni+"
",2);case"td":case"th":return s(""+ni+"
",3);case"script":case"style":return s("
"+ni+"
",1);default:return s(ni,0)}}function ie(ii){ii&&ii()}function I(ii,ti){return Object.prototype.toString.call(ii)==="[object "+ti+"]"}function k(ii){return I(ii,"Function")}function P(ii){return I(ii,"Object")}function ae(ii){var ti="htmx-internal-data",ei=ii[ti];return ei||(ei=ii[ti]={}),ei}function M(ii){var ti=[];if(ii)for(var ei=0;ei=0}function se(ii){return ii.getRootNode&&ii.getRootNode()instanceof window.ShadowRoot?re().body.contains(ii.getRootNode().host):re().body.contains(ii)}function D(ii){return ii.trim().split(/\s+/)}function le(ii,ti){for(var ei in ti)ti.hasOwnProperty(ei)&&(ii[ei]=ti[ei]);return ii}function E(ii){try{return JSON.parse(ii)}catch(ti){return b(ti),null}}function U(){var ii="htmx:localStorageTest";try{return localStorage.setItem(ii,ii),localStorage.removeItem(ii),!0}catch(ti){return!1}}function B(ii){try{var ti=new URL(ii);return ti&&(ii=ti.pathname+ti.search),/^\/$/.test(ii)||(ii=ii.replace(/\/+$/,"")),ii}catch(ei){return ii}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(ii){var ti=Q.on("htmx:load",function(ei){ii(ei.detail.elt)});return ti}function V(){Q.logger=function(ii,ti,ei){console&&console.log(ti,ii,ei)}}function j(){Q.logger=null}function C(ii,ti){return ti?ii.querySelector(ti):C(re(),ii)}function f(ii,ti){return ti?ii.querySelectorAll(ti):f(re(),ii)}function _(ii,ti){ii=p(ii),ti?setTimeout(function(){_(ii),ii=null},ti):ii.parentElement.removeChild(ii)}function z(ii,ti,ei){ii=p(ii),ei?setTimeout(function(){z(ii,ti),ii=null},ei):ii.classList&&ii.classList.add(ti)}function n(ii,ti,ei){ii=p(ii),ei?setTimeout(function(){n(ii,ti),ii=null},ei):ii.classList&&(ii.classList.remove(ti),ii.classList.length===0&&ii.removeAttribute("class"))}function $(ii,ti){ii=p(ii),ii.classList.toggle(ti)}function W(ii,ti){ii=p(ii),oe(ii.parentElement.children,function(ei){n(ei,ti)}),z(ii,ti)}function v(ii,ti){if(ii=p(ii),ii.closest)return ii.closest(ti);do if(ii==null||h(ii,ti))return ii;while(ii=ii&&u(ii));return null}function g(ii,ti){return ii.substring(0,ti.length)===ti}function G(ii,ti){return ii.substring(ii.length-ti.length)===ti}function J(ii){var ti=ii.trim();return g(ti,"<")&&G(ti,"/>")?ti.substring(1,ti.length-2):ti}function Z(ii,ti){return ti.indexOf("closest ")===0?[v(ii,J(ti.substr(8)))]:ti.indexOf("find ")===0?[C(ii,J(ti.substr(5)))]:ti==="next"?[ii.nextElementSibling]:ti.indexOf("next ")===0?[K(ii,J(ti.substr(5)))]:ti==="previous"?[ii.previousElementSibling]:ti.indexOf("previous ")===0?[Y(ii,J(ti.substr(9)))]:ti==="document"?[document]:ti==="window"?[window]:ti==="body"?[document.body]:re().querySelectorAll(J(ti))}var K=function(ii,ti){for(var ei=re().querySelectorAll(ti),ni=0;ni=0;ni--){var ri=ei[ni];if(ri.compareDocumentPosition(ii)===Node.DOCUMENT_POSITION_FOLLOWING)return ri}};function ue(ii,ti){return ti?Z(ii,ti)[0]:Z(re().body,ii)[0]}function p(ii){return I(ii,"String")?C(ii):ii}function ve(ii,ti,ei){return k(ti)?{target:re().body,event:ii,listener:ti}:{target:p(ii),event:ti,listener:ei}}function de(ii,ti,ei){jr(function(){var ri=ve(ii,ti,ei);ri.target.addEventListener(ri.event,ri.listener)});var ni=k(ti);return ni?ti:ei}function ge(ii,ti,ei){return jr(function(){var ni=ve(ii,ti,ei);ni.target.removeEventListener(ni.event,ni.listener)}),k(ti)?ti:ei}var pe=re().createElement("output");function me(ii,ti){var ei=ne(ii,ti);if(ei){if(ei==="this")return[xe(ii,ti)];var ni=Z(ii,ei);return ni.length===0?(b('The selector "'+ei+'" on '+ti+" returned no matches!"),[pe]):ni}}function xe(ii,ti){return c(ii,function(ei){return te(ei,ti)!=null})}function ye(ii){var ti=ne(ii,"hx-target");if(ti)return ti==="this"?xe(ii,"hx-target"):ue(ii,ti);var ei=ae(ii);return ei.boosted?re().body:ii}function be(ii){for(var ti=Q.config.attributesToSettle,ei=0;ei0?(ri=ii.substr(0,ii.indexOf(":")),ni=ii.substr(ii.indexOf(":")+1,ii.length)):ri=ii);var si=re().querySelectorAll(ni);return si?(oe(si,function(ai){var li,di=ti.cloneNode(!0);li=re().createDocumentFragment(),li.appendChild(di),Se(ri,ai)||(li=di);var yi={shouldSwap:!0,target:ai,fragment:li};!ce(ai,"htmx:oobBeforeSwap",yi)||(ai=yi.target,yi.shouldSwap&&Fe(ri,ai,ai,li,ei),oe(ei.elts,function(pi){ce(pi,"htmx:oobAfterSwap",yi)}))}),ti.parentNode.removeChild(ti)):(ti.parentNode.removeChild(ti),fe(re().body,"htmx:oobErrorNoTarget",{content:ti})),ii}function Ce(ii,ti,ei){var ni=ne(ii,"hx-select-oob");if(ni)for(var ri=ni.split(","),si=0;si0){var si=ri.replace("'","\\'"),ai=ni.tagName.replace(":","\\:"),li=ii.querySelector(ai+"[id='"+si+"']");if(li&&li!==ii){var di=ni.cloneNode();we(ni,li),ei.tasks.push(function(){we(ni,di)})}}})}function Oe(ii){return function(){n(ii,Q.config.addedClass),zt(ii),Nt(ii),qe(ii),ce(ii,"htmx:load")}}function qe(ii){var ti="[autofocus]",ei=h(ii,ti)?ii:ii.querySelector(ti);ei!=null&&ei.focus()}function a(ii,ti,ei,ni){for(Te(ii,ei,ni);ei.childNodes.length>0;){var ri=ei.firstChild;z(ri,Q.config.addedClass),ii.insertBefore(ri,ti),ri.nodeType!==Node.TEXT_NODE&&ri.nodeType!==Node.COMMENT_NODE&&ni.tasks.push(Oe(ri))}}function He(ii,ti){for(var ei=0;ei-1){var ti=ii.replace(H,""),ei=ti.match(q);if(ei)return ei[2]}}function je(ii,ti,ei,ni,ri,si){ri.title=Ve(ni);var ai=l(ni);if(ai)return Ce(ei,ai,ri),ai=Be(ei,ai,si),Re(ai),Fe(ii,ei,ti,ai,ri)}function _e(ii,ti,ei){var ni=ii.getResponseHeader(ti);if(ni.indexOf("{")===0){var ri=E(ni);for(var si in ri)if(ri.hasOwnProperty(si)){var ai=ri[si];P(ai)||(ai={value:ai}),ce(ei,si,ai)}}else for(var li=ni.split(","),di=0;di0;){var ai=ti[0];if(ai==="]"){if(ni--,ni===0){si===null&&(ri=ri+"true"),ti.shift(),ri+=")})";try{var li=Tr(ii,function(){return Function(ri)()},function(){return!0});return li.source=ri,li}catch(di){return fe(re().body,"htmx:syntax:error",{error:di,source:ri}),null}}}else ai==="["&&ni++;Qe(ai,si,ei)?ri+="(("+ei+"."+ai+") ? ("+ei+"."+ai+") : (window."+ai+"))":ri=ri+ai,si=ti.shift()}}}function y(ii,ti){for(var ei="";ii.length>0&&!ti.test(ii[0]);)ei+=ii.shift();return ei}function tt(ii){var ti;return ii.length>0&&Ze.test(ii[0])?(ii.shift(),ti=y(ii,Ke).trim(),ii.shift()):ti=y(ii,x),ti}var rt="input, textarea, select";function nt(ii,ti,ei){var ni=[],ri=Ye(ti);do{y(ri,Je);var si=ri.length,ai=y(ri,/[,\[\s]/);if(ai!=="")if(ai==="every"){var li={trigger:"every"};y(ri,Je),li.pollInterval=d(y(ri,/[,\[\s]/)),y(ri,Je);var di=et(ii,ri,"event");di&&(li.eventFilter=di),ni.push(li)}else if(ai.indexOf("sse:")===0)ni.push({trigger:"sse",sseEvent:ai.substr(4)});else{var yi={trigger:ai},di=et(ii,ri,"event");for(di&&(yi.eventFilter=di);ri.length>0&&ri[0]!==",";){y(ri,Je);var pi=ri.shift();if(pi==="changed")yi.changed=!0;else if(pi==="once")yi.once=!0;else if(pi==="consume")yi.consume=!0;else if(pi==="delay"&&ri[0]===":")ri.shift(),yi.delay=d(y(ri,x));else if(pi==="from"&&ri[0]===":"){if(ri.shift(),Ze.test(ri[0]))var vi=tt(ri);else{var vi=y(ri,x);if(vi==="closest"||vi==="find"||vi==="next"||vi==="previous"){ri.shift();var Ei=tt(ri);Ei.length>0&&(vi+=" "+Ei)}}yi.from=vi}else pi==="target"&&ri[0]===":"?(ri.shift(),yi.target=tt(ri)):pi==="throttle"&&ri[0]===":"?(ri.shift(),yi.throttle=d(y(ri,x))):pi==="queue"&&ri[0]===":"?(ri.shift(),yi.queue=y(ri,x)):pi==="root"&&ri[0]===":"?(ri.shift(),yi[pi]=tt(ri)):pi==="threshold"&&ri[0]===":"?(ri.shift(),yi[pi]=y(ri,x)):fe(ii,"htmx:syntax:error",{token:ri.shift()})}ni.push(yi)}ri.length===si&&fe(ii,"htmx:syntax:error",{token:ri.shift()}),y(ri,Je)}while(ri[0]===","&&ri.shift());return ei&&(ei[ti]=ni),ni}function it(ii){var ti=te(ii,"hx-trigger"),ei=[];if(ti){var ni=Q.config.triggerSpecsCache;ei=ni&&ni[ti]||nt(ii,ti,ni)}return ei.length>0?ei:h(ii,"form")?[{trigger:"submit"}]:h(ii,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:h(ii,rt)?[{trigger:"change"}]:[{trigger:"click"}]}function at(ii){ae(ii).cancelled=!0}function ot(ii,ti,ei){var ni=ae(ii);ni.timeout=setTimeout(function(){se(ii)&&ni.cancelled!==!0&&(ct(ei,ii,Wt("hx:poll:trigger",{triggerSpec:ei,target:ii}))||ti(ii),ot(ii,ti,ei))},ei.pollInterval)}function st(ii){return location.hostname===ii.hostname&&ee(ii,"href")&&ee(ii,"href").indexOf("#")!==0}function lt(ii,ti,ei){if(ii.tagName==="A"&&st(ii)&&(ii.target===""||ii.target==="_self")||ii.tagName==="FORM"){ti.boosted=!0;var ni,ri;if(ii.tagName==="A")ni="get",ri=ee(ii,"href");else{var si=ee(ii,"method");ni=si?si.toLowerCase():"get",ri=ee(ii,"action")}ei.forEach(function(ai){ht(ii,function(li,di){if(v(li,Q.config.disableSelector)){m(li);return}he(ni,ri,li,di)},ti,ai,!0)})}}function ut(ii,ti){return!!((ii.type==="submit"||ii.type==="click")&&(ti.tagName==="FORM"||h(ti,'input[type="submit"], button')&&v(ti,"form")!==null||ti.tagName==="A"&&ti.href&&(ti.getAttribute("href")==="#"||ti.getAttribute("href").indexOf("#")!==0)))}function ft(ii,ti){return ae(ii).boosted&&ii.tagName==="A"&&ti.type==="click"&&(ti.ctrlKey||ti.metaKey)}function ct(ii,ti,ei){var ni=ii.eventFilter;if(ni)try{return ni.call(ti,ei)!==!0}catch(ri){return fe(re().body,"htmx:eventFilter:error",{error:ri,source:ni.source}),!0}return!1}function ht(ii,ti,ei,ni,ri){var si=ae(ii),ai;ni.from?ai=Z(ii,ni.from):ai=[ii],ni.changed&&ai.forEach(function(li){var di=ae(li);di.lastValue=li.value}),oe(ai,function(li){var di=function(yi){if(!se(ii)){li.removeEventListener(ni.trigger,di);return}if(!ft(ii,yi)&&((ri||ut(yi,ii))&&yi.preventDefault(),!ct(ni,ii,yi))){var pi=ae(yi);if(pi.triggerSpec=ni,pi.handledFor==null&&(pi.handledFor=[]),pi.handledFor.indexOf(ii)<0){if(pi.handledFor.push(ii),ni.consume&&yi.stopPropagation(),ni.target&&yi.target&&!h(yi.target,ni.target))return;if(ni.once){if(si.triggeredOnce)return;si.triggeredOnce=!0}if(ni.changed){var vi=ae(li);if(vi.lastValue===li.value)return;vi.lastValue=li.value}if(si.delayed&&clearTimeout(si.delayed),si.throttle)return;ni.throttle>0?si.throttle||(ti(ii,yi),si.throttle=setTimeout(function(){si.throttle=null},ni.throttle)):ni.delay>0?si.delayed=setTimeout(function(){ti(ii,yi)},ni.delay):(ce(ii,"htmx:trigger"),ti(ii,yi))}}};ei.listenerInfos==null&&(ei.listenerInfos=[]),ei.listenerInfos.push({trigger:ni.trigger,listener:di,on:li}),li.addEventListener(ni.trigger,di)})}var vt=!1,dt=null;function gt(){dt||(dt=function(){vt=!0},window.addEventListener("scroll",dt),setInterval(function(){vt&&(vt=!1,oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(ii){pt(ii)}))},200))}function pt(ii){if(!o(ii,"data-hx-revealed")&&X(ii)){ii.setAttribute("data-hx-revealed","true");var ti=ae(ii);ti.initHash?ce(ii,"revealed"):ii.addEventListener("htmx:afterProcessNode",function(ei){ce(ii,"revealed")},{once:!0})}}function mt(ii,ti,ei){for(var ni=D(ei),ri=0;ri=0){var ai=wt(ei);setTimeout(function(){xt(ii,ti,ei+1)},ai)}},ri.onopen=function(si){ei=0},ae(ii).webSocket=ri,ri.addEventListener("message",function(si){if(!yt(ii)){var ai=si.data;R(ii,function(Ei){ai=Ei.transformResponse(ai,null,ii)});for(var li=T(ii),di=l(ai),yi=M(di.children),pi=0;pi0){ce(ii,"htmx:validation:halted",ai);return}ni.send(JSON.stringify(pi)),ut(ei,ii)&&ei.preventDefault()}):fe(ii,"htmx:noWebSocketSourceError")}function wt(ii){var ti=Q.config.wsReconnectDelay;if(typeof ti=="function")return ti(ii);if(ti==="full-jitter"){var ei=Math.min(ii,6),ni=1e3*Math.pow(2,ei);return ni*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(ii,ti,ei){for(var ni=D(ei),ri=0;ri0?setTimeout(ri,ni):ri()}function Ht(ii,ti,ei){var ni=!1;return oe(w,function(ri){if(o(ii,"hx-"+ri)){var si=te(ii,"hx-"+ri);ni=!0,ti.path=si,ti.verb=ri,ei.forEach(function(ai){Lt(ii,ai,ti,function(li,di){if(v(li,Q.config.disableSelector)){m(li);return}he(ri,si,li,di)})})}}),ni}function Lt(ii,ti,ei,ni){if(ti.sseEvent)Rt(ii,ni,ti.sseEvent);else if(ti.trigger==="revealed")gt(),ht(ii,ni,ei,ti),pt(ii);else if(ti.trigger==="intersect"){var ri={};ti.root&&(ri.root=ue(ii,ti.root)),ti.threshold&&(ri.threshold=parseFloat(ti.threshold));var si=new IntersectionObserver(function(ai){for(var li=0;li0?(ei.polling=!0,ot(ii,ni,ti)):ht(ii,ni,ei,ti)}function At(ii){if(!ii.htmxExecuted&&Q.config.allowScriptTags&&(ii.type==="text/javascript"||ii.type==="module"||ii.type==="")){var ti=re().createElement("script");oe(ii.attributes,function(ni){ti.setAttribute(ni.name,ni.value)}),ti.textContent=ii.textContent,ti.async=!1,Q.config.inlineScriptNonce&&(ti.nonce=Q.config.inlineScriptNonce);var ei=ii.parentElement;try{ei.insertBefore(ti,ii)}catch(ni){b(ni)}finally{ii.parentElement&&ii.parentElement.removeChild(ii)}}}function Nt(ii){h(ii,"script")&&At(ii),oe(f(ii,"script"),function(ti){At(ti)})}function It(ii){var ti=ii.attributes;if(!ti)return!1;for(var ei=0;ei0;){var ai=ni.shift(),li=ai.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);si===0&&li?(ai.split(":"),ri=li[1].slice(0,-1),ei[ri]=li[2]):ei[ri]+=ai,si+=Bt(ai)}for(var di in ei)Ft(ii,di,ei[di])}}function jt(ii){Ae(ii);for(var ti=0;tiQ.config.historyCacheSize;)ri.shift();for(;ri.length>0;)try{localStorage.setItem("htmx-history-cache",JSON.stringify(ri));break}catch(li){fe(re().body,"htmx:historyCacheError",{cause:li,cache:ri}),ri.shift()}}}function Yt(ii){if(!U())return null;ii=B(ii);for(var ti=E(localStorage.getItem("htmx-history-cache"))||[],ei=0;ei=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",ei);var ni=l(this.response);ni=ni.querySelector("[hx-history-elt],[data-hx-history-elt]")||ni;var ri=Zt(),si=T(ri),ai=Ve(this.response);if(ai){var li=C("title");li?li.innerHTML=ai:window.document.title=ai}Ue(ri,ni,si),nr(si.tasks),Jt=ii,ce(re().body,"htmx:historyRestore",{path:ii,cacheMiss:!0,serverResponse:this.response})}else fe(re().body,"htmx:historyCacheMissLoadError",ei)},ti.send()}function ar(ii){er(),ii=ii||location.pathname+location.search;var ti=Yt(ii);if(ti){var ei=l(ti.content),ni=Zt(),ri=T(ni);Ue(ni,ei,ri),nr(ri.tasks),document.title=ti.title,setTimeout(function(){window.scrollTo(0,ti.scroll)},0),Jt=ii,ce(re().body,"htmx:historyRestore",{path:ii,item:ti})}else Q.config.refreshOnHistoryMiss?window.location.reload(!0):ir(ii)}function or(ii){var ti=me(ii,"hx-indicator");return ti==null&&(ti=[ii]),oe(ti,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)+1,ei.classList.add.call(ei.classList,Q.config.requestClass)}),ti}function sr(ii){var ti=me(ii,"hx-disabled-elt");return ti==null&&(ti=[]),oe(ti,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)+1,ei.setAttribute("disabled","")}),ti}function lr(ii,ti){oe(ii,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)-1,ni.requestCount===0&&ei.classList.remove.call(ei.classList,Q.config.requestClass)}),oe(ti,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)-1,ni.requestCount===0&&ei.removeAttribute("disabled")})}function ur(ii,ti){for(var ei=0;ei=0}function wr(ii,ti){var ei=ti||ne(ii,"hx-swap"),ni={swapStyle:ae(ii).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(ii).boosted&&!br(ii)&&(ni.show="top"),ei){var ri=D(ei);if(ri.length>0)for(var si=0;si0?di.join(":"):null;ni.scroll=yi,ni.scrollTarget=pi}else if(ai.indexOf("show:")===0){var vi=ai.substr(5),di=vi.split(":"),Ei=di.pop(),pi=di.length>0?di.join(":"):null;ni.show=Ei,ni.showTarget=pi}else if(ai.indexOf("focus-scroll:")===0){var Ai=ai.substr("focus-scroll:".length);ni.focusScroll=Ai=="true"}else si==0?ni.swapStyle=ai:b("Unknown modifier in hx-swap: "+ai)}}return ni}function Sr(ii){return ne(ii,"hx-encoding")==="multipart/form-data"||h(ii,"form")&&ee(ii,"enctype")==="multipart/form-data"}function Er(ii,ti,ei){var ni=null;return R(ti,function(ri){ni==null&&(ni=ri.encodeParameters(ii,ei,ti))}),ni!=null?ni:Sr(ti)?mr(ei):pr(ei)}function T(ii){return{tasks:[],elts:[ii]}}function Cr(ii,ti){var ei=ii[0],ni=ii[ii.length-1];if(ti.scroll){var ri=null;ti.scrollTarget&&(ri=ue(ei,ti.scrollTarget)),ti.scroll==="top"&&(ei||ri)&&(ri=ri||ei,ri.scrollTop=0),ti.scroll==="bottom"&&(ni||ri)&&(ri=ri||ni,ri.scrollTop=ri.scrollHeight)}if(ti.show){var ri=null;if(ti.showTarget){var si=ti.showTarget;ti.showTarget==="window"&&(si="body"),ri=ue(ei,si)}ti.show==="top"&&(ei||ri)&&(ri=ri||ei,ri.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})),ti.show==="bottom"&&(ni||ri)&&(ri=ri||ni,ri.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior}))}}function Rr(ii,ti,ei,ni){if(ni==null&&(ni={}),ii==null)return ni;var ri=te(ii,ti);if(ri){var si=ri.trim(),ai=ei;if(si==="unset")return null;si.indexOf("javascript:")===0?(si=si.substr(11),ai=!0):si.indexOf("js:")===0&&(si=si.substr(3),ai=!0),si.indexOf("{")!==0&&(si="{"+si+"}");var li;ai?li=Tr(ii,function(){return Function("return ("+si+")")()},{}):li=E(si);for(var di in li)li.hasOwnProperty(di)&&ni[di]==null&&(ni[di]=li[di])}return Rr(u(ii),ti,ei,ni)}function Tr(ii,ti,ei){return Q.config.allowEval?ti():(fe(ii,"htmx:evalDisallowedError"),ei)}function Or(ii,ti){return Rr(ii,"hx-vars",!0,ti)}function qr(ii,ti){return Rr(ii,"hx-vals",!1,ti)}function Hr(ii){return le(Or(ii),qr(ii))}function Lr(ii,ti,ei){if(ei!==null)try{ii.setRequestHeader(ti,ei)}catch(ni){ii.setRequestHeader(ti,encodeURIComponent(ei)),ii.setRequestHeader(ti+"-URI-AutoEncoded","true")}}function Ar(ii){if(ii.responseURL&&typeof URL!="undefined")try{var ti=new URL(ii.responseURL);return ti.pathname+ti.search}catch(ei){fe(re().body,"htmx:badResponseUrl",{url:ii.responseURL})}}function O(ii,ti){return ti.test(ii.getAllResponseHeaders())}function Nr(ii,ti,ei){return ii=ii.toLowerCase(),ei?ei instanceof Element||I(ei,"String")?he(ii,ti,null,null,{targetOverride:p(ei),returnPromise:!0}):he(ii,ti,p(ei.source),ei.event,{handler:ei.handler,headers:ei.headers,values:ei.values,targetOverride:p(ei.target),swapOverride:ei.swap,select:ei.select,returnPromise:!0}):he(ii,ti,null,null,{returnPromise:!0})}function Ir(ii){for(var ti=[];ii;)ti.push(ii),ii=ii.parentElement;return ti}function kr(ii,ti,ei){var ni,ri;if(typeof URL=="function"){ri=new URL(ti,document.location.href);var si=document.location.origin;ni=si===ri.origin}else ri=ti,ni=g(ti,document.location.origin);return Q.config.selfRequestsOnly&&!ni?!1:ce(ii,"htmx:validateUrl",le({url:ri,sameHost:ni},ei))}function he(ii,ti,ei,ni,ri,si){var ai=null,li=null;if(ri=ri!=null?ri:{},ri.returnPromise&&typeof Promise!="undefined")var di=new Promise(function(Wn,_n){ai=Wn,li=_n});ei==null&&(ei=re().body);var yi=ri.handler||Mr,pi=ri.select||null;if(!se(ei))return ie(ai),di;var vi=ri.targetOverride||ye(ei);if(vi==null||vi==pe)return fe(ei,"htmx:targetError",{target:te(ei,"hx-target")}),ie(li),di;var Ei=ae(ei),Ai=Ei.lastButtonClicked;if(Ai){var Mi=ee(Ai,"formaction");Mi!=null&&(ti=Mi);var Ti=ee(Ai,"formmethod");Ti!=null&&Ti.toLowerCase()!=="dialog"&&(ii=Ti)}var Fi=ne(ei,"hx-confirm");if(si===void 0){var en=function(Wn){return he(ii,ti,ei,ni,ri,!!Wn)},rn={target:vi,elt:ei,path:ti,verb:ii,triggeringEvent:ni,etc:ri,issueRequest:en,question:Fi};if(ce(ei,"htmx:confirm",rn)===!1)return ie(ai),di}var ln=ei,Yi=ne(ei,"hx-sync"),sn=null,on=!1;if(Yi){var En=Yi.split(":"),pn=En[0].trim();if(pn==="this"?ln=xe(ei,"hx-sync"):ln=ue(ei,pn),Yi=(En[1]||"drop").trim(),Ei=ae(ln),Yi==="drop"&&Ei.xhr&&Ei.abortable!==!0)return ie(ai),di;if(Yi==="abort"){if(Ei.xhr)return ie(ai),di;on=!0}else if(Yi==="replace")ce(ln,"htmx:abort");else if(Yi.indexOf("queue")===0){var mn=Yi.split(" ");sn=(mn[1]||"last").trim()}}if(Ei.xhr)if(Ei.abortable)ce(ln,"htmx:abort");else{if(sn==null){if(ni){var vn=ae(ni);vn&&vn.triggerSpec&&vn.triggerSpec.queue&&(sn=vn.triggerSpec.queue)}sn==null&&(sn="last")}return Ei.queuedRequests==null&&(Ei.queuedRequests=[]),sn==="first"&&Ei.queuedRequests.length===0?Ei.queuedRequests.push(function(){he(ii,ti,ei,ni,ri)}):sn==="all"?Ei.queuedRequests.push(function(){he(ii,ti,ei,ni,ri)}):sn==="last"&&(Ei.queuedRequests=[],Ei.queuedRequests.push(function(){he(ii,ti,ei,ni,ri)})),ie(ai),di}var un=new XMLHttpRequest;Ei.xhr=un,Ei.abortable=on;var yn=function(){if(Ei.xhr=null,Ei.abortable=!1,Ei.queuedRequests!=null&&Ei.queuedRequests.length>0){var Wn=Ei.queuedRequests.shift();Wn()}},Cn=ne(ei,"hx-prompt");if(Cn){var Ni=prompt(Cn);if(Ni===null||!ce(ei,"htmx:prompt",{prompt:Ni,target:vi}))return ie(ai),yn(),di}if(Fi&&!si&&!confirm(Fi))return ie(ai),yn(),di;var Hi=xr(ei,vi,Ni);ii!=="get"&&!Sr(ei)&&(Hi["Content-Type"]="application/x-www-form-urlencoded"),ri.headers&&(Hi=le(Hi,ri.headers));var Pi=dr(ei,ii),$i=Pi.errors,gn=Pi.values;ri.values&&(gn=le(gn,ri.values));var tn=Hr(ei),Bi=le(gn,tn),Li=yr(Bi,ei);Q.config.getCacheBusterParam&&ii==="get"&&(Li["org.htmx.cache-buster"]=ee(vi,"id")||"true"),(ti==null||ti==="")&&(ti=re().location.href);var zi=Rr(ei,"hx-request"),Ji=ae(ei).boosted,Vi=Q.config.methodsThatUseUrlParams.indexOf(ii)>=0,Ri={boosted:Ji,useUrlParams:Vi,parameters:Li,unfilteredParameters:Bi,headers:Hi,target:vi,verb:ii,errors:$i,withCredentials:ri.credentials||zi.credentials||Q.config.withCredentials,timeout:ri.timeout||zi.timeout||Q.config.timeout,path:ti,triggeringEvent:ni};if(!ce(ei,"htmx:configRequest",Ri))return ie(ai),yn(),di;if(ti=Ri.path,ii=Ri.verb,Hi=Ri.headers,Li=Ri.parameters,$i=Ri.errors,Vi=Ri.useUrlParams,$i&&$i.length>0)return ce(ei,"htmx:validation:halted",Ri),ie(ai),yn(),di;var Sn=ti.split("#"),Kn=Sn[0],$n=Sn[1],Bn=ti;if(Vi){Bn=Kn;var Gn=Object.keys(Li).length!==0;Gn&&(Bn.indexOf("?")<0?Bn+="?":Bn+="&",Bn+=pr(Li),$n&&(Bn+="#"+$n))}if(!kr(ei,Bn,Ri))return fe(ei,"htmx:invalidPath",Ri),ie(li),di;if(un.open(ii.toUpperCase(),Bn,!0),un.overrideMimeType("text/html"),un.withCredentials=Ri.withCredentials,un.timeout=Ri.timeout,!zi.noHeaders){for(var Xn in Hi)if(Hi.hasOwnProperty(Xn)){var Cs=Hi[Xn];Lr(un,Xn,Cs)}}var zn={xhr:un,target:vi,requestConfig:Ri,etc:ri,boosted:Ji,select:pi,pathInfo:{requestPath:ti,finalRequestPath:Bn,anchor:$n}};if(un.onload=function(){try{var Wn=Ir(ei);if(zn.pathInfo.responsePath=Ar(un),yi(ei,zn),lr(ps,ms),ce(ei,"htmx:afterRequest",zn),ce(ei,"htmx:afterOnLoad",zn),!se(ei)){for(var _n=null;Wn.length>0&&_n==null;){var Qr=Wn.shift();se(Qr)&&(_n=Qr)}_n&&(ce(_n,"htmx:afterRequest",zn),ce(_n,"htmx:afterOnLoad",zn))}ie(ai),yn()}catch(ls){throw fe(ei,"htmx:onLoadError",le({error:ls},zn)),ls}},un.onerror=function(){lr(ps,ms),fe(ei,"htmx:afterRequest",zn),fe(ei,"htmx:sendError",zn),ie(li),yn()},un.onabort=function(){lr(ps,ms),fe(ei,"htmx:afterRequest",zn),fe(ei,"htmx:sendAbort",zn),ie(li),yn()},un.ontimeout=function(){lr(ps,ms),fe(ei,"htmx:afterRequest",zn),fe(ei,"htmx:timeout",zn),ie(li),yn()},!ce(ei,"htmx:beforeRequest",zn))return ie(ai),yn(),di;var ps=or(ei),ms=sr(ei);oe(["loadstart","loadend","progress","abort"],function(Wn){oe([un,un.upload],function(_n){_n.addEventListener(Wn,function(Qr){ce(ei,"htmx:xhr:"+Wn,{lengthComputable:Qr.lengthComputable,loaded:Qr.loaded,total:Qr.total})})})}),ce(ei,"htmx:beforeSend",zn);var Qs=Vi?null:Er(un,ei,Li);return un.send(Qs),di}function Pr(ii,ti){var ei=ti.xhr,ni=null,ri=null;if(O(ei,/HX-Push:/i)?(ni=ei.getResponseHeader("HX-Push"),ri="push"):O(ei,/HX-Push-Url:/i)?(ni=ei.getResponseHeader("HX-Push-Url"),ri="push"):O(ei,/HX-Replace-Url:/i)&&(ni=ei.getResponseHeader("HX-Replace-Url"),ri="replace"),ni)return ni==="false"?{}:{type:ri,path:ni};var si=ti.pathInfo.finalRequestPath,ai=ti.pathInfo.responsePath,li=ne(ii,"hx-push-url"),di=ne(ii,"hx-replace-url"),yi=ae(ii).boosted,pi=null,vi=null;return li?(pi="push",vi=li):di?(pi="replace",vi=di):yi&&(pi="push",vi=ai||si),vi?vi==="false"?{}:(vi==="true"&&(vi=ai||si),ti.pathInfo.anchor&&vi.indexOf("#")===-1&&(vi=vi+"#"+ti.pathInfo.anchor),{type:pi,path:vi}):{}}function Mr(ii,ti){var ei=ti.xhr,ni=ti.target,ri=ti.etc,si=ti.requestConfig,ai=ti.select;if(!!ce(ii,"htmx:beforeOnLoad",ti)){if(O(ei,/HX-Trigger:/i)&&_e(ei,"HX-Trigger",ii),O(ei,/HX-Location:/i)){er();var li=ei.getResponseHeader("HX-Location"),di;li.indexOf("{")===0&&(di=E(li),li=di.path,delete di.path),Nr("GET",li,di).then(function(){tr(li)});return}var yi=O(ei,/HX-Refresh:/i)&&ei.getResponseHeader("HX-Refresh")==="true";if(O(ei,/HX-Redirect:/i)){location.href=ei.getResponseHeader("HX-Redirect"),yi&&location.reload();return}if(yi){location.reload();return}O(ei,/HX-Retarget:/i)&&(ei.getResponseHeader("HX-Retarget")==="this"?ti.target=ii:ti.target=ue(ii,ei.getResponseHeader("HX-Retarget")));var pi=Pr(ii,ti),vi=ei.status>=200&&ei.status<400&&ei.status!==204,Ei=ei.response,Ai=ei.status>=400,Mi=Q.config.ignoreTitle,Ti=le({shouldSwap:vi,serverResponse:Ei,isError:Ai,ignoreTitle:Mi},ti);if(!!ce(ni,"htmx:beforeSwap",Ti)){if(ni=Ti.target,Ei=Ti.serverResponse,Ai=Ti.isError,Mi=Ti.ignoreTitle,ti.target=ni,ti.failed=Ai,ti.successful=!Ai,Ti.shouldSwap){ei.status===286&&at(ii),R(ii,function(pn){Ei=pn.transformResponse(Ei,ei,ii)}),pi.type&&er();var Fi=ri.swapOverride;O(ei,/HX-Reswap:/i)&&(Fi=ei.getResponseHeader("HX-Reswap"));var di=wr(ii,Fi);di.hasOwnProperty("ignoreTitle")&&(Mi=di.ignoreTitle),ni.classList.add(Q.config.swappingClass);var en=null,rn=null,ln=function(){try{var pn=document.activeElement,mn={};try{mn={elt:pn,start:pn?pn.selectionStart:null,end:pn?pn.selectionEnd:null}}catch(Pi){}var vn;ai&&(vn=ai),O(ei,/HX-Reselect:/i)&&(vn=ei.getResponseHeader("HX-Reselect")),pi.type&&(ce(re().body,"htmx:beforeHistoryUpdate",le({history:pi},ti)),pi.type==="push"?(tr(pi.path),ce(re().body,"htmx:pushedIntoHistory",{path:pi.path})):(rr(pi.path),ce(re().body,"htmx:replacedInHistory",{path:pi.path})));var un=T(ni);if(je(di.swapStyle,ni,ii,Ei,un,vn),mn.elt&&!se(mn.elt)&&ee(mn.elt,"id")){var yn=document.getElementById(ee(mn.elt,"id")),Cn={preventScroll:di.focusScroll!==void 0?!di.focusScroll:!Q.config.defaultFocusScroll};if(yn){if(mn.start&&yn.setSelectionRange)try{yn.setSelectionRange(mn.start,mn.end)}catch(Pi){}yn.focus(Cn)}}if(ni.classList.remove(Q.config.swappingClass),oe(un.elts,function(Pi){Pi.classList&&Pi.classList.add(Q.config.settlingClass),ce(Pi,"htmx:afterSwap",ti)}),O(ei,/HX-Trigger-After-Swap:/i)){var Ni=ii;se(ii)||(Ni=re().body),_e(ei,"HX-Trigger-After-Swap",Ni)}var Hi=function(){if(oe(un.tasks,function(tn){tn.call()}),oe(un.elts,function(tn){tn.classList&&tn.classList.remove(Q.config.settlingClass),ce(tn,"htmx:afterSettle",ti)}),ti.pathInfo.anchor){var Pi=re().getElementById(ti.pathInfo.anchor);Pi&&Pi.scrollIntoView({block:"start",behavior:"auto"})}if(un.title&&!Mi){var $i=C("title");$i?$i.innerHTML=un.title:window.document.title=un.title}if(Cr(un.elts,di),O(ei,/HX-Trigger-After-Settle:/i)){var gn=ii;se(ii)||(gn=re().body),_e(ei,"HX-Trigger-After-Settle",gn)}ie(en)};di.settleDelay>0?setTimeout(Hi,di.settleDelay):Hi()}catch(Pi){throw fe(ii,"htmx:swapError",ti),ie(rn),Pi}},Yi=Q.config.globalViewTransitions;if(di.hasOwnProperty("transition")&&(Yi=di.transition),Yi&&ce(ii,"htmx:beforeTransition",ti)&&typeof Promise!="undefined"&&document.startViewTransition){var sn=new Promise(function(pn,mn){en=pn,rn=mn}),on=ln;ln=function(){document.startViewTransition(function(){return on(),sn})}}di.swapDelay>0?setTimeout(ln,di.swapDelay):ln()}Ai&&fe(ii,"htmx:responseError",le({error:"Response Status Error Code "+ei.status+" from "+ti.pathInfo.requestPath},ti))}}}var Xr={};function Dr(){return{init:function(ii){return null},onEvent:function(ii,ti){return!0},transformResponse:function(ii,ti,ei){return ii},isInlineSwap:function(ii){return!1},handleSwap:function(ii,ti,ei,ni){return!1},encodeParameters:function(ii,ti,ei){return null}}}function Ur(ii,ti){ti.init&&ti.init(r),Xr[ii]=le(Dr(),ti)}function Br(ii){delete Xr[ii]}function Fr(ii,ti,ei){if(ii==null)return ti;ti==null&&(ti=[]),ei==null&&(ei=[]);var ni=te(ii,"hx-ext");return ni&&oe(ni.split(","),function(ri){if(ri=ri.replace(/ /g,""),ri.slice(0,7)=="ignore:"){ei.push(ri.slice(7));return}if(ei.indexOf(ri)<0){var si=Xr[ri];si&&ti.indexOf(si)<0&&ti.push(si)}}),Fr(u(ii),ti,ei)}var Vr=!1;re().addEventListener("DOMContentLoaded",function(){Vr=!0});function jr(ii){Vr||re().readyState==="complete"?ii():re().addEventListener("DOMContentLoaded",ii)}function _r(){Q.config.includeIndicatorStyles!==!1&&re().head.insertAdjacentHTML("beforeend","")}function zr(){var ii=re().querySelector('meta[name="htmx-config"]');return ii?E(ii.content):null}function $r(){var ii=zr();ii&&(Q.config=le(Q.config,ii))}return jr(function(){$r(),_r();var ii=re().body;zt(ii);var ti=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");ii.addEventListener("htmx:abort",function(ni){var ri=ni.target,si=ae(ri);si&&si.xhr&&si.xhr.abort()});let ei=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(ni){ni.state&&ni.state.htmx?(ar(),oe(ti,function(ri){ce(ri,"htmx:restored",{document:re(),triggerEvent:ce})})):ei&&ei(ni)},setTimeout(function(){ce(ii,"htmx:load",{}),ii=null},0)}),Q}()})});var _l=Ga((Ic,Pc)=>{(function(ii,ti){typeof Ic=="object"&&typeof Pc!="undefined"?Pc.exports=ti():typeof define=="function"&&define.amd?define(ti):(ii=typeof globalThis!="undefined"?globalThis:ii||self,ii.TomSelect=ti())})(Ic,function(){"use strict";function ii(fi,oi){fi.split(/\s+/).forEach(ci=>{oi(ci)})}class ti{constructor(){this._events=void 0,this._events={}}on(oi,ci){ii(oi,ui=>{let mi=this._events[ui]||[];mi.push(ci),this._events[ui]=mi})}off(oi,ci){var ui=arguments.length;if(ui===0){this._events={};return}ii(oi,mi=>{if(ui===1){delete this._events[mi];return}let bi=this._events[mi];bi!==void 0&&(bi.splice(bi.indexOf(ci),1),this._events[mi]=bi)})}trigger(oi,...ci){var ui=this;ii(oi,mi=>{let bi=ui._events[mi];bi!==void 0&&bi.forEach(_i=>{_i.apply(ui,ci)})})}}function ei(fi){return fi.plugins={},class extends fi{constructor(...oi){super(...oi);this.plugins={names:[],settings:{},requested:{},loaded:{}}}static define(oi,ci){fi.plugins[oi]={name:oi,fn:ci}}initializePlugins(oi){var ci,ui;let mi=this,bi=[];if(Array.isArray(oi))oi.forEach(_i=>{typeof _i=="string"?bi.push(_i):(mi.plugins.settings[_i.name]=_i.options,bi.push(_i.name))});else if(oi)for(ci in oi)oi.hasOwnProperty(ci)&&(mi.plugins.settings[ci]=oi[ci],bi.push(ci));for(;ui=bi.shift();)mi.require(ui)}loadPlugin(oi){var ci=this,ui=ci.plugins,mi=fi.plugins[oi];if(!fi.plugins.hasOwnProperty(oi))throw new Error('Unable to find "'+oi+'" plugin');ui.requested[oi]=!0,ui.loaded[oi]=mi.fn.apply(ci,[ci.plugins.settings[oi]||{}]),ui.names.push(oi)}require(oi){var ci=this,ui=ci.plugins;if(!ci.plugins.loaded.hasOwnProperty(oi)){if(ui.requested[oi])throw new Error('Plugin has circular dependency ("'+oi+'")');ci.loadPlugin(oi)}return ui.loaded[oi]}}}let ni=fi=>(fi=fi.filter(Boolean),fi.length<2?fi[0]||"":di(fi)==1?"["+fi.join("")+"]":"(?:"+fi.join("|")+")"),ri=fi=>{if(!ai(fi))return fi.join("");let oi="",ci=0,ui=()=>{ci>1&&(oi+="{"+ci+"}")};return fi.forEach((mi,bi)=>{if(mi===fi[bi-1]){ci++;return}ui(),oi+=mi,ci=1}),ui(),oi},si=fi=>{let oi=pi(fi);return ni(oi)},ai=fi=>new Set(fi).size!==fi.length,li=fi=>(fi+"").replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu,"\\$1"),di=fi=>fi.reduce((oi,ci)=>Math.max(oi,yi(ci)),0),yi=fi=>pi(fi).length,pi=fi=>Array.from(fi);let vi=fi=>{if(fi.length===1)return[[fi]];let oi=[],ci=fi.substring(1);return vi(ci).forEach(function(mi){let bi=mi.slice(0);bi[0]=fi.charAt(0)+bi[0],oi.push(bi),bi=mi.slice(0),bi.unshift(fi.charAt(0)),oi.push(bi)}),oi};let Ei=[[0,65535]],Ai="[\u0300-\u036F\xB7\u02BE\u02BC]",Mi,Ti,Fi=3,en={},rn={"/":"\u2044\u2215","0":"\u07C0",a:"\u2C65\u0250\u0251",aa:"\uA733",ae:"\xE6\u01FD\u01E3",ao:"\uA735",au:"\uA737",av:"\uA739\uA73B",ay:"\uA73D",b:"\u0180\u0253\u0183",c:"\uA73F\u0188\u023C\u2184",d:"\u0111\u0257\u0256\u1D05\u018C\uABB7\u0501\u0266",e:"\u025B\u01DD\u1D07\u0247",f:"\uA77C\u0192",g:"\u01E5\u0260\uA7A1\u1D79\uA77F\u0262",h:"\u0127\u2C68\u2C76\u0265",i:"\u0268\u0131",j:"\u0249\u0237",k:"\u0199\u2C6A\uA741\uA743\uA745\uA7A3",l:"\u0142\u019A\u026B\u2C61\uA749\uA747\uA781\u026D",m:"\u0271\u026F\u03FB",n:"\uA7A5\u019E\u0272\uA791\u1D0E\u043B\u0509",o:"\xF8\u01FF\u0254\u0275\uA74B\uA74D\u1D11",oe:"\u0153",oi:"\u01A3",oo:"\uA74F",ou:"\u0223",p:"\u01A5\u1D7D\uA751\uA753\uA755\u03C1",q:"\uA757\uA759\u024B",r:"\u024D\u027D\uA75B\uA7A7\uA783",s:"\xDF\u023F\uA7A9\uA785\u0282",t:"\u0167\u01AD\u0288\u2C66\uA787",th:"\xFE",tz:"\uA729",u:"\u0289",v:"\u028B\uA75F\u028C",vy:"\uA761",w:"\u2C73",y:"\u01B4\u024F\u1EFF",z:"\u01B6\u0225\u0240\u2C6C\uA763",hv:"\u0195"};for(let fi in rn){let oi=rn[fi]||"";for(let ci=0;ci{Mi===void 0&&(Mi=vn(fi||Ei))},sn=(fi,oi="NFKD")=>fi.normalize(oi),on=fi=>pi(fi).reduce((oi,ci)=>oi+En(ci),""),En=fi=>(fi=sn(fi).toLowerCase().replace(ln,oi=>en[oi]||""),sn(fi,"NFC"));function*pn(fi){for(let[oi,ci]of fi)for(let ui=oi;ui<=ci;ui++){let mi=String.fromCharCode(ui),bi=on(mi);bi!=mi.toLowerCase()&&(bi.length>Fi||bi.length!=0&&(yield{folded:bi,composed:mi,code_point:ui}))}}let mn=fi=>{let oi={},ci=(ui,mi)=>{let bi=oi[ui]||new Set,_i=new RegExp("^"+si(bi)+"$","iu");mi.match(_i)||(bi.add(li(mi)),oi[ui]=bi)};for(let ui of pn(fi))ci(ui.folded,ui.folded),ci(ui.folded,ui.composed);return oi},vn=fi=>{let oi=mn(fi),ci={},ui=[];for(let bi in oi){let _i=oi[bi];_i&&(ci[bi]=si(_i)),bi.length>1&&ui.push(li(bi))}ui.sort((bi,_i)=>_i.length-bi.length);let mi=ni(ui);return Ti=new RegExp("^"+mi,"u"),ci},un=(fi,oi=1)=>{let ci=0;return fi=fi.map(ui=>(Mi[ui]&&(ci+=ui.length),Mi[ui]||ui)),ci>=oi?ri(fi):""},yn=(fi,oi=1)=>(oi=Math.max(oi,fi.length-1),ni(vi(fi).map(ci=>un(ci,oi)))),Cn=(fi,oi=!0)=>{let ci=fi.length>1?1:0;return ni(fi.map(ui=>{let mi=[],bi=oi?ui.length():ui.length()-1;for(let _i=0;_i{for(let ci of oi){if(ci.start!=fi.start||ci.end!=fi.end||ci.substrs.join("")!==fi.substrs.join(""))continue;let ui=fi.parts,mi=_i=>{for(let Si of ui){if(Si.start===_i.start&&Si.substr===_i.substr)return!1;if(!(_i.length==1||Si.length==1)&&(_i.startSi.start||Si.start<_i.start&&Si.end>_i.start))return!0}return!1};if(!(ci.parts.filter(mi).length>0))return!0}return!1};class Hi{constructor(){this.parts=[],this.substrs=[],this.start=0,this.end=0}add(oi){oi&&(this.parts.push(oi),this.substrs.push(oi.substr),this.start=Math.min(oi.start,this.start),this.end=Math.max(oi.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(oi,ci){let ui=new Hi,mi=JSON.parse(JSON.stringify(this.parts)),bi=mi.pop();for(let Di of mi)ui.add(Di);let _i=ci.substr.substring(0,oi-bi.start),Si=_i.length;return ui.add({start:bi.start,end:bi.start+Si,length:Si,substr:_i}),ui}}let Pi=fi=>{Yi(),fi=on(fi);let oi="",ci=[new Hi];for(let ui=0;ui0){Di=Di.sort((Wi,ji)=>Wi.length()-ji.length());for(let Wi of Di)Ni(Wi,ci)||ci.push(Wi);continue}if(ui>0&&Oi.size==1&&!Oi.has("3")){oi+=Cn(ci,!1);let Wi=new Hi,ji=ci[0];ji&&Wi.add(ji.last()),ci=[Wi]}}return oi+=Cn(ci,!0),oi};let $i=(fi,oi)=>{if(!!fi)return fi[oi]},gn=(fi,oi)=>{if(!!fi){for(var ci,ui=oi.split(".");(ci=ui.shift())&&(fi=fi[ci]););return fi}},tn=(fi,oi,ci)=>{var ui,mi;return!fi||(fi=fi+"",oi.regex==null)||(mi=fi.search(oi.regex),mi===-1)?0:(ui=oi.string.length/fi.length,mi===0&&(ui+=.5),ui*ci)},Bi=(fi,oi)=>{var ci=fi[oi];if(typeof ci=="function")return ci;ci&&!Array.isArray(ci)&&(fi[oi]=[ci])},Li=(fi,oi)=>{if(Array.isArray(fi))fi.forEach(oi);else for(var ci in fi)fi.hasOwnProperty(ci)&&oi(fi[ci],ci)},zi=(fi,oi)=>typeof fi=="number"&&typeof oi=="number"?fi>oi?1:fioi?1:oi>fi?-1:0);class Ji{constructor(oi,ci){this.items=void 0,this.settings=void 0,this.items=oi,this.settings=ci||{diacritics:!0}}tokenize(oi,ci,ui){if(!oi||!oi.length)return[];let mi=[],bi=oi.split(/\s+/);var _i;return ui&&(_i=new RegExp("^("+Object.keys(ui).map(li).join("|")+"):(.*)$")),bi.forEach(Si=>{let Di,Oi=null,Wi=null;_i&&(Di=Si.match(_i))&&(Oi=Di[1],Si=Di[2]),Si.length>0&&(this.settings.diacritics?Wi=Pi(Si)||null:Wi=li(Si),Wi&&ci&&(Wi="\\b"+Wi)),mi.push({string:Si,regex:Wi?new RegExp(Wi,"iu"):null,field:Oi})}),mi}getScoreFunction(oi,ci){var ui=this.prepareSearch(oi,ci);return this._getScoreFunction(ui)}_getScoreFunction(oi){let ci=oi.tokens,ui=ci.length;if(!ui)return function(){return 0};let mi=oi.options.fields,bi=oi.weights,_i=mi.length,Si=oi.getAttrFn;if(!_i)return function(){return 1};let Di=function(){return _i===1?function(Oi,Wi){let ji=mi[0].field;return tn(Si(Wi,ji),Oi,bi[ji]||1)}:function(Oi,Wi){var ji=0;if(Oi.field){let Zi=Si(Wi,Oi.field);!Oi.regex&&Zi?ji+=1/_i:ji+=tn(Zi,Oi,1)}else Li(bi,(Zi,In)=>{ji+=tn(Si(Wi,In),Oi,Zi)});return ji/_i}}();return ui===1?function(Oi){return Di(ci[0],Oi)}:oi.options.conjunction==="and"?function(Oi){var Wi,ji=0;for(let Zi of ci){if(Wi=Di(Zi,Oi),Wi<=0)return 0;ji+=Wi}return ji/ui}:function(Oi){var Wi=0;return Li(ci,ji=>{Wi+=Di(ji,Oi)}),Wi/ui}}getSortFunction(oi,ci){var ui=this.prepareSearch(oi,ci);return this._getSortFunction(ui)}_getSortFunction(oi){var ci,ui=[];let mi=this,bi=oi.options,_i=!oi.query&&bi.sort_empty?bi.sort_empty:bi.sort;if(typeof _i=="function")return _i.bind(this);let Si=function(Wi,ji){return Wi==="$score"?ji.score:oi.getAttrFn(mi.items[ji.id],Wi)};if(_i)for(let Oi of _i)(oi.query||Oi.field!=="$score")&&ui.push(Oi);if(oi.query){ci=!0;for(let Oi of ui)if(Oi.field==="$score"){ci=!1;break}ci&&ui.unshift({field:"$score",direction:"desc"})}else ui=ui.filter(Oi=>Oi.field!=="$score");return ui.length?function(Oi,Wi){var ji,Zi;for(let In of ui)if(Zi=In.field,ji=(In.direction==="desc"?-1:1)*zi(Si(Zi,Oi),Si(Zi,Wi)),ji)return ji;return 0}:null}prepareSearch(oi,ci){let ui={};var mi=Object.assign({},ci);if(Bi(mi,"sort"),Bi(mi,"sort_empty"),mi.fields){Bi(mi,"fields");let bi=[];mi.fields.forEach(_i=>{typeof _i=="string"&&(_i={field:_i,weight:1}),bi.push(_i),ui[_i.field]="weight"in _i?_i.weight:1}),mi.fields=bi}return{options:mi,query:oi.toLowerCase().trim(),tokens:this.tokenize(oi,mi.respect_word_boundaries,ui),total:0,items:[],weights:ui,getAttrFn:mi.nesting?gn:$i}}search(oi,ci){var ui=this,mi,bi;bi=this.prepareSearch(oi,ci),ci=bi.options,oi=bi.query;let _i=ci.score||ui._getScoreFunction(bi);oi.length?Li(ui.items,(Di,Oi)=>{mi=_i(Di),(ci.filter===!1||mi>0)&&bi.items.push({score:mi,id:Oi})}):Li(ui.items,(Di,Oi)=>{bi.items.push({score:1,id:Oi})});let Si=ui._getSortFunction(bi);return Si&&bi.items.sort(Si),bi.total=bi.items.length,typeof ci.limit=="number"&&(bi.items=bi.items.slice(0,ci.limit)),bi}}let Vi=(fi,oi)=>{if(Array.isArray(fi))fi.forEach(oi);else for(var ci in fi)fi.hasOwnProperty(ci)&&oi(fi[ci],ci)},Ri=fi=>{if(fi.jquery)return fi[0];if(fi instanceof HTMLElement)return fi;if(Sn(fi)){var oi=document.createElement("template");return oi.innerHTML=fi.trim(),oi.content.firstChild}return document.querySelector(fi)},Sn=fi=>typeof fi=="string"&&fi.indexOf("<")>-1,Kn=fi=>fi.replace(/['"\\]/g,"\\$&"),$n=(fi,oi)=>{var ci=document.createEvent("HTMLEvents");ci.initEvent(oi,!0,!1),fi.dispatchEvent(ci)},Bn=(fi,oi)=>{Object.assign(fi.style,oi)},Gn=(fi,...oi)=>{var ci=Cs(oi);fi=zn(fi),fi.map(ui=>{ci.map(mi=>{ui.classList.add(mi)})})},Xn=(fi,...oi)=>{var ci=Cs(oi);fi=zn(fi),fi.map(ui=>{ci.map(mi=>{ui.classList.remove(mi)})})},Cs=fi=>{var oi=[];return Vi(fi,ci=>{typeof ci=="string"&&(ci=ci.trim().split(/[\11\12\14\15\40]/)),Array.isArray(ci)&&(oi=oi.concat(ci))}),oi.filter(Boolean)},zn=fi=>(Array.isArray(fi)||(fi=[fi]),fi),ps=(fi,oi,ci)=>{if(!(ci&&!ci.contains(fi)))for(;fi&&fi.matches;){if(fi.matches(oi))return fi;fi=fi.parentNode}},ms=(fi,oi=0)=>oi>0?fi[fi.length-1]:fi[0],Qs=fi=>Object.keys(fi).length===0,Wn=(fi,oi)=>{if(!fi)return-1;oi=oi||fi.nodeName;for(var ci=0;fi=fi.previousElementSibling;)fi.matches(oi)&&ci++;return ci},_n=(fi,oi)=>{Vi(oi,(ci,ui)=>{ci==null?fi.removeAttribute(ui):fi.setAttribute(ui,""+ci)})},Qr=(fi,oi)=>{fi.parentNode&&fi.parentNode.replaceChild(oi,fi)},ls=(fi,oi)=>{if(oi===null)return;if(typeof oi=="string"){if(!oi.length)return;oi=new RegExp(oi,"i")}let ci=bi=>{var _i=bi.data.match(oi);if(_i&&bi.data.length>0){var Si=document.createElement("span");Si.className="highlight";var Di=bi.splitText(_i.index);Di.splitText(_i[0].length);var Oi=Di.cloneNode(!0);return Si.appendChild(Oi),Qr(Di,Si),1}return 0},ui=bi=>{bi.nodeType===1&&bi.childNodes&&!/(script|style)/i.test(bi.tagName)&&(bi.className!=="highlight"||bi.tagName!=="SPAN")&&Array.from(bi.childNodes).forEach(_i=>{mi(_i)})},mi=bi=>bi.nodeType===3?ci(bi):(ui(bi),0);mi(fi)},Xs=fi=>{var oi=fi.querySelectorAll("span.highlight");Array.prototype.forEach.call(oi,function(ci){var ui=ci.parentNode;ui.replaceChild(ci.firstChild,ci),ui.normalize()})},mo=65,Js=13,Fs=27,Ss=37,Po=38,Zs=39,Ho=40,ga=8,$l=46,va=9,Fo=(typeof navigator=="undefined"?!1:/Mac/.test(navigator.userAgent))?"metaKey":"ctrlKey";var Wa={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:null,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,shouldOpen:null,maxOptions:50,maxItems:null,hideSelected:null,duplicates:!1,addPrecedence:!1,selectOnTab:!1,preload:null,allowEmptyOption:!1,refreshThrottle:300,loadThrottle:300,loadingClass:"loading",dataAttr:null,optgroupField:"optgroup",valueField:"value",labelField:"text",disabledField:"disabled",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"ts-wrapper",controlClass:"ts-control",dropdownClass:"ts-dropdown",dropdownContentClass:"ts-dropdown-content",itemClass:"item",optionClass:"option",dropdownParent:null,controlInput:'',copyClassesToDropdown:!1,placeholder:null,hidePlaceholder:null,shouldLoad:function(fi){return fi.length>0},render:{}};let xn=fi=>typeof fi=="undefined"||fi===null?null:eo(fi),eo=fi=>typeof fi=="boolean"?fi?"1":"0":fi+"",to=fi=>(fi+"").replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),Bl=(fi,oi)=>oi>0?setTimeout(fi,oi):(fi.call(null),null),$o=(fi,oi)=>{var ci;return function(ui,mi){var bi=this;ci&&(bi.loading=Math.max(bi.loading-1,0),clearTimeout(ci)),ci=setTimeout(function(){ci=null,bi.loadedSearches[ui]=!0,fi.call(bi,ui,mi)},oi)}},jo=(fi,oi,ci)=>{var ui,mi=fi.trigger,bi={};fi.trigger=function(){var _i=arguments[0];if(oi.indexOf(_i)!==-1)bi[_i]=arguments;else return mi.apply(fi,arguments)},ci.apply(fi,[]),fi.trigger=mi;for(ui of oi)ui in bi&&mi.apply(fi,bi[ui])},As=fi=>({start:fi.selectionStart||0,length:(fi.selectionEnd||0)-(fi.selectionStart||0)}),Dn=(fi,oi=!1)=>{fi&&(fi.preventDefault(),oi&&fi.stopPropagation())},Ln=(fi,oi,ci,ui)=>{fi.addEventListener(oi,ci,ui)},hi=(fi,oi)=>{if(!oi||!oi[fi])return!1;var ci=(oi.altKey?1:0)+(oi.ctrlKey?1:0)+(oi.shiftKey?1:0)+(oi.metaKey?1:0);return ci===1},gi=(fi,oi)=>{let ci=fi.getAttribute("id");return ci||(fi.setAttribute("id",oi),oi)},wi=fi=>fi.replace(/[\\"']/g,"\\$&"),Ci=(fi,oi)=>{oi&&fi.append(oi)};function Ii(fi,oi){var ci=Object.assign({},Wa,oi),ui=ci.dataAttr,mi=ci.labelField,bi=ci.valueField,_i=ci.disabledField,Si=ci.optgroupField,Di=ci.optgroupLabelField,Oi=ci.optgroupValueField,Wi=fi.tagName.toLowerCase(),ji=fi.getAttribute("placeholder")||fi.getAttribute("data-placeholder");if(!ji&&!ci.allowEmptyOption){let An=fi.querySelector('option[value=""]');An&&(ji=An.textContent)}var Zi={placeholder:ji,options:[],optgroups:[],items:[],maxItems:null},In=()=>{var An,Hn=Zi.options,Nn={},dn=1;let Vn=0;var ks=Tn=>{var Mn=Object.assign({},Tn.dataset),wn=ui&&Mn[ui];return typeof wn=="string"&&wn.length&&(Mn=Object.assign(Mn,JSON.parse(wn))),Mn},Ua=(Tn,Mn)=>{var wn=xn(Tn.value);if(wn!=null&&!(!wn&&!ci.allowEmptyOption)){if(Nn.hasOwnProperty(wn)){if(Mn){var Zr=Nn[wn][Si];Zr?Array.isArray(Zr)?Zr.push(Mn):Nn[wn][Si]=[Zr,Mn]:Nn[wn][Si]=Mn}}else{var Rn=ks(Tn);Rn[mi]=Rn[mi]||Tn.textContent,Rn[bi]=Rn[bi]||wn,Rn[_i]=Rn[_i]||Tn.disabled,Rn[Si]=Rn[Si]||Mn,Rn.$option=Tn,Rn.$order=Rn.$order||++Vn,Nn[wn]=Rn,Hn.push(Rn)}Tn.selected&&Zi.items.push(wn)}},Bo=Tn=>{var Mn,wn;wn=ks(Tn),wn[Di]=wn[Di]||Tn.getAttribute("label")||"",wn[Oi]=wn[Oi]||dn++,wn[_i]=wn[_i]||Tn.disabled,wn.$order=wn.$order||++Vn,Zi.optgroups.push(wn),Mn=wn[Oi],Vi(Tn.children,Zr=>{Ua(Zr,Mn)})};Zi.maxItems=fi.hasAttribute("multiple")?null:1,Vi(fi.children,Tn=>{An=Tn.tagName.toLowerCase(),An==="optgroup"?Bo(Tn):An==="option"&&Ua(Tn)})},Qi=()=>{let An=fi.getAttribute(ui);if(An)Zi.options=JSON.parse(An),Vi(Zi.options,Nn=>{Zi.items.push(Nn[bi])});else{var Hn=fi.value.trim()||"";if(!ci.allowEmptyOption&&!Hn.length)return;let Nn=Hn.split(ci.delimiter);Vi(Nn,dn=>{let Vn={};Vn[mi]=dn,Vn[bi]=dn,Zi.options.push(Vn)}),Zi.items=Nn}};return Wi==="select"?In():Qi(),Object.assign({},Wa,Zi,oi)}var qi=0;class nn extends ei(ti){constructor(oi,ci){super();this.control_input=void 0,this.wrapper=void 0,this.dropdown=void 0,this.control=void 0,this.dropdown_content=void 0,this.focus_node=void 0,this.order=0,this.settings=void 0,this.input=void 0,this.tabIndex=void 0,this.is_select_tag=void 0,this.rtl=void 0,this.inputId=void 0,this._destroy=void 0,this.sifter=void 0,this.isOpen=!1,this.isDisabled=!1,this.isReadOnly=!1,this.isRequired=void 0,this.isInvalid=!1,this.isValid=!0,this.isLocked=!1,this.isFocused=!1,this.isInputHidden=!1,this.isSetup=!1,this.ignoreFocus=!1,this.ignoreHover=!1,this.hasOptions=!1,this.currentResults=void 0,this.lastValue="",this.caretPos=0,this.loading=0,this.loadedSearches={},this.activeOption=null,this.activeItems=[],this.optgroups={},this.options={},this.userOptions={},this.items=[],this.refreshTimeout=null,qi++;var ui,mi=Ri(oi);if(mi.tomselect)throw new Error("Tom Select already initialized on this element");mi.tomselect=this;var bi=window.getComputedStyle&&window.getComputedStyle(mi,null);ui=bi.getPropertyValue("direction");let _i=Ii(mi,ci);this.settings=_i,this.input=mi,this.tabIndex=mi.tabIndex||0,this.is_select_tag=mi.tagName.toLowerCase()==="select",this.rtl=/rtl/i.test(ui),this.inputId=gi(mi,"tomselect-"+qi),this.isRequired=mi.required,this.sifter=new Ji(this.options,{diacritics:_i.diacritics}),_i.mode=_i.mode||(_i.maxItems===1?"single":"multi"),typeof _i.hideSelected!="boolean"&&(_i.hideSelected=_i.mode==="multi"),typeof _i.hidePlaceholder!="boolean"&&(_i.hidePlaceholder=_i.mode!=="multi");var Si=_i.createFilter;typeof Si!="function"&&(typeof Si=="string"&&(Si=new RegExp(Si)),Si instanceof RegExp?_i.createFilter=Hn=>Si.test(Hn):_i.createFilter=Hn=>this.settings.duplicates||!this.options[Hn]),this.initializePlugins(_i.plugins),this.setupCallbacks(),this.setupTemplates();let Di=Ri("
"),Oi=Ri("
"),Wi=this._render("dropdown"),ji=Ri('
'),Zi=this.input.getAttribute("class")||"",In=_i.mode;var Qi;if(Gn(Di,_i.wrapperClass,Zi,In),Gn(Oi,_i.controlClass),Ci(Di,Oi),Gn(Wi,_i.dropdownClass,In),_i.copyClassesToDropdown&&Gn(Wi,Zi),Gn(ji,_i.dropdownContentClass),Ci(Wi,ji),Ri(_i.dropdownParent||Di).appendChild(Wi),Sn(_i.controlInput)){Qi=Ri(_i.controlInput);var An=["autocorrect","autocapitalize","autocomplete","spellcheck"];Li(An,Hn=>{mi.getAttribute(Hn)&&_n(Qi,{[Hn]:mi.getAttribute(Hn)})}),Qi.tabIndex=-1,Oi.appendChild(Qi),this.focus_node=Qi}else _i.controlInput?(Qi=Ri(_i.controlInput),this.focus_node=Qi):(Qi=Ri(""),this.focus_node=Oi);this.wrapper=Di,this.dropdown=Wi,this.dropdown_content=ji,this.control=Oi,this.control_input=Qi,this.setup()}setup(){let oi=this,ci=oi.settings,ui=oi.control_input,mi=oi.dropdown,bi=oi.dropdown_content,_i=oi.wrapper,Si=oi.control,Di=oi.input,Oi=oi.focus_node,Wi={passive:!0},ji=oi.inputId+"-ts-dropdown";_n(bi,{id:ji}),_n(Oi,{role:"combobox","aria-haspopup":"listbox","aria-expanded":"false","aria-controls":ji});let Zi=gi(Oi,oi.inputId+"-ts-control"),In="label[for='"+Kn(oi.inputId)+"']",Qi=document.querySelector(In),An=oi.focus.bind(oi);if(Qi){Ln(Qi,"click",An),_n(Qi,{for:Zi});let dn=gi(Qi,oi.inputId+"-ts-label");_n(Oi,{"aria-labelledby":dn}),_n(bi,{"aria-labelledby":dn})}if(_i.style.width=Di.style.width,oi.plugins.names.length){let dn="plugin-"+oi.plugins.names.join(" plugin-");Gn([_i,mi],dn)}(ci.maxItems===null||ci.maxItems>1)&&oi.is_select_tag&&_n(Di,{multiple:"multiple"}),ci.placeholder&&_n(ui,{placeholder:ci.placeholder}),!ci.splitOn&&ci.delimiter&&(ci.splitOn=new RegExp("\\s*"+li(ci.delimiter)+"+\\s*")),ci.load&&ci.loadThrottle&&(ci.load=$o(ci.load,ci.loadThrottle)),Ln(mi,"mousemove",()=>{oi.ignoreHover=!1}),Ln(mi,"mouseenter",dn=>{var Vn=ps(dn.target,"[data-selectable]",mi);Vn&&oi.onOptionHover(dn,Vn)},{capture:!0}),Ln(mi,"click",dn=>{let Vn=ps(dn.target,"[data-selectable]");Vn&&(oi.onOptionSelect(dn,Vn),Dn(dn,!0))}),Ln(Si,"click",dn=>{var Vn=ps(dn.target,"[data-ts-item]",Si);if(Vn&&oi.onItemSelect(dn,Vn)){Dn(dn,!0);return}ui.value==""&&(oi.onClick(),Dn(dn,!0))}),Ln(Oi,"keydown",dn=>oi.onKeyDown(dn)),Ln(ui,"keypress",dn=>oi.onKeyPress(dn)),Ln(ui,"input",dn=>oi.onInput(dn)),Ln(Oi,"blur",dn=>oi.onBlur(dn)),Ln(Oi,"focus",dn=>oi.onFocus(dn)),Ln(ui,"paste",dn=>oi.onPaste(dn));let Hn=dn=>{let Vn=dn.composedPath()[0];if(!_i.contains(Vn)&&!mi.contains(Vn)){oi.isFocused&&oi.blur(),oi.inputState();return}Vn==ui&&oi.isOpen?dn.stopPropagation():Dn(dn,!0)},Nn=()=>{oi.isOpen&&oi.positionDropdown()};Ln(document,"mousedown",Hn),Ln(window,"scroll",Nn,Wi),Ln(window,"resize",Nn,Wi),this._destroy=()=>{document.removeEventListener("mousedown",Hn),window.removeEventListener("scroll",Nn),window.removeEventListener("resize",Nn),Qi&&Qi.removeEventListener("click",An)},this.revertSettings={innerHTML:Di.innerHTML,tabIndex:Di.tabIndex},Di.tabIndex=-1,Di.insertAdjacentElement("afterend",oi.wrapper),oi.sync(!1),ci.items=[],delete ci.optgroups,delete ci.options,Ln(Di,"invalid",()=>{oi.isValid&&(oi.isValid=!1,oi.isInvalid=!0,oi.refreshState())}),oi.updateOriginalInput(),oi.refreshItems(),oi.close(!1),oi.inputState(),oi.isSetup=!0,Di.disabled?oi.disable():Di.readOnly?oi.setReadOnly(!0):oi.enable(),oi.on("change",this.onChange),Gn(Di,"tomselected","ts-hidden-accessible"),oi.trigger("initialize"),ci.preload===!0&&oi.preload()}setupOptions(oi=[],ci=[]){this.addOptions(oi),Li(ci,ui=>{this.registerOptionGroup(ui)})}setupTemplates(){var oi=this,ci=oi.settings.labelField,ui=oi.settings.optgroupLabelField,mi={optgroup:bi=>{let _i=document.createElement("div");return _i.className="optgroup",_i.appendChild(bi.options),_i},optgroup_header:(bi,_i)=>'
'+_i(bi[ui])+"
",option:(bi,_i)=>"
"+_i(bi[ci])+"
",item:(bi,_i)=>"
"+_i(bi[ci])+"
",option_create:(bi,_i)=>'
Add '+_i(bi.input)+"
",no_results:()=>'
No results found
',loading:()=>'
',not_loading:()=>{},dropdown:()=>"
"};oi.settings.render=Object.assign({},mi,oi.settings.render)}setupCallbacks(){var oi,ci,ui={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",item_select:"onItemSelect",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(oi in ui)ci=this.settings[ui[oi]],ci&&this.on(oi,ci)}sync(oi=!0){let ci=this,ui=oi?Ii(ci.input,{delimiter:ci.settings.delimiter}):ci.settings;ci.setupOptions(ui.options,ui.optgroups),ci.setValue(ui.items||[],!0),ci.lastQuery=null}onClick(){var oi=this;if(oi.activeItems.length>0){oi.clearActiveItems(),oi.focus();return}oi.isFocused&&oi.isOpen?oi.blur():oi.focus()}onMouseDown(){}onChange(){$n(this.input,"input"),$n(this.input,"change")}onPaste(oi){var ci=this;if(ci.isInputHidden||ci.isLocked){Dn(oi);return}!ci.settings.splitOn||setTimeout(()=>{var ui=ci.inputValue();if(!!ui.match(ci.settings.splitOn)){var mi=ui.trim().split(ci.settings.splitOn);Li(mi,bi=>{xn(bi)&&(this.options[bi]?ci.addItem(bi):ci.createItem(bi))})}},0)}onKeyPress(oi){var ci=this;if(ci.isLocked){Dn(oi);return}var ui=String.fromCharCode(oi.keyCode||oi.which);if(ci.settings.create&&ci.settings.mode==="multi"&&ui===ci.settings.delimiter){ci.createItem(),Dn(oi);return}}onKeyDown(oi){var ci=this;if(ci.ignoreHover=!0,ci.isLocked){oi.keyCode!==va&&Dn(oi);return}switch(oi.keyCode){case mo:if(hi(Fo,oi)&&ci.control_input.value==""){Dn(oi),ci.selectAll();return}break;case Fs:ci.isOpen&&(Dn(oi,!0),ci.close()),ci.clearActiveItems();return;case Ho:if(!ci.isOpen&&ci.hasOptions)ci.open();else if(ci.activeOption){let ui=ci.getAdjacent(ci.activeOption,1);ui&&ci.setActiveOption(ui)}Dn(oi);return;case Po:if(ci.activeOption){let ui=ci.getAdjacent(ci.activeOption,-1);ui&&ci.setActiveOption(ui)}Dn(oi);return;case Js:ci.canSelect(ci.activeOption)?(ci.onOptionSelect(oi,ci.activeOption),Dn(oi)):(ci.settings.create&&ci.createItem()||document.activeElement==ci.control_input&&ci.isOpen)&&Dn(oi);return;case Ss:ci.advanceSelection(-1,oi);return;case Zs:ci.advanceSelection(1,oi);return;case va:ci.settings.selectOnTab&&(ci.canSelect(ci.activeOption)&&(ci.onOptionSelect(oi,ci.activeOption),Dn(oi)),ci.settings.create&&ci.createItem()&&Dn(oi));return;case ga:case $l:ci.deleteSelection(oi);return}ci.isInputHidden&&!hi(Fo,oi)&&Dn(oi)}onInput(oi){if(this.isLocked)return;let ci=this.inputValue();if(this.lastValue!==ci){if(this.lastValue=ci,ci==""){this._onInput();return}this.refreshTimeout&&clearTimeout(this.refreshTimeout),this.refreshTimeout=Bl(()=>{this.refreshTimeout=null,this._onInput()},this.settings.refreshThrottle)}}_onInput(){let oi=this.lastValue;this.settings.shouldLoad.call(this,oi)&&this.load(oi),this.refreshOptions(),this.trigger("type",oi)}onOptionHover(oi,ci){this.ignoreHover||this.setActiveOption(ci,!1)}onFocus(oi){var ci=this,ui=ci.isFocused;if(ci.isDisabled||ci.isReadOnly){ci.blur(),Dn(oi);return}ci.ignoreFocus||(ci.isFocused=!0,ci.settings.preload==="focus"&&ci.preload(),ui||ci.trigger("focus"),ci.activeItems.length||(ci.inputState(),ci.refreshOptions(!!ci.settings.openOnFocus)),ci.refreshState())}onBlur(oi){if(document.hasFocus()!==!1){var ci=this;if(!!ci.isFocused){ci.isFocused=!1,ci.ignoreFocus=!1;var ui=()=>{ci.close(),ci.setActiveItem(),ci.setCaret(ci.items.length),ci.trigger("blur")};ci.settings.create&&ci.settings.createOnBlur?ci.createItem(null,ui):ui()}}}onOptionSelect(oi,ci){var ui,mi=this;ci.parentElement&&ci.parentElement.matches("[data-disabled]")||(ci.classList.contains("create")?mi.createItem(null,()=>{mi.settings.closeAfterSelect&&mi.close()}):(ui=ci.dataset.value,typeof ui!="undefined"&&(mi.lastQuery=null,mi.addItem(ui),mi.settings.closeAfterSelect&&mi.close(),!mi.settings.hideSelected&&oi.type&&/click/.test(oi.type)&&mi.setActiveOption(ci))))}canSelect(oi){return!!(this.isOpen&&oi&&this.dropdown_content.contains(oi))}onItemSelect(oi,ci){var ui=this;return!ui.isLocked&&ui.settings.mode==="multi"?(Dn(oi),ui.setActiveItem(ci,oi),!0):!1}canLoad(oi){return!(!this.settings.load||this.loadedSearches.hasOwnProperty(oi))}load(oi){let ci=this;if(!ci.canLoad(oi))return;Gn(ci.wrapper,ci.settings.loadingClass),ci.loading++;let ui=ci.loadCallback.bind(ci);ci.settings.load.call(ci,oi,ui)}loadCallback(oi,ci){let ui=this;ui.loading=Math.max(ui.loading-1,0),ui.lastQuery=null,ui.clearActiveOption(),ui.setupOptions(oi,ci),ui.refreshOptions(ui.isFocused&&!ui.isInputHidden),ui.loading||Xn(ui.wrapper,ui.settings.loadingClass),ui.trigger("load",oi,ci)}preload(){var oi=this.wrapper.classList;oi.contains("preloaded")||(oi.add("preloaded"),this.load(""))}setTextboxValue(oi=""){var ci=this.control_input,ui=ci.value!==oi;ui&&(ci.value=oi,$n(ci,"update"),this.lastValue=oi)}getValue(){return this.is_select_tag&&this.input.hasAttribute("multiple")?this.items:this.items.join(this.settings.delimiter)}setValue(oi,ci){var ui=ci?[]:["change"];jo(this,ui,()=>{this.clear(ci),this.addItems(oi,ci)})}setMaxItems(oi){oi===0&&(oi=null),this.settings.maxItems=oi,this.refreshState()}setActiveItem(oi,ci){var ui=this,mi,bi,_i,Si,Di,Oi;if(ui.settings.mode!=="single"){if(!oi){ui.clearActiveItems(),ui.isFocused&&ui.inputState();return}if(mi=ci&&ci.type.toLowerCase(),mi==="click"&&hi("shiftKey",ci)&&ui.activeItems.length){for(Oi=ui.getLastActive(),_i=Array.prototype.indexOf.call(ui.control.children,Oi),Si=Array.prototype.indexOf.call(ui.control.children,oi),_i>Si&&(Di=_i,_i=Si,Si=Di),bi=_i;bi<=Si;bi++)oi=ui.control.children[bi],ui.activeItems.indexOf(oi)===-1&&ui.setActiveItemClass(oi);Dn(ci)}else mi==="click"&&hi(Fo,ci)||mi==="keydown"&&hi("shiftKey",ci)?oi.classList.contains("active")?ui.removeActiveItem(oi):ui.setActiveItemClass(oi):(ui.clearActiveItems(),ui.setActiveItemClass(oi));ui.inputState(),ui.isFocused||ui.focus()}}setActiveItemClass(oi){let ci=this,ui=ci.control.querySelector(".last-active");ui&&Xn(ui,"last-active"),Gn(oi,"active last-active"),ci.trigger("item_select",oi),ci.activeItems.indexOf(oi)==-1&&ci.activeItems.push(oi)}removeActiveItem(oi){var ci=this.activeItems.indexOf(oi);this.activeItems.splice(ci,1),Xn(oi,"active")}clearActiveItems(){Xn(this.activeItems,"active"),this.activeItems=[]}setActiveOption(oi,ci=!0){oi!==this.activeOption&&(this.clearActiveOption(),!!oi&&(this.activeOption=oi,_n(this.focus_node,{"aria-activedescendant":oi.getAttribute("id")}),_n(oi,{"aria-selected":"true"}),Gn(oi,"active"),ci&&this.scrollToOption(oi)))}scrollToOption(oi,ci){if(!oi)return;let ui=this.dropdown_content,mi=ui.clientHeight,bi=ui.scrollTop||0,_i=oi.offsetHeight,Si=oi.getBoundingClientRect().top-ui.getBoundingClientRect().top+bi;Si+_i>mi+bi?this.scroll(Si-mi+_i,ci):Si{oi.setActiveItemClass(ui)}))}inputState(){var oi=this;!oi.control.contains(oi.control_input)||(_n(oi.control_input,{placeholder:oi.settings.placeholder}),oi.activeItems.length>0||!oi.isFocused&&oi.settings.hidePlaceholder&&oi.items.length>0?(oi.setTextboxValue(),oi.isInputHidden=!0):(oi.settings.hidePlaceholder&&oi.items.length>0&&_n(oi.control_input,{placeholder:""}),oi.isInputHidden=!1),oi.wrapper.classList.toggle("input-hidden",oi.isInputHidden))}inputValue(){return this.control_input.value.trim()}focus(){var oi=this;oi.isDisabled||oi.isReadOnly||(oi.ignoreFocus=!0,oi.control_input.offsetWidth?oi.control_input.focus():oi.focus_node.focus(),setTimeout(()=>{oi.ignoreFocus=!1,oi.onFocus()},0))}blur(){this.focus_node.blur(),this.onBlur()}getScoreFunction(oi){return this.sifter.getScoreFunction(oi,this.getSearchOptions())}getSearchOptions(){var oi=this.settings,ci=oi.sortField;return typeof oi.sortField=="string"&&(ci=[{field:oi.sortField}]),{fields:oi.searchField,conjunction:oi.searchConjunction,sort:ci,nesting:oi.nesting}}search(oi){var ci,ui,mi=this,bi=this.getSearchOptions();if(mi.settings.score&&(ui=mi.settings.score.call(mi,oi),typeof ui!="function"))throw new Error('Tom Select "score" setting must be a function that returns a function');return oi!==mi.lastQuery?(mi.lastQuery=oi,ci=mi.sifter.search(oi,Object.assign(bi,{score:ui})),mi.currentResults=ci):ci=Object.assign({},mi.currentResults),mi.settings.hideSelected&&(ci.items=ci.items.filter(_i=>{let Si=xn(_i.id);return!(Si&&mi.items.indexOf(Si)!==-1)})),ci}refreshOptions(oi=!0){var ci,ui,mi,bi,_i,Si,Di,Oi,Wi,ji;let Zi={},In=[];var Qi=this,An=Qi.inputValue();let Hn=An===Qi.lastQuery||An==""&&Qi.lastQuery==null;var Nn=Qi.search(An),dn=null,Vn=Qi.settings.shouldOpen||!1,ks=Qi.dropdown_content;Hn&&(dn=Qi.activeOption,dn&&(Wi=dn.closest("[data-group]"))),bi=Nn.items.length,typeof Qi.settings.maxOptions=="number"&&(bi=Math.min(bi,Qi.settings.maxOptions)),bi>0&&(Vn=!0);let Ua=(Tn,Mn)=>{let wn=Zi[Tn];if(wn!==void 0){let Rn=In[wn];if(Rn!==void 0)return[wn,Rn.fragment]}let Zr=document.createDocumentFragment();return wn=In.length,In.push({fragment:Zr,order:Mn,optgroup:Tn}),[wn,Zr]};for(ci=0;ci0&&(Rn=Rn.cloneNode(!0),_n(Rn,{id:wn.$id+"-clone-"+ui,"aria-selected":null}),Rn.classList.add("ts-cloned"),Xn(Rn,"active"),Qi.activeOption&&Qi.activeOption.dataset.value==Mn&&Wi&&Wi.dataset.group===_i.toString()&&(dn=Rn)),ch.appendChild(Rn),_i!=""&&(Zi[_i]=lh)}}Qi.settings.lockOptgroupOrder&&In.sort((Tn,Mn)=>Tn.order-Mn.order),Di=document.createDocumentFragment(),Li(In,Tn=>{let Mn=Tn.fragment,wn=Tn.optgroup;if(!Mn||!Mn.children.length)return;let Zr=Qi.optgroups[wn];if(Zr!==void 0){let Rn=document.createDocumentFragment(),Ya=Qi.render("optgroup_header",Zr);Ci(Rn,Ya),Ci(Rn,Mn);let Ka=Qi.render("optgroup",{group:Zr,options:Rn});Ci(Di,Ka)}else Ci(Di,Mn)}),ks.innerHTML="",Ci(ks,Di),Qi.settings.highlight&&(Xs(ks),Nn.query.length&&Nn.tokens.length&&Li(Nn.tokens,Tn=>{ls(ks,Tn.regex)}));var Bo=Tn=>{let Mn=Qi.render(Tn,{input:An});return Mn&&(Vn=!0,ks.insertBefore(Mn,ks.firstChild)),Mn};if(Qi.loading?Bo("loading"):Qi.settings.shouldLoad.call(Qi,An)?Nn.items.length===0&&Bo("no_results"):Bo("not_loading"),Oi=Qi.canCreate(An),Oi&&(ji=Bo("option_create")),Qi.hasOptions=Nn.items.length>0||Oi,Vn){if(Nn.items.length>0){if(!dn&&Qi.settings.mode==="single"&&Qi.items[0]!=null&&(dn=Qi.getOption(Qi.items[0])),!ks.contains(dn)){let Tn=0;ji&&!Qi.settings.addPrecedence&&(Tn=1),dn=Qi.selectable()[Tn]}}else ji&&(dn=ji);oi&&!Qi.isOpen&&(Qi.open(),Qi.scrollToOption(dn,"auto")),Qi.setActiveOption(dn)}else Qi.clearActiveOption(),oi&&Qi.isOpen&&Qi.close(!1)}selectable(){return this.dropdown_content.querySelectorAll("[data-selectable]")}addOption(oi,ci=!1){let ui=this;if(Array.isArray(oi))return ui.addOptions(oi,ci),!1;let mi=xn(oi[ui.settings.valueField]);return mi===null||ui.options.hasOwnProperty(mi)?!1:(oi.$order=oi.$order||++ui.order,oi.$id=ui.inputId+"-opt-"+oi.$order,ui.options[mi]=oi,ui.lastQuery=null,ci&&(ui.userOptions[mi]=ci,ui.trigger("option_add",mi,oi)),mi)}addOptions(oi,ci=!1){Li(oi,ui=>{this.addOption(ui,ci)})}registerOption(oi){return this.addOption(oi)}registerOptionGroup(oi){var ci=xn(oi[this.settings.optgroupValueField]);return ci===null?!1:(oi.$order=oi.$order||++this.order,this.optgroups[ci]=oi,ci)}addOptionGroup(oi,ci){var ui;ci[this.settings.optgroupValueField]=oi,(ui=this.registerOptionGroup(ci))&&this.trigger("optgroup_add",ui,ci)}removeOptionGroup(oi){this.optgroups.hasOwnProperty(oi)&&(delete this.optgroups[oi],this.clearCache(),this.trigger("optgroup_remove",oi))}clearOptionGroups(){this.optgroups={},this.clearCache(),this.trigger("optgroup_clear")}updateOption(oi,ci){let ui=this;var mi,bi;let _i=xn(oi),Si=xn(ci[ui.settings.valueField]);if(_i===null)return;let Di=ui.options[_i];if(Di==null)return;if(typeof Si!="string")throw new Error("Value must be set in option data");let Oi=ui.getOption(_i),Wi=ui.getItem(_i);if(ci.$order=ci.$order||Di.$order,delete ui.options[_i],ui.uncacheValue(Si),ui.options[Si]=ci,Oi){if(ui.dropdown_content.contains(Oi)){let ji=ui._render("option",ci);Qr(Oi,ji),ui.activeOption===Oi&&ui.setActiveOption(ji)}Oi.remove()}Wi&&(bi=ui.items.indexOf(_i),bi!==-1&&ui.items.splice(bi,1,Si),mi=ui._render("item",ci),Wi.classList.contains("active")&&Gn(mi,"active"),Qr(Wi,mi)),ui.lastQuery=null}removeOption(oi,ci){let ui=this;oi=eo(oi),ui.uncacheValue(oi),delete ui.userOptions[oi],delete ui.options[oi],ui.lastQuery=null,ui.trigger("option_remove",oi),ui.removeItem(oi,ci)}clearOptions(oi){let ci=(oi||this.clearFilter).bind(this);this.loadedSearches={},this.userOptions={},this.clearCache();let ui={};Li(this.options,(mi,bi)=>{ci(mi,bi)&&(ui[bi]=mi)}),this.options=this.sifter.items=ui,this.lastQuery=null,this.trigger("option_clear")}clearFilter(oi,ci){return this.items.indexOf(ci)>=0}getOption(oi,ci=!1){let ui=xn(oi);if(ui===null)return null;let mi=this.options[ui];if(mi!=null){if(mi.$div)return mi.$div;if(ci)return this._render("option",mi)}return null}getAdjacent(oi,ci,ui="option"){var mi=this,bi;if(!oi)return null;ui=="item"?bi=mi.controlChildren():bi=mi.dropdown_content.querySelectorAll("[data-selectable]");for(let _i=0;_i0?bi[_i+1]:bi[_i-1];return null}getItem(oi){if(typeof oi=="object")return oi;var ci=xn(oi);return ci!==null?this.control.querySelector(`[data-value="${wi(ci)}"]`):null}addItems(oi,ci){var ui=this,mi=Array.isArray(oi)?oi:[oi];mi=mi.filter(_i=>ui.items.indexOf(_i)===-1);let bi=mi[mi.length-1];mi.forEach(_i=>{ui.isPending=_i!==bi,ui.addItem(_i,ci)})}addItem(oi,ci){var ui=ci?[]:["change","dropdown_close"];jo(this,ui,()=>{var mi,bi;let _i=this,Si=_i.settings.mode,Di=xn(oi);if(!(Di&&_i.items.indexOf(Di)!==-1&&(Si==="single"&&_i.close(),Si==="single"||!_i.settings.duplicates))&&!(Di===null||!_i.options.hasOwnProperty(Di))&&(Si==="single"&&_i.clear(ci),!(Si==="multi"&&_i.isFull()))){if(mi=_i._render("item",_i.options[Di]),_i.control.contains(mi)&&(mi=mi.cloneNode(!0)),bi=_i.isFull(),_i.items.splice(_i.caretPos,0,Di),_i.insertAtCaret(mi),_i.isSetup){if(!_i.isPending&&_i.settings.hideSelected){let Oi=_i.getOption(Di),Wi=_i.getAdjacent(Oi,1);Wi&&_i.setActiveOption(Wi)}!_i.isPending&&!_i.settings.closeAfterSelect&&_i.refreshOptions(_i.isFocused&&Si!=="single"),_i.settings.closeAfterSelect!=!1&&_i.isFull()?_i.close():_i.isPending||_i.positionDropdown(),_i.trigger("item_add",Di,mi),_i.isPending||_i.updateOriginalInput({silent:ci})}(!_i.isPending||!bi&&_i.isFull())&&(_i.inputState(),_i.refreshState())}})}removeItem(oi=null,ci){let ui=this;if(oi=ui.getItem(oi),!oi)return;var mi,bi;let _i=oi.dataset.value;mi=Wn(oi),oi.remove(),oi.classList.contains("active")&&(bi=ui.activeItems.indexOf(oi),ui.activeItems.splice(bi,1),Xn(oi,"active")),ui.items.splice(mi,1),ui.lastQuery=null,!ui.settings.persist&&ui.userOptions.hasOwnProperty(_i)&&ui.removeOption(_i,ci),mi{}){arguments.length===3&&(ci=arguments[2]),typeof ci!="function"&&(ci=()=>{});var ui=this,mi=ui.caretPos,bi;if(oi=oi||ui.inputValue(),!ui.canCreate(oi))return ci(),!1;ui.lock();var _i=!1,Si=Di=>{if(ui.unlock(),!Di||typeof Di!="object")return ci();var Oi=xn(Di[ui.settings.valueField]);if(typeof Oi!="string")return ci();ui.setTextboxValue(),ui.addOption(Di,!0),ui.setCaret(mi),ui.addItem(Oi),ci(Di),_i=!0};return typeof ui.settings.create=="function"?bi=ui.settings.create.call(this,oi,Si):bi={[ui.settings.labelField]:oi,[ui.settings.valueField]:oi},_i||Si(bi),!0}refreshItems(){var oi=this;oi.lastQuery=null,oi.isSetup&&oi.addItems(oi.items),oi.updateOriginalInput(),oi.refreshState()}refreshState(){let oi=this;oi.refreshValidityState();let ci=oi.isFull(),ui=oi.isLocked;oi.wrapper.classList.toggle("rtl",oi.rtl);let mi=oi.wrapper.classList;mi.toggle("focus",oi.isFocused),mi.toggle("disabled",oi.isDisabled),mi.toggle("readonly",oi.isReadOnly),mi.toggle("required",oi.isRequired),mi.toggle("invalid",!oi.isValid),mi.toggle("locked",ui),mi.toggle("full",ci),mi.toggle("input-active",oi.isFocused&&!oi.isInputHidden),mi.toggle("dropdown-active",oi.isOpen),mi.toggle("has-options",Qs(oi.options)),mi.toggle("has-items",oi.items.length>0)}refreshValidityState(){var oi=this;!oi.input.validity||(oi.isValid=oi.input.validity.valid,oi.isInvalid=!oi.isValid)}isFull(){return this.settings.maxItems!==null&&this.items.length>=this.settings.maxItems}updateOriginalInput(oi={}){let ci=this;var ui,mi;let bi=ci.input.querySelector('option[value=""]');if(ci.is_select_tag){let Di=function(Oi,Wi,ji){return Oi||(Oi=Ri('")),Oi!=bi&&ci.input.append(Oi),_i.push(Oi),(Oi!=bi||Si>0)&&(Oi.selected=!0),Oi},_i=[],Si=ci.input.querySelectorAll("option:checked").length;ci.input.querySelectorAll("option:checked").forEach(Oi=>{Oi.selected=!1}),ci.items.length==0&&ci.settings.mode=="single"?Di(bi,"",""):ci.items.forEach(Oi=>{if(ui=ci.options[Oi],mi=ui[ci.settings.labelField]||"",_i.includes(ui.$option)){let Wi=ci.input.querySelector(`option[value="${wi(Oi)}"]:not(:checked)`);Di(Wi,Oi,mi)}else ui.$option=Di(ui.$option,Oi,mi)})}else ci.input.value=ci.getValue();ci.isSetup&&(oi.silent||ci.trigger("change",ci.getValue()))}open(){var oi=this;oi.isLocked||oi.isOpen||oi.settings.mode==="multi"&&oi.isFull()||(oi.isOpen=!0,_n(oi.focus_node,{"aria-expanded":"true"}),oi.refreshState(),Bn(oi.dropdown,{visibility:"hidden",display:"block"}),oi.positionDropdown(),Bn(oi.dropdown,{visibility:"visible",display:"block"}),oi.focus(),oi.trigger("dropdown_open",oi.dropdown))}close(oi=!0){var ci=this,ui=ci.isOpen;oi&&(ci.setTextboxValue(),ci.settings.mode==="single"&&ci.items.length&&ci.inputState()),ci.isOpen=!1,_n(ci.focus_node,{"aria-expanded":"false"}),Bn(ci.dropdown,{display:"none"}),ci.settings.hideSelected&&ci.clearActiveOption(),ci.refreshState(),ui&&ci.trigger("dropdown_close",ci.dropdown)}positionDropdown(){if(this.settings.dropdownParent==="body"){var oi=this.control,ci=oi.getBoundingClientRect(),ui=oi.offsetHeight+ci.top+window.scrollY,mi=ci.left+window.scrollX;Bn(this.dropdown,{width:ci.width+"px",top:ui+"px",left:mi+"px"})}}clear(oi){var ci=this;if(!!ci.items.length){var ui=ci.controlChildren();Li(ui,mi=>{ci.removeItem(mi,!0)}),ci.inputState(),oi||ci.updateOriginalInput(),ci.trigger("clear")}}insertAtCaret(oi){let ci=this,ui=ci.caretPos,mi=ci.control;mi.insertBefore(oi,mi.children[ui]||null),ci.setCaret(ui+1)}deleteSelection(oi){var ci,ui,mi,bi,_i=this;ci=oi&&oi.keyCode===ga?-1:1,ui=As(_i.control_input);let Si=[];if(_i.activeItems.length)bi=ms(_i.activeItems,ci),mi=Wn(bi),ci>0&&mi++,Li(_i.activeItems,Di=>Si.push(Di));else if((_i.isFocused||_i.settings.mode==="single")&&_i.items.length){let Di=_i.controlChildren(),Oi;ci<0&&ui.start===0&&ui.length===0?Oi=Di[_i.caretPos-1]:ci>0&&ui.start===_i.inputValue().length&&(Oi=Di[_i.caretPos]),Oi!==void 0&&Si.push(Oi)}if(!_i.shouldDelete(Si,oi))return!1;for(Dn(oi,!0),typeof mi!="undefined"&&_i.setCaret(mi);Si.length;)_i.removeItem(Si.pop());return _i.inputState(),_i.positionDropdown(),_i.refreshOptions(!1),!0}shouldDelete(oi,ci){let ui=oi.map(mi=>mi.dataset.value);return!(!ui.length||typeof this.settings.onDelete=="function"&&this.settings.onDelete(ui,ci)===!1)}advanceSelection(oi,ci){var ui,mi,bi=this;bi.rtl&&(oi*=-1),!bi.inputValue().length&&(hi(Fo,ci)||hi("shiftKey",ci)?(ui=bi.getLastActive(oi),ui?ui.classList.contains("active")?mi=bi.getAdjacent(ui,oi,"item"):mi=ui:oi>0?mi=bi.control_input.nextElementSibling:mi=bi.control_input.previousElementSibling,mi&&(mi.classList.contains("active")&&bi.removeActiveItem(ui),bi.setActiveItemClass(mi))):bi.moveCaret(oi))}moveCaret(oi){}getLastActive(oi){let ci=this.control.querySelector(".last-active");if(ci)return ci;var ui=this.control.querySelectorAll(".active");if(ui)return ms(ui,oi)}setCaret(oi){this.caretPos=this.items.length}controlChildren(){return Array.from(this.control.querySelectorAll("[data-ts-item]"))}lock(){this.setLocked(!0)}unlock(){this.setLocked(!1)}setLocked(oi=this.isReadOnly||this.isDisabled){this.isLocked=oi,this.refreshState()}disable(){this.setDisabled(!0),this.close()}enable(){this.setDisabled(!1)}setDisabled(oi){this.focus_node.tabIndex=oi?-1:this.tabIndex,this.isDisabled=oi,this.input.disabled=oi,this.control_input.disabled=oi,this.setLocked()}setReadOnly(oi){this.isReadOnly=oi,this.input.readOnly=oi,this.control_input.readOnly=oi,this.setLocked()}destroy(){var oi=this,ci=oi.revertSettings;oi.trigger("destroy"),oi.off(),oi.wrapper.remove(),oi.dropdown.remove(),oi.input.innerHTML=ci.innerHTML,oi.input.tabIndex=ci.tabIndex,Xn(oi.input,"tomselected","ts-hidden-accessible"),oi._destroy(),delete oi.input.tomselect}render(oi,ci){var ui,mi;let bi=this;if(typeof this.settings.render[oi]!="function"||(mi=bi.settings.render[oi].call(this,ci,to),!mi))return null;if(mi=Ri(mi),oi==="option"||oi==="option_create"?ci[bi.settings.disabledField]?_n(mi,{"aria-disabled":"true"}):_n(mi,{"data-selectable":""}):oi==="optgroup"&&(ui=ci.group[bi.settings.optgroupValueField],_n(mi,{"data-group":ui}),ci.group[bi.settings.disabledField]&&_n(mi,{"data-disabled":""})),oi==="option"||oi==="item"){let _i=eo(ci[bi.settings.valueField]);_n(mi,{"data-value":_i}),oi==="item"?(Gn(mi,bi.settings.itemClass),_n(mi,{"data-ts-item":""})):(Gn(mi,bi.settings.optionClass),_n(mi,{role:"option",id:ci.$id}),ci.$div=mi,bi.options[_i]=ci)}return mi}_render(oi,ci){let ui=this.render(oi,ci);if(ui==null)throw"HTMLElement expected";return ui}clearCache(){Li(this.options,oi=>{oi.$div&&(oi.$div.remove(),delete oi.$div)})}uncacheValue(oi){let ci=this.getOption(oi);ci&&ci.remove()}canCreate(oi){return this.settings.create&&oi.length>0&&this.settings.createFilter.call(this,oi)}hook(oi,ci,ui){var mi=this,bi=mi[ci];mi[ci]=function(){var _i,Si;return oi==="after"&&(_i=bi.apply(mi,arguments)),Si=ui.apply(mi,arguments),oi==="instead"?Si:(oi==="before"&&(_i=bi.apply(mi,arguments)),_i)}}}function Xi(){Ln(this.input,"change",()=>{this.sync()})}function bn(fi){var oi=this,ci=oi.onOptionSelect;oi.settings.hideSelected=!1;let ui=Object.assign({className:"tomselect-checkbox",checkedClassNames:void 0,uncheckedClassNames:void 0},fi);var mi=function(Si,Di){Di?(Si.checked=!0,ui.uncheckedClassNames&&Si.classList.remove(...ui.uncheckedClassNames),ui.checkedClassNames&&Si.classList.add(...ui.checkedClassNames)):(Si.checked=!1,ui.checkedClassNames&&Si.classList.remove(...ui.checkedClassNames),ui.uncheckedClassNames&&Si.classList.add(...ui.uncheckedClassNames))},bi=function(Si){setTimeout(()=>{var Di=Si.querySelector("input."+ui.className);Di instanceof HTMLInputElement&&mi(Di,Si.classList.contains("selected"))},1)};oi.hook("after","setupTemplates",()=>{var _i=oi.settings.render.option;oi.settings.render.option=(Si,Di)=>{var Oi=Ri(_i.call(oi,Si,Di)),Wi=document.createElement("input");ui.className&&Wi.classList.add(ui.className),Wi.addEventListener("click",function(Zi){Dn(Zi)}),Wi.type="checkbox";let ji=xn(Si[oi.settings.valueField]);return mi(Wi,!!(ji&&oi.items.indexOf(ji)>-1)),Oi.prepend(Wi),Oi}}),oi.on("item_remove",_i=>{var Si=oi.getOption(_i);Si&&(Si.classList.remove("selected"),bi(Si))}),oi.on("item_add",_i=>{var Si=oi.getOption(_i);Si&&bi(Si)}),oi.hook("instead","onOptionSelect",(_i,Si)=>{if(Si.classList.contains("selected")){Si.classList.remove("selected"),oi.removeItem(Si.dataset.value),oi.refreshOptions(),Dn(_i,!0);return}ci.call(oi,_i,Si),bi(Si)})}function Gi(fi){let oi=this,ci=Object.assign({className:"clear-button",title:"Clear All",html:ui=>`
`},fi);oi.on("initialize",()=>{var ui=Ri(ci.html(ci));ui.addEventListener("click",mi=>{oi.isLocked||(oi.clear(),oi.settings.mode==="single"&&oi.settings.allowEmptyOption&&oi.addItem(""),mi.preventDefault(),mi.stopPropagation())}),oi.control.appendChild(ui)})}let hn=(fi,oi)=>{var ci;(ci=fi.parentNode)==null||ci.insertBefore(oi,fi.nextSibling)},kn=(fi,oi)=>{var ci;(ci=fi.parentNode)==null||ci.insertBefore(oi,fi)},gs=(fi,oi)=>{do{var ci;if(oi=(ci=oi)==null?void 0:ci.previousElementSibling,fi==oi)return!0}while(oi&&oi.previousElementSibling);return!1};function ns(){var fi=this;if(fi.settings.mode!=="multi")return;var oi=fi.lock,ci=fi.unlock;let ui=!0,mi;fi.hook("after","setupTemplates",()=>{var bi=fi.settings.render.item;fi.settings.render.item=(_i,Si)=>{let Di=Ri(bi.call(fi,_i,Si));_n(Di,{draggable:"true"});let Oi=An=>{ui||Dn(An),An.stopPropagation()},Wi=An=>{mi=Di,setTimeout(()=>{Di.classList.add("ts-dragging")},0)},ji=An=>{An.preventDefault(),Di.classList.add("ts-drag-over"),In(Di,mi)},Zi=()=>{Di.classList.remove("ts-drag-over")},In=(An,Hn)=>{Hn!==void 0&&(gs(Hn,Di)?hn(An,Hn):kn(An,Hn))},Qi=()=>{var An;document.querySelectorAll(".ts-drag-over").forEach(Nn=>Nn.classList.remove("ts-drag-over")),(An=mi)==null||An.classList.remove("ts-dragging"),mi=void 0;var Hn=[];fi.control.querySelectorAll("[data-value]").forEach(Nn=>{if(Nn.dataset.value){let dn=Nn.dataset.value;dn&&Hn.push(dn)}}),fi.setValue(Hn)};return Ln(Di,"mousedown",Oi),Ln(Di,"dragstart",Wi),Ln(Di,"dragenter",ji),Ln(Di,"dragover",ji),Ln(Di,"dragleave",Zi),Ln(Di,"dragend",Qi),Di}}),fi.hook("instead","lock",()=>(ui=!1,oi.call(fi))),fi.hook("instead","unlock",()=>(ui=!0,ci.call(fi)))}function go(fi){let oi=this,ci=Object.assign({title:"Untitled",headerClass:"dropdown-header",titleRowClass:"dropdown-header-title",labelClass:"dropdown-header-label",closeClass:"dropdown-header-close",html:ui=>'
'+ui.title+'×
'},fi);oi.on("initialize",()=>{var ui=Ri(ci.html(ci)),mi=ui.querySelector("."+ci.closeClass);mi&&mi.addEventListener("click",bi=>{Dn(bi,!0),oi.close()}),oi.dropdown.insertBefore(ui,oi.dropdown.firstChild)})}function io(){var fi=this;fi.hook("instead","setCaret",oi=>{fi.settings.mode==="single"||!fi.control.contains(fi.control_input)?oi=fi.items.length:(oi=Math.max(0,Math.min(fi.items.length,oi)),oi!=fi.caretPos&&!fi.isPending&&fi.controlChildren().forEach((ci,ui)=>{ui{if(!fi.isFocused)return;let ci=fi.getLastActive(oi);if(ci){let ui=Wn(ci);fi.setCaret(oi>0?ui+1:ui),fi.setActiveItem(),Xn(ci,"last-active")}else fi.setCaret(fi.caretPos+oi)})}function zl(){let fi=this;fi.settings.shouldOpen=!0,fi.hook("before","setup",()=>{fi.focus_node=fi.control,Gn(fi.control_input,"dropdown-input");let oi=Ri('