From cc50e229283cacdc9dac59d5e96008461f1841a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Jonak-M=C3=B6chel?= Date: Tue, 7 Dec 2021 15:14:17 +0100 Subject: [PATCH 01/21] feat: add 6GHz & 60Ghz channels --- netbox/wireless/choices.py | 278 +++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) diff --git a/netbox/wireless/choices.py b/netbox/wireless/choices.py index c8e7fd09f..2813eafd6 100644 --- a/netbox/wireless/choices.py +++ b/netbox/wireless/choices.py @@ -83,6 +83,143 @@ class WirelessChannelChoices(ChoiceSet): CHANNEL_5G_173 = '5g-173-5865-20' CHANNEL_5G_175 = '5g-175-5875-40' CHANNEL_5G_177 = '5g-177-5885-20' + + # 6 GHz + CHANNEL_6G_1 = '6g-1-5955-20' + CHANNEL_6G_3 = '6g-3-5955-40' + CHANNEL_6G_5 = '6g-5-5965-20' + CHANNEL_6G_7 = '6g-7-5975-80' + CHANNEL_6G_9 = '6g-9-5985-20' + CHANNEL_6G_11 = '6g-11-5995-40' + CHANNEL_6G_13 = '6g-13-6005-20' + CHANNEL_6G_15 = '6g-15-6015-160' + CHANNEL_6G_17 = '6g-17-6025-20' + CHANNEL_6G_19 = '6g-19-6035-40' + CHANNEL_6G_21 = '6g-21-6045-20' + CHANNEL_6G_23 = '6g-23-6055-80' + CHANNEL_6G_25 = '6g-25-6065-20' + CHANNEL_6G_28 = '6g-28-6080-40' + CHANNEL_6G_29 = '6g-29-6085-20' + CHANNEL_6G_31 = '6g-31-6095-320' + CHANNEL_6G_33 = '6g-33-6105-20' + CHANNEL_6G_36 = '6g-36-6120-40' + CHANNEL_6G_37 = '6g-37-6125-20' + CHANNEL_6G_39 = '6g-39-6135-80' + CHANNEL_6G_41 = '6g-41-6145-20' + CHANNEL_6G_44 = '6g-44-6160-40' + CHANNEL_6G_45 = '6g-45-6165-20' + CHANNEL_6G_47 = '6g-47-6175-160' + CHANNEL_6G_49 = '6g-49-6185-20' + CHANNEL_6G_52 = '6g-52-6200-40' + CHANNEL_6G_53 = '6g-53-6205-20' + CHANNEL_6G_55 = '6g-55-6215-80' + CHANNEL_6G_57 = '6g-57-6225-20' + CHANNEL_6G_60 = '6g-60-6240-40' + CHANNEL_6G_61 = '6g-61-6245-20' + CHANNEL_6G_65 = '6g-65-6265-20' + CHANNEL_6G_68 = '6g-68-6280-40' + CHANNEL_6G_69 = '6g-69-6285-20' + CHANNEL_6G_71 = '6g-71-6295-80' + CHANNEL_6G_73 = '6g-73-6305-20' + CHANNEL_6G_76 = '6g-76-6320-40' + CHANNEL_6G_77 = '6g-77-6325-20' + CHANNEL_6G_79 = '6g-79-6335-160' + CHANNEL_6G_81 = '6g-81-6345-20' + CHANNEL_6G_84 = '6g-84-6360-40' + CHANNEL_6G_85 = '6g-85-6365-20' + CHANNEL_6G_87 = '6g-87-6375-80' + CHANNEL_6G_89 = '6g-89-6385-20' + CHANNEL_6G_92 = '6g-92-6400-40' + CHANNEL_6G_93 = '6g-93-6405-20' + CHANNEL_6G_95 = '6g-95-6415-320' + CHANNEL_6G_97 = '6g-97-6425-20' + CHANNEL_6G_100 = '6g-100-6440-40' + CHANNEL_6G_101 = '6g-101-6445-20' + CHANNEL_6G_103 = '6g-103-6455-80' + CHANNEL_6G_105 = '6g-105-6465-20' + CHANNEL_6G_108 = '6g-108-6480-40' + CHANNEL_6G_109 = '6g-109-6485-20' + CHANNEL_6G_111 = '6g-111-6495-160' + CHANNEL_6G_113 = '6g-113-6505-20' + CHANNEL_6G_116 = '6g-116-6520-40' + CHANNEL_6G_117 = '6g-117-6525-20' + CHANNEL_6G_119 = '6g-119-6535-80' + CHANNEL_6G_121 = '6g-121-6545-20' + CHANNEL_6G_124 = '6g-124-6560-40' + CHANNEL_6G_125 = '6g-125-6565-20' + CHANNEL_6G_129 = '6g-129-6585-20' + CHANNEL_6G_132 = '6g-132-6600-40' + CHANNEL_6G_133 = '6g-133-6605-20' + CHANNEL_6G_135 = '6g-135-6615-80' + CHANNEL_6G_137 = '6g-137-6625-20' + CHANNEL_6G_140 = '6g-140-6640-40' + CHANNEL_6G_141 = '6g-141-6645-20' + CHANNEL_6G_143 = '6g-143-6655-160' + CHANNEL_6G_145 = '6g-145-6665-20' + CHANNEL_6G_148 = '6g-148-6680-40' + CHANNEL_6G_149 = '6g-149-6685-20' + CHANNEL_6G_151 = '6g-151-6695-80' + CHANNEL_6G_153 = '6g-153-6705-20' + CHANNEL_6G_156 = '6g-156-6720-40' + CHANNEL_6G_157 = '6g-157-6725-20' + CHANNEL_6G_159 = '6g-159-6735-320' + CHANNEL_6G_161 = '6g-161-6745-20' + CHANNEL_6G_164 = '6g-164-6760-40' + CHANNEL_6G_165 = '6g-165-6765-20' + CHANNEL_6G_167 = '6g-167-6775-80' + CHANNEL_6G_169 = '6g-169-6785-20' + CHANNEL_6G_172 = '6g-172-6800-40' + CHANNEL_6G_173 = '6g-173-6805-20' + CHANNEL_6G_175 = '6g-175-6815-160' + CHANNEL_6G_177 = '6g-177-6825-20' + CHANNEL_6G_180 = '6g-180-6840-40' + CHANNEL_6G_181 = '6g-181-6845-20' + CHANNEL_6G_183 = '6g-183-6855-80' + CHANNEL_6G_185 = '6g-185-6865-20' + CHANNEL_6G_188 = '6g-188-6880-40' + CHANNEL_6G_189 = '6g-189-6885-20' + CHANNEL_6G_193 = '6g-193-6905-20' + CHANNEL_6G_196 = '6g-196-6920-40' + CHANNEL_6G_197 = '6g-197-6925-20' + CHANNEL_6G_199 = '6g-199-6935-80' + CHANNEL_6G_201 = '6g-201-6945-20' + CHANNEL_6G_204 = '6g-204-6960-40' + CHANNEL_6G_205 = '6g-205-6965-20' + CHANNEL_6G_207 = '6g-207-6975-160' + CHANNEL_6G_209 = '6g-209-6985-20' + CHANNEL_6G_212 = '6g-212-7000-40' + CHANNEL_6G_213 = '6g-213-7005-20' + CHANNEL_6G_215 = '6g-215-7015-80' + CHANNEL_6G_217 = '6g-217-7025-20' + CHANNEL_6G_220 = '6g-220-7040-40' + CHANNEL_6G_221 = '6g-221-7045-20' + CHANNEL_6G_225 = '6g-225-7065-20' + CHANNEL_6G_228 = '6g-228-7080-40' + CHANNEL_6G_229 = '6g-229-7085-20' + CHANNEL_6G_233 = '6g-233-7105-20' + + + # 60 GHz + CHANNEL_60G_1 = '60g-1-58320-2160' + CHANNEL_60G_2 = '60g-2-60480-2160' + CHANNEL_60G_3 = '60g-3-62640-2160' + CHANNEL_60G_4 = '60g-4-64800-2160' + CHANNEL_60G_5 = '60g-5-66960-2160' + CHANNEL_60G_6 = '60g-6-69120-2160' + CHANNEL_60G_9 = '60g-9-59400-4320' + CHANNEL_60G_10 = '60g-10-61560-4320' + CHANNEL_60G_11 = '60g-11-63720-4320' + CHANNEL_60G_12 = '60g-12-65880-4320' + CHANNEL_60G_13 = '60g-13-68040-4320' + CHANNEL_60G_17 = '60g-17-60480-6480' + CHANNEL_60G_18 = '60g-18-62640-6480' + CHANNEL_60G_19 = '60g-19-64800-6480' + CHANNEL_60G_20 = '60g-20-66960-6480' + CHANNEL_60G_25 = '60g-25-61560-6480' + CHANNEL_60G_26 = '60g-26-63720-6480' + CHANNEL_60G_27 = '60g-27-65880-6480' + + CHOICES = ( ( @@ -162,6 +299,147 @@ class WirelessChannelChoices(ChoiceSet): (CHANNEL_5G_177, '177 (5885/20 MHz)'), ) ), + ( + '6 GHz (802.11ax)', + ( + (CHANNEL_6G_1, '1 (5945/20 MHz)'), + (CHANNEL_6G_3, '3 (5955/40 MHz)'), + (CHANNEL_6G_5, '5 (5965/20 MHz)'), + (CHANNEL_6G_7, '7 (5975/80 MHz)'), + (CHANNEL_6G_9, '9 (5985/20 MHz)'), + (CHANNEL_6G_11, '11 (5995/40 MHz)'), + (CHANNEL_6G_13, '13 (6005/20 MHz)'), + (CHANNEL_6G_15, '15 (6015/160 MHz)'), + (CHANNEL_6G_17, '17 (6025/20 MHz)'), + (CHANNEL_6G_19, '19 (6035/40 MHz)'), + (CHANNEL_6G_21, '21 (6045/20 MHz)'), + (CHANNEL_6G_23, '23 (6055/80 MHz)'), + (CHANNEL_6G_25, '25 (6065/20 MHz)'), + (CHANNEL_6G_28, '28 (6080/40 MHz)'), + (CHANNEL_6G_29, '29 (6085/20 MHz)'), + (CHANNEL_6G_31, '31 (6095/320 MHz)'), + (CHANNEL_6G_33, '33 (6105/20 MHz)'), + (CHANNEL_6G_36, '36 (6120/40 MHz)'), + (CHANNEL_6G_37, '37 (6125/20 MHz)'), + (CHANNEL_6G_39, '39 (6135/80 MHz)'), + (CHANNEL_6G_41, '41 (6145/20 MHz)'), + (CHANNEL_6G_44, '44 (6160/40 MHz)'), + (CHANNEL_6G_45, '45 (6165/20 MHz)'), + (CHANNEL_6G_47, '47 (6175/160 MHz)'), + (CHANNEL_6G_49, '49 (6185/20 MHz)'), + (CHANNEL_6G_52, '52 (6200/40 MHz)'), + (CHANNEL_6G_53, '53 (6205/20 MHz)'), + (CHANNEL_6G_55, '55 (6215/80 MHz)'), + (CHANNEL_6G_57, '57 (6225/20 MHz)'), + (CHANNEL_6G_60, '60 (6240/40 MHz)'), + (CHANNEL_6G_61, '61 (6245/20 MHz)'), + (CHANNEL_6G_65, '65 (6265/20 MHz)'), + (CHANNEL_6G_68, '68 (6280/40 MHz)'), + (CHANNEL_6G_69, '69 (6285/20 MHz)'), + (CHANNEL_6G_71, '71 (6295/80 MHz)'), + (CHANNEL_6G_73, '73 (6305/20 MHz)'), + (CHANNEL_6G_76, '76 (6320/40 MHz)'), + (CHANNEL_6G_77, '77 (6325/20 MHz)'), + (CHANNEL_6G_79, '79 (6335/160 MHz)'), + (CHANNEL_6G_81, '81 (6345/20 MHz)'), + (CHANNEL_6G_84, '84 (6360/40 MHz)'), + (CHANNEL_6G_85, '85 (6365/20 MHz)'), + (CHANNEL_6G_87, '87 (6375/80 MHz)'), + (CHANNEL_6G_89, '89 (6385/20 MHz)'), + (CHANNEL_6G_92, '92 (6400/40 MHz)'), + (CHANNEL_6G_93, '93 (6405/20 MHz)'), + (CHANNEL_6G_95, '95 (6415/320 MHz)'), + (CHANNEL_6G_97, '97 (6425/20 MHz)'), + (CHANNEL_6G_100, '100 (6440/40 MHz)'), + (CHANNEL_6G_101, '101 (6445/20 MHz)'), + (CHANNEL_6G_103, '103 (6455/80 MHz)'), + (CHANNEL_6G_105, '105 (6465/20 MHz)'), + (CHANNEL_6G_108, '108 (6480/40 MHz)'), + (CHANNEL_6G_109, '109 (6485/20 MHz)'), + (CHANNEL_6G_111, '111 (6495/160 MHz)'), + (CHANNEL_6G_113, '113 (6505/20 MHz)'), + (CHANNEL_6G_116, '116 (6520/40 MHz)'), + (CHANNEL_6G_117, '117 (6525/20 MHz)'), + (CHANNEL_6G_119, '119 (6535/80 MHz)'), + (CHANNEL_6G_121, '121 (6545/20 MHz)'), + (CHANNEL_6G_124, '124 (6560/40 MHz)'), + (CHANNEL_6G_125, '125 (6565/20 MHz)'), + (CHANNEL_6G_129, '129 (6585/20 MHz)'), + (CHANNEL_6G_132, '132 (6600/40 MHz)'), + (CHANNEL_6G_133, '133 (6605/20 MHz)'), + (CHANNEL_6G_135, '135 (6615/80 MHz)'), + (CHANNEL_6G_137, '137 (6625/20 MHz)'), + (CHANNEL_6G_140, '140 (6640/40 MHz)'), + (CHANNEL_6G_141, '141 (6645/20 MHz)'), + (CHANNEL_6G_143, '143 (6655/160 MHz)'), + (CHANNEL_6G_145, '145 (6665/20 MHz)'), + (CHANNEL_6G_148, '148 (6680/40 MHz)'), + (CHANNEL_6G_149, '149 (6685/20 MHz)'), + (CHANNEL_6G_151, '151 (6695/80 MHz)'), + (CHANNEL_6G_153, '153 (6705/20 MHz)'), + (CHANNEL_6G_156, '156 (6720/40 MHz)'), + (CHANNEL_6G_157, '157 (6725/20 MHz)'), + (CHANNEL_6G_159, '159 (6735/320 MHz)'), + (CHANNEL_6G_161, '161 (6745/20 MHz)'), + (CHANNEL_6G_164, '164 (6760/40 MHz)'), + (CHANNEL_6G_165, '165 (6765/20 MHz)'), + (CHANNEL_6G_167, '167 (6775/80 MHz)'), + (CHANNEL_6G_169, '169 (6785/20 MHz)'), + (CHANNEL_6G_172, '172 (6800/40 MHz)'), + (CHANNEL_6G_173, '173 (6805/20 MHz)'), + (CHANNEL_6G_175, '175 (6815/160 MHz)'), + (CHANNEL_6G_177, '177 (6825/20 MHz)'), + (CHANNEL_6G_180, '180 (6840/40 MHz)'), + (CHANNEL_6G_181, '181 (6845/20 MHz)'), + (CHANNEL_6G_183, '183 (6855/80 MHz)'), + (CHANNEL_6G_185, '185 (6865/20 MHz)'), + (CHANNEL_6G_188, '188 (6880/40 MHz)'), + (CHANNEL_6G_189, '189 (6885/20 MHz)'), + (CHANNEL_6G_193, '193 (6905/20 MHz)'), + (CHANNEL_6G_196, '196 (6920/40 MHz)'), + (CHANNEL_6G_197, '197 (6925/20 MHz)'), + (CHANNEL_6G_199, '199 (6935/80 MHz)'), + (CHANNEL_6G_201, '201 (6945/20 MHz)'), + (CHANNEL_6G_204, '204 (6960/40 MHz)'), + (CHANNEL_6G_205, '205 (6965/20 MHz)'), + (CHANNEL_6G_207, '207 (6975/160 MHz)'), + (CHANNEL_6G_209, '209 (6985/20 MHz)'), + (CHANNEL_6G_212, '212 (7000/40 MHz)'), + (CHANNEL_6G_213, '213 (7005/20 MHz)'), + (CHANNEL_6G_215, '215 (7015/80 MHz)'), + (CHANNEL_6G_217, '217 (7025/20 MHz)'), + (CHANNEL_6G_220, '220 (7040/40 MHz)'), + (CHANNEL_6G_221, '221 (7045/20 MHz)'), + (CHANNEL_6G_225, '225 (7065/20 MHz)'), + (CHANNEL_6G_228, '228 (7080/40 MHz)'), + (CHANNEL_6G_229, '229 (7085/20 MHz)'), + (CHANNEL_6G_233, '233 (7105/20 MHz)'), + + ) + ), + ( + '60 GHz (802.11ad/ay)', + ( + (CHANNEL_60G_1, '1 (58.32/2.16 GHz)'), + (CHANNEL_60G_2, '2 (60.48/2.16 GHz)'), + (CHANNEL_60G_3, '3 (62.64/2.16 GHz)'), + (CHANNEL_60G_4, '4 (64.80/2.16 GHz)'), + (CHANNEL_60G_5, '5 (66.96/2.16 GHz)'), + (CHANNEL_60G_6, '6 (69.12/2.16 GHz)'), + (CHANNEL_60G_9, '9 (59.40/4.32 GHz)'), + (CHANNEL_60G_10, '10 (61.56/4.32 GHz)'), + (CHANNEL_60G_11, '11 (63.72/4.32 GHz)'), + (CHANNEL_60G_12, '12 (65.88/4.32 GHz)'), + (CHANNEL_60G_13, '13 (68.04/4.32 GHz)'), + (CHANNEL_60G_17, '17 (60.48/6.48 GHz)'), + (CHANNEL_60G_18, '18 (62.64/6.48 GHz)'), + (CHANNEL_60G_19, '19 (64.80/6.48 GHz)'), + (CHANNEL_60G_20, '20 (66.96/6.48 GHz)'), + (CHANNEL_60G_25, '25 (61.56/8.64 GHz)'), + (CHANNEL_60G_26, '26 (63.72/8.64 GHz)'), + (CHANNEL_60G_27, '27 (65.88/8.64 GHz)'), + ) + ), ) From 806706ca1d56e7a6b02a7962bd3f65db8cb5b28b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 15 Dec 2021 16:31:06 -0500 Subject: [PATCH 02/21] Refresh development documentation --- docs/development/adding-models.md | 14 ++++++-------- docs/development/extending-models.md | 15 +++++++-------- docs/development/getting-started.md | 27 +++++++++++++++++++-------- docs/development/index.md | 13 ++++++++----- docs/development/models.md | 5 +++-- docs/development/style-guide.md | 6 +++--- 6 files changed, 46 insertions(+), 34 deletions(-) diff --git a/docs/development/adding-models.md b/docs/development/adding-models.md index 6b778d886..156a8ba97 100644 --- a/docs/development/adding-models.md +++ b/docs/development/adding-models.md @@ -6,9 +6,9 @@ Models within each app are stored in either `models.py` or within a submodule un Each model should define, at a minimum: +* A `Meta` class specifying a deterministic ordering (if ordered by fields other than the primary ID) * A `__str__()` method returning a user-friendly string representation of the instance * A `get_absolute_url()` method returning an instance's direct URL (using `reverse()`) -* A `Meta` class specifying a deterministic ordering (if ordered by fields other than the primary ID) ## 2. Define field choices @@ -16,9 +16,9 @@ If the model has one or more fields with static choices, define those choices in ## 3. Generate database migrations -Once your model definition is complete, generate database migrations by running `manage.py -n $NAME --no-header`. Always specify a short unique name when generating migrations. +Once your model definition is complete, generate database migrations by running `manage.py makemigrations -n $NAME --no-header`. Always specify a short unique name when generating migrations. -!!! info +!!! info "Configuration Required" Set `DEVELOPER = True` in your NetBox configuration to enable the creation of new migrations. ## 4. Add all standard views @@ -41,9 +41,7 @@ Add the relevant URL path for each view created in the previous step to `urls.py Each model should have a corresponding FilterSet class defined. This is used to filter UI and API queries. Subclass the appropriate class from `netbox.filtersets` that matches the model's parent class. -Every model FilterSet should define a `q` filter to support general search queries. - -## 7. Create the table +## 7. Create the table class Create a table class for the model in `tables.py` by subclassing `utilities.tables.BaseTable`. Under the table's `Meta` class, be sure to list both the fields and default columns. @@ -53,7 +51,7 @@ Create the HTML template for the object view. (The other views each typically em ## 9. Add the model to the navigation menu -For NetBox releases prior to v3.0, add the relevant link(s) to the navigation menu template. For later releases, add the relevant items in `netbox/netbox/navigation_menu.py`. +Add the relevant navigation menu items in `netbox/netbox/navigation_menu.py`. ## 10. REST API components @@ -64,7 +62,7 @@ Create the following for each model: * API view in `api/views.py` * Endpoint route in `api/urls.py` -## 11. GraphQL API components (v3.0+) +## 11. GraphQL API components Create a Graphene object type for the model in `graphql/types.py` by subclassing the appropriate class from `netbox.graphql.types`. diff --git a/docs/development/extending-models.md b/docs/development/extending-models.md index 99c448c06..ad8fe5024 100644 --- a/docs/development/extending-models.md +++ b/docs/development/extending-models.md @@ -4,16 +4,16 @@ Below is a list of tasks to consider when adding a new field to a core model. ## 1. Generate and run database migrations -Django migrations are used to express changes to the database schema. In most cases, Django can generate these automatically, however very complex changes may require manual intervention. Always remember to specify a short but descriptive name when generating a new migration. +[Django migrations](https://docs.djangoproject.com/en/stable/topics/migrations/) are used to express changes to the database schema. In most cases, Django can generate these automatically, however very complex changes may require manual intervention. Always remember to specify a short but descriptive name when generating a new migration. ``` ./manage.py makemigrations -n ./manage.py migrate ``` -Where possible, try to merge related changes into a single migration. For example, if three new fields are being added to different models within an app, these can be expressed in the same migration. You can merge a new migration with an existing one by combining their `operations` lists. +Where possible, try to merge related changes into a single migration. For example, if three new fields are being added to different models within an app, these can be expressed in a single migration. You can merge a newly generated migration with an existing one by combining their `operations` lists. -!!! note +!!! warning "Do not alter existing migrations" Migrations can only be merged within a release. Once a new release has been published, its migrations cannot be altered (other than for the purpose of correcting a bug). ## 2. Add validation logic to `clean()` @@ -24,7 +24,6 @@ If the new field introduces additional validation requirements (beyond what's in class Foo(models.Model): def clean(self): - super().clean() # Custom validation goes here @@ -40,9 +39,9 @@ If you're adding a relational field (e.g. `ForeignKey`) and intend to include th Extend the model's API serializer in `.api.serializers` to include the new field. In most cases, it will not be necessary to also extend the nested serializer, which produces a minimal representation of the model. -## 5. Add field to forms +## 5. Add fields to forms -Extend any forms to include the new field as appropriate. Common forms include: +Extend any forms to include the new field(s) as appropriate. These are found under the `forms/` directory within each app. Common forms include: * **Credit/edit** - Manipulating a single object * **Bulk edit** - Performing a change on many objects at once @@ -51,11 +50,11 @@ Extend any forms to include the new field as appropriate. Common forms include: ## 6. Extend object filter set -If the new field should be filterable, add it to the `FilterSet` for the model. If the field should be searchable, remember to reference it in the FilterSet's `search()` method. +If the new field should be filterable, add it to the `FilterSet` for the model. If the field should be searchable, remember to query it in the FilterSet's `search()` method. ## 7. Add column to object table -If the new field will be included in the object list view, add a column to the model's table. For simple fields, adding the field name to `Meta.fields` will be sufficient. More complex fields may require declaring a custom column. +If the new field will be included in the object list view, add a column to the model's table. For simple fields, adding the field name to `Meta.fields` will be sufficient. More complex fields may require declaring a custom column. Also add the field name to `default_columns` if the column should be present in the table by default. ## 8. Update the UI templates diff --git a/docs/development/getting-started.md b/docs/development/getting-started.md index beeae2ffb..acf13b82f 100644 --- a/docs/development/getting-started.md +++ b/docs/development/getting-started.md @@ -35,6 +35,8 @@ The NetBox project utilizes three persistent git branches to track work: Typically, you'll base pull requests off of the `develop` branch, or off of `feature` if you're working on a new major release. **Never** merge pull requests into the `master` branch, which receives merged only from the `develop` branch. +For example, assume that the current NetBox release is v3.1.1. Work applied to the `develop` branch will appear in v3.1.2, and work done under the `feature` branch will be included in the next minor release (v3.2.0). + ### Enable Pre-Commit Hooks NetBox ships with a [git pre-commit hook](https://githooks.com/) script that automatically checks for style compliance and missing database migrations prior to committing changes. This helps avoid erroneous commits that result in CI test failures. You are encouraged to enable it by creating a link to `scripts/git-hooks/pre-commit`: @@ -46,7 +48,7 @@ $ ln -s ../../scripts/git-hooks/pre-commit ### Create a Python Virtual Environment -A [virtual environment](https://docs.python.org/3/tutorial/venv.html) is like a container for a set of Python packages. They allow you to build environments suited to specific projects without interfering with system packages or other projects. When installed per the documentation, NetBox uses a virtual environment in production. +A [virtual environment](https://docs.python.org/3/tutorial/venv.html) (or "venv" for short) is like a container for a set of Python packages. These allow you to build environments suited to specific projects without interfering with system packages or other projects. When installed per the documentation, NetBox uses a virtual environment in production. Create a virtual environment using the `venv` Python module: @@ -57,8 +59,8 @@ $ python3 -m venv ~/.venv/netbox This will create a directory named `.venv/netbox/` in your home directory, which houses a virtual copy of the Python executable and its related libraries and tooling. When running NetBox for development, it will be run using the Python binary at `~/.venv/netbox/bin/python`. -!!! info - Keeping virtual environments in `~/.venv/` is a common convention but entirely optional: Virtual environments can be created wherever you please. +!!! info "Where to Create Your Virtual Environments" + Keeping virtual environments in `~/.venv/` is a common convention but entirely optional: Virtual environments can be created almost wherever you please. Once created, activate the virtual environment: @@ -94,7 +96,7 @@ Within the `netbox/netbox/` directory, copy `configuration.example.py` to `confi ### Start the Development Server -Django provides a lightweight, auto-updating HTTP/WSGI server for development use. NetBox extends this slightly to automatically import models and other utilities. Run the NetBox development server with the `nbshell` management command: +Django provides a lightweight, auto-updating HTTP/WSGI server for development use. It is started with the `runserver` management command: ```no-highlight $ python netbox/manage.py runserver @@ -109,9 +111,12 @@ Quit the server with CONTROL-C. This ensures that your development environment is now complete and operational. Any changes you make to the code base will be automatically adapted by the development server. +!!! info "IDE Integration" + Some IDEs, such as PyCharm, will integrate with Django's development server and allow you to run it directly within the IDE. This is strongly encouraged as it makes for a much more convenient development environment. + ## Running Tests -Throughout the course of development, it's a good idea to occasionally run NetBox's test suite to catch any potential errors. Tests are run using the `test` management command: +Prior to committing any substantial changes to the code base, be sure to run NetBox's test suite to catch any potential errors. Tests are run using the `test` management command. Remember to ensure the Python virtual environment is active before running this command. ```no-highlight $ python netbox/manage.py test @@ -123,9 +128,15 @@ In cases where you haven't made any changes to the database (which is most of th $ python netbox/manage.py test --keepdb ``` +You can also limit the command to running only a specific subset of tests. For example, to run only IPAM and DCIM view tests: + +```no-highlight +$ python netbox/manage.py test dcim.tests.test_views ipam.tests.test_views +``` + ## Submitting Pull Requests -Once you're happy with your work and have verified that all tests pass, commit your changes and push it upstream to your fork. Always provide descriptive (but not excessively verbose) commit messages. When working on a specific issue, be sure to reference it. +Once you're happy with your work and have verified that all tests pass, commit your changes and push it upstream to your fork. Always provide descriptive (but not excessively verbose) commit messages. When working on a specific issue, be sure to prefix your commit message with the word "Fixes" or "Closes" and the issue number (with a hash mark). This tells GitHub to automatically close the referenced issue once the commit has been merged. ```no-highlight $ git commit -m "Closes #1234: Add IPv5 support" @@ -136,5 +147,5 @@ Once your fork has the new commit, submit a [pull request](https://github.com/ne Once submitted, a maintainer will review your pull request and either merge it or request changes. If changes are needed, you can make them via new commits to your fork: The pull request will update automatically. -!!! note - Remember, pull requests are entertained only for **accepted** issues. If an issue you want to work on hasn't been approved by a maintainer yet, it's best to avoid risking your time and effort on a change that might not be accepted. +!!! note "Remember to Open an Issue First" + Remember, pull requests are permitted only for **accepted** issues. If an issue you want to work on hasn't been approved by a maintainer yet, it's best to avoid risking your time and effort on a change that might not be accepted. (The one exception to this is trivial changes to the documentation or other non-critical resources.) diff --git a/docs/development/index.md b/docs/development/index.md index c10c752d5..03e2cc0c3 100644 --- a/docs/development/index.md +++ b/docs/development/index.md @@ -1,25 +1,25 @@ # NetBox Development -NetBox is maintained as a [GitHub project](https://github.com/netbox-community/netbox) under the Apache 2 license. Users are encouraged to submit GitHub issues for feature requests and bug reports, however we are very selective about pull requests. Please see the `CONTRIBUTING` guide for more direction on contributing to NetBox. +NetBox is maintained as a [GitHub project](https://github.com/netbox-community/netbox) under the Apache 2 license. Users are encouraged to submit GitHub issues for feature requests and bug reports, however we are very selective about pull requests. Each pull request must be preceded by an **approved** issue. Please see the `CONTRIBUTING` guide for more direction on contributing to NetBox. ## Communication There are several official forums for communication among the developers and community members: -* [GitHub issues](https://github.com/netbox-community/netbox/issues) - All feature requests, bug reports, and other substantial changes to the code base **must** be documented in an issue. +* [GitHub issues](https://github.com/netbox-community/netbox/issues) - All feature requests, bug reports, and other substantial changes to the code base **must** be documented in a GitHub issue. * [GitHub Discussions](https://github.com/netbox-community/netbox/discussions) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue. * [#netbox on NetDev Community Slack](https://netdev.chat/) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained long. * [Google Group](https://groups.google.com/g/netbox-discuss) - Legacy mailing list; slowly being phased out in favor of GitHub discussions. ## Governance -NetBox follows the [benevolent dictator](http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel) model of governance, with [Jeremy Stretch](https://github.com/jeremystretch) ultimately responsible for all changes to the code base. While community contributions are welcomed and encouraged, the lead maintainer's primary role is to ensure the project's long-term maintainability and continued focus on its primary functions (in other words, avoid scope creep). +NetBox follows the [benevolent dictator](http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel) model of governance, with [Jeremy Stretch](https://github.com/jeremystretch) ultimately responsible for all changes to the code base. While community contributions are welcomed and encouraged, the lead maintainer's primary role is to ensure the project's long-term maintainability and continued focus on its primary functions. ## Project Structure -All development of the current NetBox release occurs in the `develop` branch; releases are packaged from the `master` branch. The `master` branch should _always_ represent the current stable release in its entirety, such that installing NetBox by either downloading a packaged release or cloning the `master` branch provides the same code base. +All development of the current NetBox release occurs in the `develop` branch; releases are packaged from the `master` branch. The `master` branch should _always_ represent the current stable release in its entirety, such that installing NetBox by either downloading a packaged release or cloning the `master` branch provides the same code base. Only pull requests representing new releases should be merged into `master`. -NetBox components are arranged into functional subsections called _apps_ (a carryover from Django vernacular). Each app holds the models, views, and templates relevant to a particular function: +NetBox components are arranged into Django apps. Each app holds the models, views, and other resources relevant to a particular function: * `circuits`: Communications circuits and providers (not to be confused with power circuits) * `dcim`: Datacenter infrastructure management (sites, racks, and devices) @@ -29,3 +29,6 @@ NetBox components are arranged into functional subsections called _apps_ (a carr * `users`: Authentication and user preferences * `utilities`: Resources which are not user-facing (extendable classes, etc.) * `virtualization`: Virtual machines and clusters +* `wireless`: Wireless links and LANs + +All core functionality is stored within the `netbox/` subdirectory. HTML templates are stored in a common `templates/` directory, with model- and view-specific templates arranged by app. Documentation is kept in the `docs/` root directory. diff --git a/docs/development/models.md b/docs/development/models.md index 62dd016f3..ae1bab7e7 100644 --- a/docs/development/models.md +++ b/docs/development/models.md @@ -17,12 +17,12 @@ The Django [content types](https://docs.djangoproject.com/en/stable/ref/contrib/ * Nesting - These models can be nested recursively to create a hierarchy | Type | Change Logging | Webhooks | Custom Fields | Export Templates | Tags | Journaling | Nesting | -| ------------------ | ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | +| ------------------ | ---------------- | ---------------- |------------------| ---------------- | ---------------- | ---------------- | ---------------- | | Primary | :material-check: | :material-check: | :material-check: | :material-check: | :material-check: | :material-check: | | | Organizational | :material-check: | :material-check: | :material-check: | :material-check: | :material-check: | | | | Nested Group | :material-check: | :material-check: | :material-check: | :material-check: | :material-check: | | :material-check: | | Component | :material-check: | :material-check: | :material-check: | :material-check: | :material-check: | | | -| Component Template | :material-check: | :material-check: | :material-check: | | | | | +| Component Template | :material-check: | :material-check: | | | | | | ## Models Index @@ -44,6 +44,7 @@ The Django [content types](https://docs.djangoproject.com/en/stable/ref/contrib/ * [ipam.ASN](../models/ipam/asn.md) * [ipam.FHRPGroup](../models/ipam/fhrpgroup.md) * [ipam.IPAddress](../models/ipam/ipaddress.md) +* [ipam.IPRange](../models/ipam/iprange.md) * [ipam.Prefix](../models/ipam/prefix.md) * [ipam.RouteTarget](../models/ipam/routetarget.md) * [ipam.Service](../models/ipam/service.md) diff --git a/docs/development/style-guide.md b/docs/development/style-guide.md index 6081867f0..2a6d86ab0 100644 --- a/docs/development/style-guide.md +++ b/docs/development/style-guide.md @@ -1,6 +1,6 @@ # Style Guide -NetBox generally follows the [Django style guide](https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/coding-style/), which is itself based on [PEP 8](https://www.python.org/dev/peps/pep-0008/). [Pycodestyle](https://github.com/pycqa/pycodestyle) is used to validate code formatting, ignoring certain violations. See `scripts/cibuild.sh`. +NetBox generally follows the [Django style guide](https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/coding-style/), which is itself based on [PEP 8](https://www.python.org/dev/peps/pep-0008/). [Pycodestyle](https://github.com/pycqa/pycodestyle) is used to validate code formatting, ignoring certain violations. See `scripts/cibuild.sh` for details. ## PEP 8 Exceptions @@ -30,7 +30,7 @@ pycodestyle --ignore=W504,E501 netbox/ ## Introducing New Dependencies -The introduction of a new dependency is best avoided unless it is absolutely necessary. For small features, it's generally preferable to replicate functionality within the NetBox code base rather than to introduce reliance on an external project. This reduces both the burden of tracking new releases and our exposure to outside bugs and attacks. +The introduction of a new dependency is best avoided unless it is absolutely necessary. For small features, it's generally preferable to replicate functionality within the NetBox code base rather than to introduce reliance on an external project. This reduces both the burden of tracking new releases and our exposure to outside bugs and supply chain attacks. If there's a strong case for introducing a new dependency, it must meet the following criteria: @@ -43,7 +43,7 @@ When adding a new dependency, a short description of the package and the URL of ## General Guidance -* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and open a bug so that the entire code base can be evaluated at a later point. +* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and submit a separate bug report so that the entire code base can be evaluated at a later point. * Prioritize readability over concision. Python is a very flexible language that typically offers several options for expressing a given piece of logic, but some may be more friendly to the reader than others. (List comprehensions are particularly vulnerable to over-optimization.) Always remain considerate of the future reader who may need to interpret your code without the benefit of the context within which you are writing it. From 8d069083535e478de95b0c895f9ac2d24121b04b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 15 Dec 2021 16:57:30 -0500 Subject: [PATCH 03/21] Bulk component add view should use tabs --- netbox/netbox/views/generic.py | 2 +- .../generic/object_bulk_add_component.html | 98 ++++++++++++------- .../templates/generic/object_bulk_edit.html | 21 ++-- 3 files changed, 79 insertions(+), 42 deletions(-) diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index 2c63ee978..7b0d3e472 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -1305,7 +1305,7 @@ class BulkComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, if not selected_objects: messages.warning(request, "No {} were selected.".format(self.parent_model._meta.verbose_name_plural)) return redirect(self.get_return_url(request)) - table = self.table(selected_objects) + table = self.table(selected_objects, orderable=False) if '_create' in request.POST: form = self.form(request.POST) diff --git a/netbox/templates/generic/object_bulk_add_component.html b/netbox/templates/generic/object_bulk_add_component.html index 191b62211..df5545ca5 100644 --- a/netbox/templates/generic/object_bulk_add_component.html +++ b/netbox/templates/generic/object_bulk_add_component.html @@ -1,41 +1,73 @@ {% extends 'base/layout.html' %} +{% load helpers %} {% load form_helpers %} {% load render_table from django_tables2 %} {% block title %}Add {{ model_name|title }}{% endblock %} -{% block content %} -

{{ table.rows|length }} {{ parent_model_name }} selected

-
- {% csrf_token %} - {% if request.POST.return_url %} - - {% endif %} - {% for field in form.hidden_fields %} - {{ field }} - {% endfor %} -
-
-
- {% render_table table 'inc/table.html' %} +{% block tabs %} + +{% endblock %} + +{% block content-wrapper %} +
+ {% block content %} + + {# Component creation form #} +
+ + + {% csrf_token %} + {% if request.POST.return_url %} + + {% endif %} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %} + +
+
+
+
{{ model_name|title }} to Add
+
+ {% for field in form.visible_fields %} + {% render_field field %} + {% endfor %} +
+
+
+
+ Cancel + +
+
+
+ + +
+ + {# Selected objects list #} +
+
+
+ {% render_table table 'inc/table.html' %} +
-
-
-
{{ model_name|title }} to Add
-
- {% for field in form.visible_fields %} - {% render_field field %} - {% endfor %} -
-
-
-
- Cancel - -
-
-
-
- -{% endblock content %} +
+ + {% endblock %} +
+{% endblock %} diff --git a/netbox/templates/generic/object_bulk_edit.html b/netbox/templates/generic/object_bulk_edit.html index cf5897184..31bc07ac4 100644 --- a/netbox/templates/generic/object_bulk_edit.html +++ b/netbox/templates/generic/object_bulk_edit.html @@ -39,14 +39,17 @@
- {% for field in form.visible_fields %} - {% if field.name in form.nullable_fields %} +
+
+ {% for field in form.visible_fields %} + {% if field.name in form.nullable_fields %} {% render_field field bulk_nullable=True %} - {% else %} + {% else %} {% render_field field %} - {% endif %} - {% endfor %} - + {% endif %} + {% endfor %} +
+
Cancel @@ -60,8 +63,10 @@ {# Selected objects list #}
-
- {% render_table table 'inc/table.html' %} +
+
+ {% render_table table 'inc/table.html' %} +
From fd785fc9a5debb5f9ece65ac69a1be4fe41952da Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 16 Dec 2021 08:41:43 -0500 Subject: [PATCH 04/21] Move speed select dropdown menu to widget template --- netbox/templates/utilities/render_field.html | 26 ------------------- .../templates/widgets/select_speed.html | 17 +++++++++++- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/netbox/templates/utilities/render_field.html b/netbox/templates/utilities/render_field.html index c32ca4e4a..9f3779bfe 100644 --- a/netbox/templates/utilities/render_field.html +++ b/netbox/templates/utilities/render_field.html @@ -60,32 +60,6 @@
-{% elif field|widget_type == 'selectspeedwidget' %} - {# This is outside the widget because bootstrap requires a specific order for border-radius purposes. #} -
- -
- -
-
- {% elif field|widget_type == 'fileinput' %}
+ {% include 'django/forms/widgets/number.html' %} + + +
From 68eb6fc3c1b144d2264f36885988b1f94dcd6135 Mon Sep 17 00:00:00 2001 From: Christian Jonak Date: Thu, 16 Dec 2021 18:14:56 +0100 Subject: [PATCH 05/21] fix: use center freq instead of beginning of freq range for 6Ghz --- netbox/wireless/choices.py | 450 ++++++++++++++++++------------------- 1 file changed, 223 insertions(+), 227 deletions(-) diff --git a/netbox/wireless/choices.py b/netbox/wireless/choices.py index 2813eafd6..103539fca 100644 --- a/netbox/wireless/choices.py +++ b/netbox/wireless/choices.py @@ -86,119 +86,118 @@ class WirelessChannelChoices(ChoiceSet): # 6 GHz CHANNEL_6G_1 = '6g-1-5955-20' - CHANNEL_6G_3 = '6g-3-5955-40' - CHANNEL_6G_5 = '6g-5-5965-20' - CHANNEL_6G_7 = '6g-7-5975-80' - CHANNEL_6G_9 = '6g-9-5985-20' - CHANNEL_6G_11 = '6g-11-5995-40' - CHANNEL_6G_13 = '6g-13-6005-20' - CHANNEL_6G_15 = '6g-15-6015-160' - CHANNEL_6G_17 = '6g-17-6025-20' - CHANNEL_6G_19 = '6g-19-6035-40' - CHANNEL_6G_21 = '6g-21-6045-20' - CHANNEL_6G_23 = '6g-23-6055-80' - CHANNEL_6G_25 = '6g-25-6065-20' - CHANNEL_6G_28 = '6g-28-6080-40' - CHANNEL_6G_29 = '6g-29-6085-20' - CHANNEL_6G_31 = '6g-31-6095-320' - CHANNEL_6G_33 = '6g-33-6105-20' - CHANNEL_6G_36 = '6g-36-6120-40' - CHANNEL_6G_37 = '6g-37-6125-20' - CHANNEL_6G_39 = '6g-39-6135-80' - CHANNEL_6G_41 = '6g-41-6145-20' - CHANNEL_6G_44 = '6g-44-6160-40' - CHANNEL_6G_45 = '6g-45-6165-20' - CHANNEL_6G_47 = '6g-47-6175-160' - CHANNEL_6G_49 = '6g-49-6185-20' - CHANNEL_6G_52 = '6g-52-6200-40' - CHANNEL_6G_53 = '6g-53-6205-20' - CHANNEL_6G_55 = '6g-55-6215-80' - CHANNEL_6G_57 = '6g-57-6225-20' - CHANNEL_6G_60 = '6g-60-6240-40' - CHANNEL_6G_61 = '6g-61-6245-20' - CHANNEL_6G_65 = '6g-65-6265-20' - CHANNEL_6G_68 = '6g-68-6280-40' - CHANNEL_6G_69 = '6g-69-6285-20' - CHANNEL_6G_71 = '6g-71-6295-80' - CHANNEL_6G_73 = '6g-73-6305-20' - CHANNEL_6G_76 = '6g-76-6320-40' - CHANNEL_6G_77 = '6g-77-6325-20' - CHANNEL_6G_79 = '6g-79-6335-160' - CHANNEL_6G_81 = '6g-81-6345-20' - CHANNEL_6G_84 = '6g-84-6360-40' - CHANNEL_6G_85 = '6g-85-6365-20' - CHANNEL_6G_87 = '6g-87-6375-80' - CHANNEL_6G_89 = '6g-89-6385-20' - CHANNEL_6G_92 = '6g-92-6400-40' - CHANNEL_6G_93 = '6g-93-6405-20' - CHANNEL_6G_95 = '6g-95-6415-320' - CHANNEL_6G_97 = '6g-97-6425-20' - CHANNEL_6G_100 = '6g-100-6440-40' - CHANNEL_6G_101 = '6g-101-6445-20' - CHANNEL_6G_103 = '6g-103-6455-80' - CHANNEL_6G_105 = '6g-105-6465-20' - CHANNEL_6G_108 = '6g-108-6480-40' - CHANNEL_6G_109 = '6g-109-6485-20' - CHANNEL_6G_111 = '6g-111-6495-160' - CHANNEL_6G_113 = '6g-113-6505-20' - CHANNEL_6G_116 = '6g-116-6520-40' - CHANNEL_6G_117 = '6g-117-6525-20' - CHANNEL_6G_119 = '6g-119-6535-80' - CHANNEL_6G_121 = '6g-121-6545-20' - CHANNEL_6G_124 = '6g-124-6560-40' - CHANNEL_6G_125 = '6g-125-6565-20' - CHANNEL_6G_129 = '6g-129-6585-20' - CHANNEL_6G_132 = '6g-132-6600-40' - CHANNEL_6G_133 = '6g-133-6605-20' - CHANNEL_6G_135 = '6g-135-6615-80' - CHANNEL_6G_137 = '6g-137-6625-20' - CHANNEL_6G_140 = '6g-140-6640-40' - CHANNEL_6G_141 = '6g-141-6645-20' - CHANNEL_6G_143 = '6g-143-6655-160' - CHANNEL_6G_145 = '6g-145-6665-20' - CHANNEL_6G_148 = '6g-148-6680-40' - CHANNEL_6G_149 = '6g-149-6685-20' - CHANNEL_6G_151 = '6g-151-6695-80' - CHANNEL_6G_153 = '6g-153-6705-20' - CHANNEL_6G_156 = '6g-156-6720-40' - CHANNEL_6G_157 = '6g-157-6725-20' - CHANNEL_6G_159 = '6g-159-6735-320' - CHANNEL_6G_161 = '6g-161-6745-20' - CHANNEL_6G_164 = '6g-164-6760-40' - CHANNEL_6G_165 = '6g-165-6765-20' - CHANNEL_6G_167 = '6g-167-6775-80' - CHANNEL_6G_169 = '6g-169-6785-20' - CHANNEL_6G_172 = '6g-172-6800-40' - CHANNEL_6G_173 = '6g-173-6805-20' - CHANNEL_6G_175 = '6g-175-6815-160' - CHANNEL_6G_177 = '6g-177-6825-20' - CHANNEL_6G_180 = '6g-180-6840-40' - CHANNEL_6G_181 = '6g-181-6845-20' - CHANNEL_6G_183 = '6g-183-6855-80' - CHANNEL_6G_185 = '6g-185-6865-20' - CHANNEL_6G_188 = '6g-188-6880-40' - CHANNEL_6G_189 = '6g-189-6885-20' - CHANNEL_6G_193 = '6g-193-6905-20' - CHANNEL_6G_196 = '6g-196-6920-40' - CHANNEL_6G_197 = '6g-197-6925-20' - CHANNEL_6G_199 = '6g-199-6935-80' - CHANNEL_6G_201 = '6g-201-6945-20' - CHANNEL_6G_204 = '6g-204-6960-40' - CHANNEL_6G_205 = '6g-205-6965-20' - CHANNEL_6G_207 = '6g-207-6975-160' - CHANNEL_6G_209 = '6g-209-6985-20' - CHANNEL_6G_212 = '6g-212-7000-40' - CHANNEL_6G_213 = '6g-213-7005-20' - CHANNEL_6G_215 = '6g-215-7015-80' - CHANNEL_6G_217 = '6g-217-7025-20' - CHANNEL_6G_220 = '6g-220-7040-40' - CHANNEL_6G_221 = '6g-221-7045-20' - CHANNEL_6G_225 = '6g-225-7065-20' - CHANNEL_6G_228 = '6g-228-7080-40' - CHANNEL_6G_229 = '6g-229-7085-20' - CHANNEL_6G_233 = '6g-233-7105-20' + CHANNEL_6G_3 = '6g-3-5975-40' + CHANNEL_6G_5 = '6g-5-5975-20' + CHANNEL_6G_7 = '6g-7-6015-80' + CHANNEL_6G_9 = '6g-9-5995-20' + CHANNEL_6G_11 = '6g-11-6015-40' + CHANNEL_6G_13 = '6g-13-6015-20' + CHANNEL_6G_15 = '6g-15-6095-160' + CHANNEL_6G_17 = '6g-17-6035-20' + CHANNEL_6G_19 = '6g-19-6055-40' + CHANNEL_6G_21 = '6g-21-6055-20' + CHANNEL_6G_23 = '6g-23-6095-80' + CHANNEL_6G_25 = '6g-25-6075-20' + CHANNEL_6G_28 = '6g-28-6100-40' + CHANNEL_6G_29 = '6g-29-6095-20' + CHANNEL_6G_31 = '6g-31-6255-320' + CHANNEL_6G_33 = '6g-33-6115-20' + CHANNEL_6G_36 = '6g-36-6140-40' + CHANNEL_6G_37 = '6g-37-6135-20' + CHANNEL_6G_39 = '6g-39-6175-80' + CHANNEL_6G_41 = '6g-41-6155-20' + CHANNEL_6G_44 = '6g-44-6180-40' + CHANNEL_6G_45 = '6g-45-6175-20' + CHANNEL_6G_47 = '6g-47-6255-160' + CHANNEL_6G_49 = '6g-49-6195-20' + CHANNEL_6G_52 = '6g-52-6220-40' + CHANNEL_6G_53 = '6g-53-6215-20' + CHANNEL_6G_55 = '6g-55-6255-80' + CHANNEL_6G_57 = '6g-57-6235-20' + CHANNEL_6G_60 = '6g-60-6260-40' + CHANNEL_6G_61 = '6g-61-6255-20' + CHANNEL_6G_65 = '6g-65-6275-20' + CHANNEL_6G_68 = '6g-68-6300-40' + CHANNEL_6G_69 = '6g-69-6295-20' + CHANNEL_6G_71 = '6g-71-6335-80' + CHANNEL_6G_73 = '6g-73-6315-20' + CHANNEL_6G_76 = '6g-76-6340-40' + CHANNEL_6G_77 = '6g-77-6335-20' + CHANNEL_6G_79 = '6g-79-6415-160' + CHANNEL_6G_81 = '6g-81-6355-20' + CHANNEL_6G_84 = '6g-84-6380-40' + CHANNEL_6G_85 = '6g-85-6375-20' + CHANNEL_6G_87 = '6g-87-6415-80' + CHANNEL_6G_89 = '6g-89-6395-20' + CHANNEL_6G_92 = '6g-92-6420-40' + CHANNEL_6G_93 = '6g-93-6415-20' + CHANNEL_6G_95 = '6g-95-6575-320' + CHANNEL_6G_97 = '6g-97-6435-20' + CHANNEL_6G_100 = '6g-100-6460-40' + CHANNEL_6G_101 = '6g-101-6455-20' + CHANNEL_6G_103 = '6g-103-6495-80' + CHANNEL_6G_105 = '6g-105-6475-20' + CHANNEL_6G_108 = '6g-108-6500-40' + CHANNEL_6G_109 = '6g-109-6495-20' + CHANNEL_6G_111 = '6g-111-6575-160' + CHANNEL_6G_113 = '6g-113-6515-20' + CHANNEL_6G_116 = '6g-116-6540-40' + CHANNEL_6G_117 = '6g-117-6535-20' + CHANNEL_6G_119 = '6g-119-6575-80' + CHANNEL_6G_121 = '6g-121-6555-20' + CHANNEL_6G_124 = '6g-124-6580-40' + CHANNEL_6G_125 = '6g-125-6575-20' + CHANNEL_6G_129 = '6g-129-6595-20' + CHANNEL_6G_132 = '6g-132-6620-40' + CHANNEL_6G_133 = '6g-133-6615-20' + CHANNEL_6G_135 = '6g-135-6655-80' + CHANNEL_6G_137 = '6g-137-6635-20' + CHANNEL_6G_140 = '6g-140-6660-40' + CHANNEL_6G_141 = '6g-141-6655-20' + CHANNEL_6G_143 = '6g-143-6735-160' + CHANNEL_6G_145 = '6g-145-6675-20' + CHANNEL_6G_148 = '6g-148-6700-40' + CHANNEL_6G_149 = '6g-149-6695-20' + CHANNEL_6G_151 = '6g-151-6735-80' + CHANNEL_6G_153 = '6g-153-6715-20' + CHANNEL_6G_156 = '6g-156-6740-40' + CHANNEL_6G_157 = '6g-157-6735-20' + CHANNEL_6G_159 = '6g-159-6895-320' + CHANNEL_6G_161 = '6g-161-6755-20' + CHANNEL_6G_164 = '6g-164-6780-40' + CHANNEL_6G_165 = '6g-165-6775-20' + CHANNEL_6G_167 = '6g-167-6815-80' + CHANNEL_6G_169 = '6g-169-6795-20' + CHANNEL_6G_172 = '6g-172-6820-40' + CHANNEL_6G_173 = '6g-173-6815-20' + CHANNEL_6G_175 = '6g-175-6895-160' + CHANNEL_6G_177 = '6g-177-6835-20' + CHANNEL_6G_180 = '6g-180-6860-40' + CHANNEL_6G_181 = '6g-181-6855-20' + CHANNEL_6G_183 = '6g-183-6895-80' + CHANNEL_6G_185 = '6g-185-6875-20' + CHANNEL_6G_188 = '6g-188-6900-40' + CHANNEL_6G_189 = '6g-189-6895-20' + CHANNEL_6G_193 = '6g-193-6915-20' + CHANNEL_6G_196 = '6g-196-6940-40' + CHANNEL_6G_197 = '6g-197-6935-20' + CHANNEL_6G_199 = '6g-199-6975-80' + CHANNEL_6G_201 = '6g-201-6955-20' + CHANNEL_6G_204 = '6g-204-6980-40' + CHANNEL_6G_205 = '6g-205-6975-20' + CHANNEL_6G_207 = '6g-207-7055-160' + CHANNEL_6G_209 = '6g-209-6995-20' + CHANNEL_6G_212 = '6g-212-7020-40' + CHANNEL_6G_213 = '6g-213-7015-20' + CHANNEL_6G_215 = '6g-215-7055-80' + CHANNEL_6G_217 = '6g-217-7035-20' + CHANNEL_6G_220 = '6g-220-7060-40' + CHANNEL_6G_221 = '6g-221-7055-20' + CHANNEL_6G_225 = '6g-225-7075-20' + CHANNEL_6G_228 = '6g-228-7100-40' + CHANNEL_6G_229 = '6g-229-7095-20' + CHANNEL_6G_233 = '6g-233-7115-20' - # 60 GHz CHANNEL_60G_1 = '60g-1-58320-2160' CHANNEL_60G_2 = '60g-2-60480-2160' @@ -219,8 +218,6 @@ class WirelessChannelChoices(ChoiceSet): CHANNEL_60G_26 = '60g-26-63720-6480' CHANNEL_60G_27 = '60g-27-65880-6480' - - CHOICES = ( ( '2.4 GHz (802.11b/g/n/ax)', @@ -302,119 +299,118 @@ class WirelessChannelChoices(ChoiceSet): ( '6 GHz (802.11ax)', ( - (CHANNEL_6G_1, '1 (5945/20 MHz)'), - (CHANNEL_6G_3, '3 (5955/40 MHz)'), - (CHANNEL_6G_5, '5 (5965/20 MHz)'), - (CHANNEL_6G_7, '7 (5975/80 MHz)'), - (CHANNEL_6G_9, '9 (5985/20 MHz)'), - (CHANNEL_6G_11, '11 (5995/40 MHz)'), - (CHANNEL_6G_13, '13 (6005/20 MHz)'), - (CHANNEL_6G_15, '15 (6015/160 MHz)'), - (CHANNEL_6G_17, '17 (6025/20 MHz)'), - (CHANNEL_6G_19, '19 (6035/40 MHz)'), - (CHANNEL_6G_21, '21 (6045/20 MHz)'), - (CHANNEL_6G_23, '23 (6055/80 MHz)'), - (CHANNEL_6G_25, '25 (6065/20 MHz)'), - (CHANNEL_6G_28, '28 (6080/40 MHz)'), - (CHANNEL_6G_29, '29 (6085/20 MHz)'), - (CHANNEL_6G_31, '31 (6095/320 MHz)'), - (CHANNEL_6G_33, '33 (6105/20 MHz)'), - (CHANNEL_6G_36, '36 (6120/40 MHz)'), - (CHANNEL_6G_37, '37 (6125/20 MHz)'), - (CHANNEL_6G_39, '39 (6135/80 MHz)'), - (CHANNEL_6G_41, '41 (6145/20 MHz)'), - (CHANNEL_6G_44, '44 (6160/40 MHz)'), - (CHANNEL_6G_45, '45 (6165/20 MHz)'), - (CHANNEL_6G_47, '47 (6175/160 MHz)'), - (CHANNEL_6G_49, '49 (6185/20 MHz)'), - (CHANNEL_6G_52, '52 (6200/40 MHz)'), - (CHANNEL_6G_53, '53 (6205/20 MHz)'), - (CHANNEL_6G_55, '55 (6215/80 MHz)'), - (CHANNEL_6G_57, '57 (6225/20 MHz)'), - (CHANNEL_6G_60, '60 (6240/40 MHz)'), - (CHANNEL_6G_61, '61 (6245/20 MHz)'), - (CHANNEL_6G_65, '65 (6265/20 MHz)'), - (CHANNEL_6G_68, '68 (6280/40 MHz)'), - (CHANNEL_6G_69, '69 (6285/20 MHz)'), - (CHANNEL_6G_71, '71 (6295/80 MHz)'), - (CHANNEL_6G_73, '73 (6305/20 MHz)'), - (CHANNEL_6G_76, '76 (6320/40 MHz)'), - (CHANNEL_6G_77, '77 (6325/20 MHz)'), - (CHANNEL_6G_79, '79 (6335/160 MHz)'), - (CHANNEL_6G_81, '81 (6345/20 MHz)'), - (CHANNEL_6G_84, '84 (6360/40 MHz)'), - (CHANNEL_6G_85, '85 (6365/20 MHz)'), - (CHANNEL_6G_87, '87 (6375/80 MHz)'), - (CHANNEL_6G_89, '89 (6385/20 MHz)'), - (CHANNEL_6G_92, '92 (6400/40 MHz)'), - (CHANNEL_6G_93, '93 (6405/20 MHz)'), - (CHANNEL_6G_95, '95 (6415/320 MHz)'), - (CHANNEL_6G_97, '97 (6425/20 MHz)'), - (CHANNEL_6G_100, '100 (6440/40 MHz)'), - (CHANNEL_6G_101, '101 (6445/20 MHz)'), - (CHANNEL_6G_103, '103 (6455/80 MHz)'), - (CHANNEL_6G_105, '105 (6465/20 MHz)'), - (CHANNEL_6G_108, '108 (6480/40 MHz)'), - (CHANNEL_6G_109, '109 (6485/20 MHz)'), - (CHANNEL_6G_111, '111 (6495/160 MHz)'), - (CHANNEL_6G_113, '113 (6505/20 MHz)'), - (CHANNEL_6G_116, '116 (6520/40 MHz)'), - (CHANNEL_6G_117, '117 (6525/20 MHz)'), - (CHANNEL_6G_119, '119 (6535/80 MHz)'), - (CHANNEL_6G_121, '121 (6545/20 MHz)'), - (CHANNEL_6G_124, '124 (6560/40 MHz)'), - (CHANNEL_6G_125, '125 (6565/20 MHz)'), - (CHANNEL_6G_129, '129 (6585/20 MHz)'), - (CHANNEL_6G_132, '132 (6600/40 MHz)'), - (CHANNEL_6G_133, '133 (6605/20 MHz)'), - (CHANNEL_6G_135, '135 (6615/80 MHz)'), - (CHANNEL_6G_137, '137 (6625/20 MHz)'), - (CHANNEL_6G_140, '140 (6640/40 MHz)'), - (CHANNEL_6G_141, '141 (6645/20 MHz)'), - (CHANNEL_6G_143, '143 (6655/160 MHz)'), - (CHANNEL_6G_145, '145 (6665/20 MHz)'), - (CHANNEL_6G_148, '148 (6680/40 MHz)'), - (CHANNEL_6G_149, '149 (6685/20 MHz)'), - (CHANNEL_6G_151, '151 (6695/80 MHz)'), - (CHANNEL_6G_153, '153 (6705/20 MHz)'), - (CHANNEL_6G_156, '156 (6720/40 MHz)'), - (CHANNEL_6G_157, '157 (6725/20 MHz)'), - (CHANNEL_6G_159, '159 (6735/320 MHz)'), - (CHANNEL_6G_161, '161 (6745/20 MHz)'), - (CHANNEL_6G_164, '164 (6760/40 MHz)'), - (CHANNEL_6G_165, '165 (6765/20 MHz)'), - (CHANNEL_6G_167, '167 (6775/80 MHz)'), - (CHANNEL_6G_169, '169 (6785/20 MHz)'), - (CHANNEL_6G_172, '172 (6800/40 MHz)'), - (CHANNEL_6G_173, '173 (6805/20 MHz)'), - (CHANNEL_6G_175, '175 (6815/160 MHz)'), - (CHANNEL_6G_177, '177 (6825/20 MHz)'), - (CHANNEL_6G_180, '180 (6840/40 MHz)'), - (CHANNEL_6G_181, '181 (6845/20 MHz)'), - (CHANNEL_6G_183, '183 (6855/80 MHz)'), - (CHANNEL_6G_185, '185 (6865/20 MHz)'), - (CHANNEL_6G_188, '188 (6880/40 MHz)'), - (CHANNEL_6G_189, '189 (6885/20 MHz)'), - (CHANNEL_6G_193, '193 (6905/20 MHz)'), - (CHANNEL_6G_196, '196 (6920/40 MHz)'), - (CHANNEL_6G_197, '197 (6925/20 MHz)'), - (CHANNEL_6G_199, '199 (6935/80 MHz)'), - (CHANNEL_6G_201, '201 (6945/20 MHz)'), - (CHANNEL_6G_204, '204 (6960/40 MHz)'), - (CHANNEL_6G_205, '205 (6965/20 MHz)'), - (CHANNEL_6G_207, '207 (6975/160 MHz)'), - (CHANNEL_6G_209, '209 (6985/20 MHz)'), - (CHANNEL_6G_212, '212 (7000/40 MHz)'), - (CHANNEL_6G_213, '213 (7005/20 MHz)'), - (CHANNEL_6G_215, '215 (7015/80 MHz)'), - (CHANNEL_6G_217, '217 (7025/20 MHz)'), - (CHANNEL_6G_220, '220 (7040/40 MHz)'), - (CHANNEL_6G_221, '221 (7045/20 MHz)'), - (CHANNEL_6G_225, '225 (7065/20 MHz)'), - (CHANNEL_6G_228, '228 (7080/40 MHz)'), - (CHANNEL_6G_229, '229 (7085/20 MHz)'), - (CHANNEL_6G_233, '233 (7105/20 MHz)'), - + (CHANNEL_6G_1, '1 (5955/20 MHz)'), + (CHANNEL_6G_3, '3 (5975/40 MHz)'), + (CHANNEL_6G_5, '5 (5975/20 MHz)'), + (CHANNEL_6G_7, '7 (6015/80 MHz)'), + (CHANNEL_6G_9, '9 (5995/20 MHz)'), + (CHANNEL_6G_11, '11 (6015/40 MHz)'), + (CHANNEL_6G_13, '13 (6015/20 MHz)'), + (CHANNEL_6G_15, '15 (6095/160 MHz)'), + (CHANNEL_6G_17, '17 (6035/20 MHz)'), + (CHANNEL_6G_19, '19 (6055/40 MHz)'), + (CHANNEL_6G_21, '21 (6055/20 MHz)'), + (CHANNEL_6G_23, '23 (6095/80 MHz)'), + (CHANNEL_6G_25, '25 (6075/20 MHz)'), + (CHANNEL_6G_28, '28 (6100/40 MHz)'), + (CHANNEL_6G_29, '29 (6095/20 MHz)'), + (CHANNEL_6G_31, '31 (6255/320 MHz)'), + (CHANNEL_6G_33, '33 (6115/20 MHz)'), + (CHANNEL_6G_36, '36 (6140/40 MHz)'), + (CHANNEL_6G_37, '37 (6135/20 MHz)'), + (CHANNEL_6G_39, '39 (6175/80 MHz)'), + (CHANNEL_6G_41, '41 (6155/20 MHz)'), + (CHANNEL_6G_44, '44 (6180/40 MHz)'), + (CHANNEL_6G_45, '45 (6175/20 MHz)'), + (CHANNEL_6G_47, '47 (6255/160 MHz)'), + (CHANNEL_6G_49, '49 (6195/20 MHz)'), + (CHANNEL_6G_52, '52 (6220/40 MHz)'), + (CHANNEL_6G_53, '53 (6215/20 MHz)'), + (CHANNEL_6G_55, '55 (6255/80 MHz)'), + (CHANNEL_6G_57, '57 (6235/20 MHz)'), + (CHANNEL_6G_60, '60 (6260/40 MHz)'), + (CHANNEL_6G_61, '61 (6255/20 MHz)'), + (CHANNEL_6G_65, '65 (6275/20 MHz)'), + (CHANNEL_6G_68, '68 (6300/40 MHz)'), + (CHANNEL_6G_69, '69 (6295/20 MHz)'), + (CHANNEL_6G_71, '71 (6335/80 MHz)'), + (CHANNEL_6G_73, '73 (6315/20 MHz)'), + (CHANNEL_6G_76, '76 (6340/40 MHz)'), + (CHANNEL_6G_77, '77 (6335/20 MHz)'), + (CHANNEL_6G_79, '79 (6415/160 MHz)'), + (CHANNEL_6G_81, '81 (6355/20 MHz)'), + (CHANNEL_6G_84, '84 (6380/40 MHz)'), + (CHANNEL_6G_85, '85 (6375/20 MHz)'), + (CHANNEL_6G_87, '87 (6415/80 MHz)'), + (CHANNEL_6G_89, '89 (6395/20 MHz)'), + (CHANNEL_6G_92, '92 (6420/40 MHz)'), + (CHANNEL_6G_93, '93 (6415/20 MHz)'), + (CHANNEL_6G_95, '95 (6575/320 MHz)'), + (CHANNEL_6G_97, '97 (6435/20 MHz)'), + (CHANNEL_6G_100, '100 (6460/40 MHz)'), + (CHANNEL_6G_101, '101 (6455/20 MHz)'), + (CHANNEL_6G_103, '103 (6495/80 MHz)'), + (CHANNEL_6G_105, '105 (6475/20 MHz)'), + (CHANNEL_6G_108, '108 (6500/40 MHz)'), + (CHANNEL_6G_109, '109 (6495/20 MHz)'), + (CHANNEL_6G_111, '111 (6575/160 MHz)'), + (CHANNEL_6G_113, '113 (6515/20 MHz)'), + (CHANNEL_6G_116, '116 (6540/40 MHz)'), + (CHANNEL_6G_117, '117 (6535/20 MHz)'), + (CHANNEL_6G_119, '119 (6575/80 MHz)'), + (CHANNEL_6G_121, '121 (6555/20 MHz)'), + (CHANNEL_6G_124, '124 (6580/40 MHz)'), + (CHANNEL_6G_125, '125 (6575/20 MHz)'), + (CHANNEL_6G_129, '129 (6595/20 MHz)'), + (CHANNEL_6G_132, '132 (6620/40 MHz)'), + (CHANNEL_6G_133, '133 (6615/20 MHz)'), + (CHANNEL_6G_135, '135 (6655/80 MHz)'), + (CHANNEL_6G_137, '137 (6635/20 MHz)'), + (CHANNEL_6G_140, '140 (6660/40 MHz)'), + (CHANNEL_6G_141, '141 (6655/20 MHz)'), + (CHANNEL_6G_143, '143 (6735/160 MHz)'), + (CHANNEL_6G_145, '145 (6675/20 MHz)'), + (CHANNEL_6G_148, '148 (6700/40 MHz)'), + (CHANNEL_6G_149, '149 (6695/20 MHz)'), + (CHANNEL_6G_151, '151 (6735/80 MHz)'), + (CHANNEL_6G_153, '153 (6715/20 MHz)'), + (CHANNEL_6G_156, '156 (6740/40 MHz)'), + (CHANNEL_6G_157, '157 (6735/20 MHz)'), + (CHANNEL_6G_159, '159 (6895/320 MHz)'), + (CHANNEL_6G_161, '161 (6755/20 MHz)'), + (CHANNEL_6G_164, '164 (6780/40 MHz)'), + (CHANNEL_6G_165, '165 (6775/20 MHz)'), + (CHANNEL_6G_167, '167 (6815/80 MHz)'), + (CHANNEL_6G_169, '169 (6795/20 MHz)'), + (CHANNEL_6G_172, '172 (6820/40 MHz)'), + (CHANNEL_6G_173, '173 (6815/20 MHz)'), + (CHANNEL_6G_175, '175 (6895/160 MHz)'), + (CHANNEL_6G_177, '177 (6835/20 MHz)'), + (CHANNEL_6G_180, '180 (6860/40 MHz)'), + (CHANNEL_6G_181, '181 (6855/20 MHz)'), + (CHANNEL_6G_183, '183 (6895/80 MHz)'), + (CHANNEL_6G_185, '185 (6875/20 MHz)'), + (CHANNEL_6G_188, '188 (6900/40 MHz)'), + (CHANNEL_6G_189, '189 (6895/20 MHz)'), + (CHANNEL_6G_193, '193 (6915/20 MHz)'), + (CHANNEL_6G_196, '196 (6940/40 MHz)'), + (CHANNEL_6G_197, '197 (6935/20 MHz)'), + (CHANNEL_6G_199, '199 (6975/80 MHz)'), + (CHANNEL_6G_201, '201 (6955/20 MHz)'), + (CHANNEL_6G_204, '204 (6980/40 MHz)'), + (CHANNEL_6G_205, '205 (6975/20 MHz)'), + (CHANNEL_6G_207, '207 (7055/160 MHz)'), + (CHANNEL_6G_209, '209 (6995/20 MHz)'), + (CHANNEL_6G_212, '212 (7020/40 MHz)'), + (CHANNEL_6G_213, '213 (7015/20 MHz)'), + (CHANNEL_6G_215, '215 (7055/80 MHz)'), + (CHANNEL_6G_217, '217 (7035/20 MHz)'), + (CHANNEL_6G_220, '220 (7060/40 MHz)'), + (CHANNEL_6G_221, '221 (7055/20 MHz)'), + (CHANNEL_6G_225, '225 (7075/20 MHz)'), + (CHANNEL_6G_228, '228 (7100/40 MHz)'), + (CHANNEL_6G_229, '229 (7095/20 MHz)'), + (CHANNEL_6G_233, '233 (7115/20 MHz)'), ) ), ( From 7735a539e9c67c7b465b5fd8b87a837386277949 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 16 Dec 2021 12:44:18 -0500 Subject: [PATCH 06/21] Fixes #8088: Improve legibility of text in labels with light-colored backgrounds --- docs/release-notes/version-3.1.md | 1 + netbox/utilities/templatetags/helpers.py | 2 +- netbox/utilities/utils.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index fa2a1fd4a..2cc3536b3 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -16,6 +16,7 @@ * [#8077](https://github.com/netbox-community/netbox/issues/8077) - Fix exception when attaching image to location, circuit, or power panel * [#8078](https://github.com/netbox-community/netbox/issues/8078) - Add missing wireless models to `lsmodels()` in `nbshell` * [#8079](https://github.com/netbox-community/netbox/issues/8079) - Fix validation of LLDP neighbors when connected device has an asset tag +* [#8088](https://github.com/netbox-community/netbox/issues/8088) - Improve legibility of text in labels with light-colored backgrounds --- diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py index 267bf7115..db9c40fc5 100644 --- a/netbox/utilities/templatetags/helpers.py +++ b/netbox/utilities/templatetags/helpers.py @@ -233,7 +233,7 @@ def fgcolor(value): value = value.lower().strip('#') if not re.match('^[0-9a-f]{6}$', value): return '' - return '#{}'.format(foreground_color(value)) + return f'#{foreground_color(value)}' @register.filter() diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index 203c12b3f..f12bc2569 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -53,9 +53,10 @@ def foreground_color(bg_color, dark='000000', light='ffffff'): :param dark: RBG color code for dark text :param light: RBG color code for light text """ + THRESHOLD = 150 bg_color = bg_color.strip('#') r, g, b = [int(bg_color[c:c + 2], 16) for c in (0, 2, 4)] - if r * 0.299 + g * 0.587 + b * 0.114 > 186: + if r * 0.299 + g * 0.587 + b * 0.114 > THRESHOLD: return dark else: return light From cab07c7c4b94be7cd4e62d096f8d91070baf8ed4 Mon Sep 17 00:00:00 2001 From: Christian Jonak Date: Thu, 16 Dec 2021 19:28:39 +0100 Subject: [PATCH 07/21] fix: non 20Mhz-wide channel centers --- netbox/wireless/choices.py | 212 ++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/netbox/wireless/choices.py b/netbox/wireless/choices.py index 103539fca..c01bff358 100644 --- a/netbox/wireless/choices.py +++ b/netbox/wireless/choices.py @@ -86,115 +86,115 @@ class WirelessChannelChoices(ChoiceSet): # 6 GHz CHANNEL_6G_1 = '6g-1-5955-20' - CHANNEL_6G_3 = '6g-3-5975-40' + CHANNEL_6G_3 = '6g-3-5965-40' CHANNEL_6G_5 = '6g-5-5975-20' - CHANNEL_6G_7 = '6g-7-6015-80' + CHANNEL_6G_7 = '6g-7-5985-80' CHANNEL_6G_9 = '6g-9-5995-20' - CHANNEL_6G_11 = '6g-11-6015-40' + CHANNEL_6G_11 = '6g-11-6005-40' CHANNEL_6G_13 = '6g-13-6015-20' - CHANNEL_6G_15 = '6g-15-6095-160' + CHANNEL_6G_15 = '6g-15-6025-160' CHANNEL_6G_17 = '6g-17-6035-20' - CHANNEL_6G_19 = '6g-19-6055-40' + CHANNEL_6G_19 = '6g-19-6045-40' CHANNEL_6G_21 = '6g-21-6055-20' - CHANNEL_6G_23 = '6g-23-6095-80' + CHANNEL_6G_23 = '6g-23-6065-80' CHANNEL_6G_25 = '6g-25-6075-20' - CHANNEL_6G_28 = '6g-28-6100-40' + CHANNEL_6G_27 = '6g-27-6085-40' CHANNEL_6G_29 = '6g-29-6095-20' - CHANNEL_6G_31 = '6g-31-6255-320' + CHANNEL_6G_31 = '6g-31-6105-320' CHANNEL_6G_33 = '6g-33-6115-20' - CHANNEL_6G_36 = '6g-36-6140-40' + CHANNEL_6G_35 = '6g-35-6125-40' CHANNEL_6G_37 = '6g-37-6135-20' - CHANNEL_6G_39 = '6g-39-6175-80' + CHANNEL_6G_39 = '6g-39-6145-80' CHANNEL_6G_41 = '6g-41-6155-20' - CHANNEL_6G_44 = '6g-44-6180-40' + CHANNEL_6G_43 = '6g-43-6165-40' CHANNEL_6G_45 = '6g-45-6175-20' - CHANNEL_6G_47 = '6g-47-6255-160' + CHANNEL_6G_47 = '6g-47-6185-160' CHANNEL_6G_49 = '6g-49-6195-20' - CHANNEL_6G_52 = '6g-52-6220-40' + CHANNEL_6G_51 = '6g-51-6205-40' CHANNEL_6G_53 = '6g-53-6215-20' - CHANNEL_6G_55 = '6g-55-6255-80' + CHANNEL_6G_55 = '6g-55-6225-80' CHANNEL_6G_57 = '6g-57-6235-20' - CHANNEL_6G_60 = '6g-60-6260-40' + CHANNEL_6G_59 = '6g-59-6245-40' CHANNEL_6G_61 = '6g-61-6255-20' CHANNEL_6G_65 = '6g-65-6275-20' - CHANNEL_6G_68 = '6g-68-6300-40' + CHANNEL_6G_67 = '6g-67-6285-40' CHANNEL_6G_69 = '6g-69-6295-20' - CHANNEL_6G_71 = '6g-71-6335-80' + CHANNEL_6G_71 = '6g-71-6305-80' CHANNEL_6G_73 = '6g-73-6315-20' - CHANNEL_6G_76 = '6g-76-6340-40' + CHANNEL_6G_75 = '6g-75-6325-40' CHANNEL_6G_77 = '6g-77-6335-20' - CHANNEL_6G_79 = '6g-79-6415-160' + CHANNEL_6G_79 = '6g-79-6345-160' CHANNEL_6G_81 = '6g-81-6355-20' - CHANNEL_6G_84 = '6g-84-6380-40' + CHANNEL_6G_83 = '6g-83-6365-40' CHANNEL_6G_85 = '6g-85-6375-20' - CHANNEL_6G_87 = '6g-87-6415-80' + CHANNEL_6G_87 = '6g-87-6385-80' CHANNEL_6G_89 = '6g-89-6395-20' - CHANNEL_6G_92 = '6g-92-6420-40' + CHANNEL_6G_91 = '6g-91-6405-40' CHANNEL_6G_93 = '6g-93-6415-20' - CHANNEL_6G_95 = '6g-95-6575-320' + CHANNEL_6G_95 = '6g-95-6425-320' CHANNEL_6G_97 = '6g-97-6435-20' - CHANNEL_6G_100 = '6g-100-6460-40' + CHANNEL_6G_99 = '6g-99-6445-40' CHANNEL_6G_101 = '6g-101-6455-20' - CHANNEL_6G_103 = '6g-103-6495-80' + CHANNEL_6G_103 = '6g-103-6465-80' CHANNEL_6G_105 = '6g-105-6475-20' - CHANNEL_6G_108 = '6g-108-6500-40' + CHANNEL_6G_107 = '6g-107-6485-40' CHANNEL_6G_109 = '6g-109-6495-20' - CHANNEL_6G_111 = '6g-111-6575-160' + CHANNEL_6G_111 = '6g-111-6505-160' CHANNEL_6G_113 = '6g-113-6515-20' - CHANNEL_6G_116 = '6g-116-6540-40' + CHANNEL_6G_115 = '6g-115-6525-40' CHANNEL_6G_117 = '6g-117-6535-20' - CHANNEL_6G_119 = '6g-119-6575-80' + CHANNEL_6G_119 = '6g-119-6545-80' CHANNEL_6G_121 = '6g-121-6555-20' - CHANNEL_6G_124 = '6g-124-6580-40' + CHANNEL_6G_123 = '6g-123-6565-40' CHANNEL_6G_125 = '6g-125-6575-20' CHANNEL_6G_129 = '6g-129-6595-20' - CHANNEL_6G_132 = '6g-132-6620-40' + CHANNEL_6G_131 = '6g-131-6605-40' CHANNEL_6G_133 = '6g-133-6615-20' - CHANNEL_6G_135 = '6g-135-6655-80' + CHANNEL_6G_135 = '6g-135-6625-80' CHANNEL_6G_137 = '6g-137-6635-20' - CHANNEL_6G_140 = '6g-140-6660-40' + CHANNEL_6G_139 = '6g-139-6645-40' CHANNEL_6G_141 = '6g-141-6655-20' - CHANNEL_6G_143 = '6g-143-6735-160' + CHANNEL_6G_143 = '6g-143-6665-160' CHANNEL_6G_145 = '6g-145-6675-20' - CHANNEL_6G_148 = '6g-148-6700-40' + CHANNEL_6G_147 = '6g-147-6685-40' CHANNEL_6G_149 = '6g-149-6695-20' - CHANNEL_6G_151 = '6g-151-6735-80' + CHANNEL_6G_151 = '6g-151-6705-80' CHANNEL_6G_153 = '6g-153-6715-20' - CHANNEL_6G_156 = '6g-156-6740-40' + CHANNEL_6G_155 = '6g-155-6725-40' CHANNEL_6G_157 = '6g-157-6735-20' - CHANNEL_6G_159 = '6g-159-6895-320' + CHANNEL_6G_159 = '6g-159-6745-320' CHANNEL_6G_161 = '6g-161-6755-20' - CHANNEL_6G_164 = '6g-164-6780-40' + CHANNEL_6G_163 = '6g-163-6765-40' CHANNEL_6G_165 = '6g-165-6775-20' - CHANNEL_6G_167 = '6g-167-6815-80' + CHANNEL_6G_167 = '6g-167-6785-80' CHANNEL_6G_169 = '6g-169-6795-20' - CHANNEL_6G_172 = '6g-172-6820-40' + CHANNEL_6G_171 = '6g-171-6805-40' CHANNEL_6G_173 = '6g-173-6815-20' - CHANNEL_6G_175 = '6g-175-6895-160' + CHANNEL_6G_175 = '6g-175-6825-160' CHANNEL_6G_177 = '6g-177-6835-20' - CHANNEL_6G_180 = '6g-180-6860-40' + CHANNEL_6G_179 = '6g-179-6845-40' CHANNEL_6G_181 = '6g-181-6855-20' - CHANNEL_6G_183 = '6g-183-6895-80' + CHANNEL_6G_183 = '6g-183-6865-80' CHANNEL_6G_185 = '6g-185-6875-20' - CHANNEL_6G_188 = '6g-188-6900-40' + CHANNEL_6G_187 = '6g-187-6885-40' CHANNEL_6G_189 = '6g-189-6895-20' CHANNEL_6G_193 = '6g-193-6915-20' - CHANNEL_6G_196 = '6g-196-6940-40' + CHANNEL_6G_195 = '6g-195-6925-40' CHANNEL_6G_197 = '6g-197-6935-20' - CHANNEL_6G_199 = '6g-199-6975-80' + CHANNEL_6G_199 = '6g-199-6945-80' CHANNEL_6G_201 = '6g-201-6955-20' - CHANNEL_6G_204 = '6g-204-6980-40' + CHANNEL_6G_203 = '6g-203-6965-40' CHANNEL_6G_205 = '6g-205-6975-20' - CHANNEL_6G_207 = '6g-207-7055-160' + CHANNEL_6G_207 = '6g-207-6985-160' CHANNEL_6G_209 = '6g-209-6995-20' - CHANNEL_6G_212 = '6g-212-7020-40' + CHANNEL_6G_211 = '6g-211-7005-40' CHANNEL_6G_213 = '6g-213-7015-20' - CHANNEL_6G_215 = '6g-215-7055-80' + CHANNEL_6G_215 = '6g-215-7025-80' CHANNEL_6G_217 = '6g-217-7035-20' - CHANNEL_6G_220 = '6g-220-7060-40' + CHANNEL_6G_219 = '6g-219-7045-40' CHANNEL_6G_221 = '6g-221-7055-20' CHANNEL_6G_225 = '6g-225-7075-20' - CHANNEL_6G_228 = '6g-228-7100-40' + CHANNEL_6G_227 = '6g-227-7085-40' CHANNEL_6G_229 = '6g-229-7095-20' CHANNEL_6G_233 = '6g-233-7115-20' @@ -300,115 +300,115 @@ class WirelessChannelChoices(ChoiceSet): '6 GHz (802.11ax)', ( (CHANNEL_6G_1, '1 (5955/20 MHz)'), - (CHANNEL_6G_3, '3 (5975/40 MHz)'), + (CHANNEL_6G_3, '3 (5965/40 MHz)'), (CHANNEL_6G_5, '5 (5975/20 MHz)'), - (CHANNEL_6G_7, '7 (6015/80 MHz)'), + (CHANNEL_6G_7, '7 (5985/80 MHz)'), (CHANNEL_6G_9, '9 (5995/20 MHz)'), - (CHANNEL_6G_11, '11 (6015/40 MHz)'), + (CHANNEL_6G_11, '11 (6005/40 MHz)'), (CHANNEL_6G_13, '13 (6015/20 MHz)'), - (CHANNEL_6G_15, '15 (6095/160 MHz)'), + (CHANNEL_6G_15, '15 (6025/160 MHz)'), (CHANNEL_6G_17, '17 (6035/20 MHz)'), - (CHANNEL_6G_19, '19 (6055/40 MHz)'), + (CHANNEL_6G_19, '19 (6045/40 MHz)'), (CHANNEL_6G_21, '21 (6055/20 MHz)'), - (CHANNEL_6G_23, '23 (6095/80 MHz)'), + (CHANNEL_6G_23, '23 (6065/80 MHz)'), (CHANNEL_6G_25, '25 (6075/20 MHz)'), - (CHANNEL_6G_28, '28 (6100/40 MHz)'), + (CHANNEL_6G_27, '27 (6085/40 MHz)'), (CHANNEL_6G_29, '29 (6095/20 MHz)'), - (CHANNEL_6G_31, '31 (6255/320 MHz)'), + (CHANNEL_6G_31, '31 (6105/320 MHz)'), (CHANNEL_6G_33, '33 (6115/20 MHz)'), - (CHANNEL_6G_36, '36 (6140/40 MHz)'), + (CHANNEL_6G_35, '35 (6125/40 MHz)'), (CHANNEL_6G_37, '37 (6135/20 MHz)'), - (CHANNEL_6G_39, '39 (6175/80 MHz)'), + (CHANNEL_6G_39, '39 (6145/80 MHz)'), (CHANNEL_6G_41, '41 (6155/20 MHz)'), - (CHANNEL_6G_44, '44 (6180/40 MHz)'), + (CHANNEL_6G_43, '43 (6165/40 MHz)'), (CHANNEL_6G_45, '45 (6175/20 MHz)'), - (CHANNEL_6G_47, '47 (6255/160 MHz)'), + (CHANNEL_6G_47, '47 (6185/160 MHz)'), (CHANNEL_6G_49, '49 (6195/20 MHz)'), - (CHANNEL_6G_52, '52 (6220/40 MHz)'), + (CHANNEL_6G_51, '51 (6205/40 MHz)'), (CHANNEL_6G_53, '53 (6215/20 MHz)'), - (CHANNEL_6G_55, '55 (6255/80 MHz)'), + (CHANNEL_6G_55, '55 (6225/80 MHz)'), (CHANNEL_6G_57, '57 (6235/20 MHz)'), - (CHANNEL_6G_60, '60 (6260/40 MHz)'), + (CHANNEL_6G_59, '59 (6245/40 MHz)'), (CHANNEL_6G_61, '61 (6255/20 MHz)'), (CHANNEL_6G_65, '65 (6275/20 MHz)'), - (CHANNEL_6G_68, '68 (6300/40 MHz)'), + (CHANNEL_6G_67, '67 (6285/40 MHz)'), (CHANNEL_6G_69, '69 (6295/20 MHz)'), - (CHANNEL_6G_71, '71 (6335/80 MHz)'), + (CHANNEL_6G_71, '71 (6305/80 MHz)'), (CHANNEL_6G_73, '73 (6315/20 MHz)'), - (CHANNEL_6G_76, '76 (6340/40 MHz)'), + (CHANNEL_6G_75, '75 (6325/40 MHz)'), (CHANNEL_6G_77, '77 (6335/20 MHz)'), - (CHANNEL_6G_79, '79 (6415/160 MHz)'), + (CHANNEL_6G_79, '79 (6345/160 MHz)'), (CHANNEL_6G_81, '81 (6355/20 MHz)'), - (CHANNEL_6G_84, '84 (6380/40 MHz)'), + (CHANNEL_6G_83, '83 (6365/40 MHz)'), (CHANNEL_6G_85, '85 (6375/20 MHz)'), - (CHANNEL_6G_87, '87 (6415/80 MHz)'), + (CHANNEL_6G_87, '87 (6385/80 MHz)'), (CHANNEL_6G_89, '89 (6395/20 MHz)'), - (CHANNEL_6G_92, '92 (6420/40 MHz)'), + (CHANNEL_6G_91, '91 (6405/40 MHz)'), (CHANNEL_6G_93, '93 (6415/20 MHz)'), - (CHANNEL_6G_95, '95 (6575/320 MHz)'), + (CHANNEL_6G_95, '95 (6425/320 MHz)'), (CHANNEL_6G_97, '97 (6435/20 MHz)'), - (CHANNEL_6G_100, '100 (6460/40 MHz)'), + (CHANNEL_6G_99, '99 (6445/40 MHz)'), (CHANNEL_6G_101, '101 (6455/20 MHz)'), - (CHANNEL_6G_103, '103 (6495/80 MHz)'), + (CHANNEL_6G_103, '103 (6465/80 MHz)'), (CHANNEL_6G_105, '105 (6475/20 MHz)'), - (CHANNEL_6G_108, '108 (6500/40 MHz)'), + (CHANNEL_6G_107, '107 (6485/40 MHz)'), (CHANNEL_6G_109, '109 (6495/20 MHz)'), - (CHANNEL_6G_111, '111 (6575/160 MHz)'), + (CHANNEL_6G_111, '111 (6505/160 MHz)'), (CHANNEL_6G_113, '113 (6515/20 MHz)'), - (CHANNEL_6G_116, '116 (6540/40 MHz)'), + (CHANNEL_6G_115, '115 (6525/40 MHz)'), (CHANNEL_6G_117, '117 (6535/20 MHz)'), - (CHANNEL_6G_119, '119 (6575/80 MHz)'), + (CHANNEL_6G_119, '119 (6545/80 MHz)'), (CHANNEL_6G_121, '121 (6555/20 MHz)'), - (CHANNEL_6G_124, '124 (6580/40 MHz)'), + (CHANNEL_6G_123, '123 (6565/40 MHz)'), (CHANNEL_6G_125, '125 (6575/20 MHz)'), (CHANNEL_6G_129, '129 (6595/20 MHz)'), - (CHANNEL_6G_132, '132 (6620/40 MHz)'), + (CHANNEL_6G_131, '131 (6605/40 MHz)'), (CHANNEL_6G_133, '133 (6615/20 MHz)'), - (CHANNEL_6G_135, '135 (6655/80 MHz)'), + (CHANNEL_6G_135, '135 (6625/80 MHz)'), (CHANNEL_6G_137, '137 (6635/20 MHz)'), - (CHANNEL_6G_140, '140 (6660/40 MHz)'), + (CHANNEL_6G_139, '139 (6645/40 MHz)'), (CHANNEL_6G_141, '141 (6655/20 MHz)'), - (CHANNEL_6G_143, '143 (6735/160 MHz)'), + (CHANNEL_6G_143, '143 (6665/160 MHz)'), (CHANNEL_6G_145, '145 (6675/20 MHz)'), - (CHANNEL_6G_148, '148 (6700/40 MHz)'), + (CHANNEL_6G_147, '147 (6685/40 MHz)'), (CHANNEL_6G_149, '149 (6695/20 MHz)'), - (CHANNEL_6G_151, '151 (6735/80 MHz)'), + (CHANNEL_6G_151, '151 (6705/80 MHz)'), (CHANNEL_6G_153, '153 (6715/20 MHz)'), - (CHANNEL_6G_156, '156 (6740/40 MHz)'), + (CHANNEL_6G_155, '155 (6725/40 MHz)'), (CHANNEL_6G_157, '157 (6735/20 MHz)'), - (CHANNEL_6G_159, '159 (6895/320 MHz)'), + (CHANNEL_6G_159, '159 (6745/320 MHz)'), (CHANNEL_6G_161, '161 (6755/20 MHz)'), - (CHANNEL_6G_164, '164 (6780/40 MHz)'), + (CHANNEL_6G_163, '163 (6765/40 MHz)'), (CHANNEL_6G_165, '165 (6775/20 MHz)'), - (CHANNEL_6G_167, '167 (6815/80 MHz)'), + (CHANNEL_6G_167, '167 (6785/80 MHz)'), (CHANNEL_6G_169, '169 (6795/20 MHz)'), - (CHANNEL_6G_172, '172 (6820/40 MHz)'), + (CHANNEL_6G_171, '171 (6805/40 MHz)'), (CHANNEL_6G_173, '173 (6815/20 MHz)'), - (CHANNEL_6G_175, '175 (6895/160 MHz)'), + (CHANNEL_6G_175, '175 (6825/160 MHz)'), (CHANNEL_6G_177, '177 (6835/20 MHz)'), - (CHANNEL_6G_180, '180 (6860/40 MHz)'), + (CHANNEL_6G_179, '179 (6845/40 MHz)'), (CHANNEL_6G_181, '181 (6855/20 MHz)'), - (CHANNEL_6G_183, '183 (6895/80 MHz)'), + (CHANNEL_6G_183, '183 (6865/80 MHz)'), (CHANNEL_6G_185, '185 (6875/20 MHz)'), - (CHANNEL_6G_188, '188 (6900/40 MHz)'), + (CHANNEL_6G_187, '187 (6885/40 MHz)'), (CHANNEL_6G_189, '189 (6895/20 MHz)'), (CHANNEL_6G_193, '193 (6915/20 MHz)'), - (CHANNEL_6G_196, '196 (6940/40 MHz)'), + (CHANNEL_6G_195, '195 (6925/40 MHz)'), (CHANNEL_6G_197, '197 (6935/20 MHz)'), - (CHANNEL_6G_199, '199 (6975/80 MHz)'), + (CHANNEL_6G_199, '199 (6945/80 MHz)'), (CHANNEL_6G_201, '201 (6955/20 MHz)'), - (CHANNEL_6G_204, '204 (6980/40 MHz)'), + (CHANNEL_6G_203, '203 (6965/40 MHz)'), (CHANNEL_6G_205, '205 (6975/20 MHz)'), - (CHANNEL_6G_207, '207 (7055/160 MHz)'), + (CHANNEL_6G_207, '207 (6985/160 MHz)'), (CHANNEL_6G_209, '209 (6995/20 MHz)'), - (CHANNEL_6G_212, '212 (7020/40 MHz)'), + (CHANNEL_6G_211, '211 (7005/40 MHz)'), (CHANNEL_6G_213, '213 (7015/20 MHz)'), - (CHANNEL_6G_215, '215 (7055/80 MHz)'), + (CHANNEL_6G_215, '215 (7025/80 MHz)'), (CHANNEL_6G_217, '217 (7035/20 MHz)'), - (CHANNEL_6G_220, '220 (7060/40 MHz)'), + (CHANNEL_6G_219, '219 (7045/40 MHz)'), (CHANNEL_6G_221, '221 (7055/20 MHz)'), (CHANNEL_6G_225, '225 (7075/20 MHz)'), - (CHANNEL_6G_228, '228 (7100/40 MHz)'), + (CHANNEL_6G_227, '227 (7085/40 MHz)'), (CHANNEL_6G_229, '229 (7095/20 MHz)'), (CHANNEL_6G_233, '233 (7115/20 MHz)'), ) From cf4a55bc2f3af952aa4aa6e520dc4771cbdbc67b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Sat, 18 Dec 2021 13:52:39 -0500 Subject: [PATCH 08/21] Closes #8107: Correct template name --- .../{ipadress_edit_header.html => ipaddress_edit_header.html} | 0 netbox/templates/ipam/ipaddress_assign.html | 2 +- netbox/templates/ipam/ipaddress_bulk_add.html | 2 +- netbox/templates/ipam/ipaddress_edit.html | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename netbox/templates/ipam/inc/{ipadress_edit_header.html => ipaddress_edit_header.html} (100%) diff --git a/netbox/templates/ipam/inc/ipadress_edit_header.html b/netbox/templates/ipam/inc/ipaddress_edit_header.html similarity index 100% rename from netbox/templates/ipam/inc/ipadress_edit_header.html rename to netbox/templates/ipam/inc/ipaddress_edit_header.html diff --git a/netbox/templates/ipam/ipaddress_assign.html b/netbox/templates/ipam/ipaddress_assign.html index 3965feebc..8187525db 100644 --- a/netbox/templates/ipam/ipaddress_assign.html +++ b/netbox/templates/ipam/ipaddress_assign.html @@ -7,7 +7,7 @@ {% block title %}Assign an IP Address{% endblock title %} {% block tabs %} - {% include 'ipam/inc/ipadress_edit_header.html' with active_tab='assign' %} + {% include 'ipam/inc/ipaddress_edit_header.html' with active_tab='assign' %} {% endblock %} {% block form %} diff --git a/netbox/templates/ipam/ipaddress_bulk_add.html b/netbox/templates/ipam/ipaddress_bulk_add.html index 84e71b9fc..84c2a4d11 100644 --- a/netbox/templates/ipam/ipaddress_bulk_add.html +++ b/netbox/templates/ipam/ipaddress_bulk_add.html @@ -5,7 +5,7 @@ {% block title %}Bulk Add IP Addresses{% endblock %} {% block tabs %} - {% include 'ipam/inc/ipadress_edit_header.html' with active_tab='bulk_add' %} + {% include 'ipam/inc/ipaddress_edit_header.html' with active_tab='bulk_add' %} {% endblock %} {% block form %} diff --git a/netbox/templates/ipam/ipaddress_edit.html b/netbox/templates/ipam/ipaddress_edit.html index e611aefbb..f4b21397a 100644 --- a/netbox/templates/ipam/ipaddress_edit.html +++ b/netbox/templates/ipam/ipaddress_edit.html @@ -4,7 +4,7 @@ {% load helpers %} {% block tabs %} - {% include 'ipam/inc/ipadress_edit_header.html' with active_tab='add' %} + {% include 'ipam/inc/ipaddress_edit_header.html' with active_tab='add' %} {% endblock tabs %} {% block form %} From 628e186846818bb8d0b57054c50f5dcad3e7039d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Sat, 18 Dec 2021 14:02:01 -0500 Subject: [PATCH 09/21] Closes #8108: Improve breadcrumb links for device/VM components --- docs/release-notes/version-3.1.md | 1 + netbox/templates/dcim/consoleport.html | 9 ++++++++- netbox/templates/dcim/consoleserverport.html | 9 ++++++++- netbox/templates/dcim/device_component.html | 9 --------- netbox/templates/dcim/devicebay.html | 9 ++++++++- netbox/templates/dcim/frontport.html | 9 ++++++++- netbox/templates/dcim/interface.html | 9 ++++++++- netbox/templates/dcim/inventoryitem.html | 9 ++++++++- netbox/templates/dcim/poweroutlet.html | 9 ++++++++- netbox/templates/dcim/powerport.html | 9 ++++++++- netbox/templates/dcim/rearport.html | 9 ++++++++- netbox/templates/virtualization/vminterface.html | 4 +++- 12 files changed, 76 insertions(+), 19 deletions(-) delete mode 100644 netbox/templates/dcim/device_component.html diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 2cc3536b3..57c734055 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -8,6 +8,7 @@ * [#8057](https://github.com/netbox-community/netbox/issues/8057) - Dynamic object tables using HTMX * [#8080](https://github.com/netbox-community/netbox/issues/8080) - Link to NAT IPs for device/VM primary IPs * [#8081](https://github.com/netbox-community/netbox/issues/8081) - Allow creating services directly from navigation menu +* [#8108](https://github.com/netbox-community/netbox/issues/8108) - Improve breadcrumb links for device/VM components ### Bug Fixes diff --git a/netbox/templates/dcim/consoleport.html b/netbox/templates/dcim/consoleport.html index 60711eb9d..38cfb90ae 100644 --- a/netbox/templates/dcim/consoleport.html +++ b/netbox/templates/dcim/consoleport.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/dcim/consoleserverport.html b/netbox/templates/dcim/consoleserverport.html index f65af3285..b44c4a9b8 100644 --- a/netbox/templates/dcim/consoleserverport.html +++ b/netbox/templates/dcim/consoleserverport.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/dcim/device_component.html b/netbox/templates/dcim/device_component.html deleted file mode 100644 index fbd542774..000000000 --- a/netbox/templates/dcim/device_component.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends 'generic/object.html' %} -{% load helpers %} -{% load perms %} -{% load plugins %} - -{% block breadcrumbs %} - {{ block.super }} - -{% endblock %} diff --git a/netbox/templates/dcim/devicebay.html b/netbox/templates/dcim/devicebay.html index ff8f90db2..3a470bec5 100644 --- a/netbox/templates/dcim/devicebay.html +++ b/netbox/templates/dcim/devicebay.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/dcim/frontport.html b/netbox/templates/dcim/frontport.html index 6cc3d482f..05be82fc9 100644 --- a/netbox/templates/dcim/frontport.html +++ b/netbox/templates/dcim/frontport.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index 353bfe115..bd0569c39 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -1,8 +1,15 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} {% load render_table from django_tables2 %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block extra_controls %} {% if perms.dcim.add_interface and not object.is_virtual %} diff --git a/netbox/templates/dcim/inventoryitem.html b/netbox/templates/dcim/inventoryitem.html index 163d8edb3..36ba0469f 100644 --- a/netbox/templates/dcim/inventoryitem.html +++ b/netbox/templates/dcim/inventoryitem.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/dcim/poweroutlet.html b/netbox/templates/dcim/poweroutlet.html index 396ef42a8..3f2c469af 100644 --- a/netbox/templates/dcim/poweroutlet.html +++ b/netbox/templates/dcim/poweroutlet.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/dcim/powerport.html b/netbox/templates/dcim/powerport.html index dfe428c50..f38edec8e 100644 --- a/netbox/templates/dcim/powerport.html +++ b/netbox/templates/dcim/powerport.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/dcim/rearport.html b/netbox/templates/dcim/rearport.html index b3ecce3ad..311ccd7ff 100644 --- a/netbox/templates/dcim/rearport.html +++ b/netbox/templates/dcim/rearport.html @@ -1,7 +1,14 @@ -{% extends 'dcim/device_component.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + {% block content %}
diff --git a/netbox/templates/virtualization/vminterface.html b/netbox/templates/virtualization/vminterface.html index 68b036950..6fcaf2a3e 100644 --- a/netbox/templates/virtualization/vminterface.html +++ b/netbox/templates/virtualization/vminterface.html @@ -5,7 +5,9 @@ {% block breadcrumbs %} {{ block.super }} - + {% endblock %} {% block content %} From b00eeb86eaf10a5dd5696f203fe909407509c5dc Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Sat, 18 Dec 2021 14:16:37 -0500 Subject: [PATCH 10/21] Fixes #8096: Fix DataError during change logging of objects with very long string representations --- docs/release-notes/version-3.1.md | 1 + netbox/netbox/models.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 57c734055..117e22cca 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -18,6 +18,7 @@ * [#8078](https://github.com/netbox-community/netbox/issues/8078) - Add missing wireless models to `lsmodels()` in `nbshell` * [#8079](https://github.com/netbox-community/netbox/issues/8079) - Fix validation of LLDP neighbors when connected device has an asset tag * [#8088](https://github.com/netbox-community/netbox/issues/8088) - Improve legibility of text in labels with light-colored backgrounds +* [#8096](https://github.com/netbox-community/netbox/issues/8096) - Fix DataError during change logging of objects with very long string representations --- diff --git a/netbox/netbox/models.py b/netbox/netbox/models.py index 091bae7bd..91240ee90 100644 --- a/netbox/netbox/models.py +++ b/netbox/netbox/models.py @@ -62,7 +62,7 @@ class ChangeLoggingMixin(models.Model): objectchange = ObjectChange( changed_object=self, related_object=related_object, - object_repr=str(self), + object_repr=str(self)[:200], action=action ) if hasattr(self, '_prechange_snapshot'): From 2db82a73a542016015a3d8545c3d5b4be9bdb49d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Sat, 18 Dec 2021 14:19:57 -0500 Subject: [PATCH 11/21] #8096: Include only first assigned IP in FHRPGroup string representation --- netbox/ipam/models/fhrp.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index 0a099499f..9e721c65f 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -58,13 +58,11 @@ class FHRPGroup(PrimaryModel): def __str__(self): name = f'{self.get_protocol_display()}: {self.group_id}' - # Append the list of assigned IP addresses to serve as an additional identifier + # Append the first assigned IP addresses (if any) to serve as an additional identifier if self.pk: - ip_addresses = [ - str(ip.address) for ip in self.ip_addresses.all() - ] - if ip_addresses: - return f"{name} ({', '.join(ip_addresses)})" + ip_address = self.ip_addresses.first() + if ip_address: + return f"{name} ({ip_address})" return name From 4723500c5fb8a6e069a132f99f5ed34cc4e9416d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Sat, 18 Dec 2021 14:26:32 -0500 Subject: [PATCH 12/21] Fixes #8092: Rack elevations should not include device asset tags --- docs/release-notes/version-3.1.md | 1 + netbox/dcim/svg.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 117e22cca..9f9ed3b44 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -18,6 +18,7 @@ * [#8078](https://github.com/netbox-community/netbox/issues/8078) - Add missing wireless models to `lsmodels()` in `nbshell` * [#8079](https://github.com/netbox-community/netbox/issues/8079) - Fix validation of LLDP neighbors when connected device has an asset tag * [#8088](https://github.com/netbox-community/netbox/issues/8088) - Improve legibility of text in labels with light-colored backgrounds +* [#8092](https://github.com/netbox-community/netbox/issues/8092) - Rack elevations should not include device asset tags * [#8096](https://github.com/netbox-community/netbox/issues/8096) - Fix DataError during change logging of objects with very long string representations --- diff --git a/netbox/dcim/svg.py b/netbox/dcim/svg.py index e90890eeb..e19e8fa2f 100644 --- a/netbox/dcim/svg.py +++ b/netbox/dcim/svg.py @@ -18,6 +18,10 @@ __all__ = ( ) +def get_device_name(device): + return device.name or str(device.device_type) + + class RackElevationSVG: """ Use this class to render a rack elevation as an SVG image. @@ -85,7 +89,7 @@ class RackElevationSVG: return drawing def _draw_device_front(self, drawing, device, start, end, text): - name = str(device) + name = get_device_name(device) if device.devicebay_count: name += ' ({}/{})'.format(device.get_children().count(), device.devicebay_count) @@ -120,7 +124,7 @@ class RackElevationSVG: rect = drawing.rect(start, end, class_="slot blocked") rect.set_desc(self._get_device_description(device)) drawing.add(rect) - drawing.add(drawing.text(str(device), insert=text)) + drawing.add(drawing.text(get_device_name(device), insert=text)) # Embed rear device type image if one exists if self.include_images and device.device_type.rear_image: @@ -132,9 +136,9 @@ class RackElevationSVG: ) image.fit(scale='slice') drawing.add(image) - drawing.add(drawing.text(str(device), insert=text, stroke='black', + drawing.add(drawing.text(get_device_name(device), insert=text, stroke='black', stroke_width='0.2em', stroke_linejoin='round', class_='device-image-label')) - drawing.add(drawing.text(str(device), insert=text, fill='white', class_='device-image-label')) + drawing.add(drawing.text(get_device_name(device), insert=text, fill='white', class_='device-image-label')) @staticmethod def _draw_empty(drawing, rack, start, end, text, id_, face_id, class_, reservation): From 85f9690377fefcc6f1f871060946f42eb783baed Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Sat, 18 Dec 2021 14:30:28 -0500 Subject: [PATCH 13/21] Closes #8083: Removed "related devices" panel from device view --- docs/release-notes/version-3.1.md | 1 + netbox/dcim/views.py | 10 ------- netbox/templates/dcim/device.html | 45 +++++-------------------------- 3 files changed, 7 insertions(+), 49 deletions(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 9f9ed3b44..f70ee6951 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -8,6 +8,7 @@ * [#8057](https://github.com/netbox-community/netbox/issues/8057) - Dynamic object tables using HTMX * [#8080](https://github.com/netbox-community/netbox/issues/8080) - Link to NAT IPs for device/VM primary IPs * [#8081](https://github.com/netbox-community/netbox/issues/8081) - Allow creating services directly from navigation menu +* [#8083](https://github.com/netbox-community/netbox/issues/8083) - Removed "related devices" panel from device view * [#8108](https://github.com/netbox-community/netbox/issues/8108) - Improve breadcrumb links for device/VM components ### Bug Fixes diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index a1d1102de..3180d47b1 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1316,19 +1316,9 @@ class DeviceView(generic.ObjectView): # Services services = Service.objects.restrict(request.user, 'view').filter(device=instance) - # Find up to ten devices in the same site with the same functional role for quick reference. - related_devices = Device.objects.restrict(request.user, 'view').filter( - site=instance.site, device_role=instance.device_role - ).exclude( - pk=instance.pk - ).prefetch_related( - 'rack', 'device_type__manufacturer' - )[:10] - return { 'services': services, 'vc_members': vc_members, - 'related_devices': related_devices, 'active_tab': 'device', } diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index b7750a640..31f2a18bd 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -148,6 +148,12 @@
{% endif %} + {% include 'inc/panels/custom_fields.html' %} + {% include 'inc/panels/tags.html' %} + {% include 'inc/panels/comments.html' %} + {% plugin_left_page object %} +
+
Management @@ -220,12 +226,6 @@
- {% include 'inc/panels/custom_fields.html' %} - {% include 'inc/panels/tags.html' %} - {% include 'inc/panels/comments.html' %} - {% plugin_left_page object %} -
-
{% if object.powerports.exists and object.poweroutlets.exists %}
@@ -298,39 +298,6 @@
{% include 'inc/panels/contacts.html' %} {% include 'inc/panels/image_attachments.html' %} -
-
- Related Devices -
-
- {% if related_devices %} - - - - - - - {% for rd in related_devices %} - - - - - - {% endfor %} -
DeviceRackType
- {{ rd }} - - {% if rd.rack %} - {{ rd.rack }} - {% else %} - - {% endif %} - {{ rd.device_type }}
- {% else %} -
None
- {% endif %} -
-
{% plugin_right_page object %}
From 7b23856cc87839e8c8cc7f76fd96fedcc0d7cf73 Mon Sep 17 00:00:00 2001 From: Arseny Maslennikov Date: Fri, 17 Dec 2021 17:31:36 +0300 Subject: [PATCH 14/21] templates: add an opaque icon for mobile home screens The netbox_touch-icon-180.png icon was produced by rendering netbox_icon.svg into a 160x160 square, centered in a 180x180 PNG filled by the background colour of #212529. In other words, it is a screenshot of the following HTML element: ```html
``` --- .../project-static/img/netbox_touch-icon-180.png | Bin 0 -> 4054 bytes netbox/templates/base/base.html | 1 + 2 files changed, 1 insertion(+) create mode 100644 netbox/project-static/img/netbox_touch-icon-180.png diff --git a/netbox/project-static/img/netbox_touch-icon-180.png b/netbox/project-static/img/netbox_touch-icon-180.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9b7108691bcb5daacc254629432d3c0554e22e GIT binary patch literal 4054 zcmd5~4#yE8kpJ3BM?Oy*U43tsMX+$=0CyjGTG z*MSlD=i@v9{0#v>P3!?NY2`A-TktXRrD$h7vg{_9-m!w3VkV6 znr~3YU_J3q0$LAY6mCe5i(r1!m$;Nua=7phtrf9d{FeXnUHOaFd6Hi`ShBoD6JMod z89SPoV-#LUQ}V6~z^yAeHDqrbFFLKrX2&J;|B!vyaNU zA1HHXLe9H6Wv$*oU3i>y`VQE}Gy1Md0M`iN2ka!TZs2r4-dOWOdAP%iNTzRuZm^F< zxT=2f+u@im>h4kWGav66SPm#Gb>4ajD|wVq>E!R}6e1!w!EUKJ54pF#QMaO3=ql_5 ztX;#7zc=*Cg>IZWy-WC~|BmZK_F#IQmqmE+do8KXwBFD_a%jS+cDhzvlSN+ zy;f#0)v>$E_{3~GvY8Jt()>}Z{|rJBWVJSv(`{*P(7<(_k&^}DAEzW&M%IufWX@10 zmuy?;u>z-|!L(RmfhT$0j5`mmF1@pRv>u&A+^p&3c#LpC*IJ2C_=&RVG0rnl|-FE{` z66~L9iPb6oW~syh*SMhBo^aT(1{x?eAgM)Tl{P&awJsPJj5UFJW44vH7UBY;22gp# z-A(-nlshAQ@-*UR-OxA8@}$}5wr)8W^?=?6nnS;5i2S^PbQeO9=1O-I|K)IOJV|-S zY?Q-1rF&fYIO;T$bIrl&`E;!)wXdSN%DrR(0wXIWRcvI-VGP{DAAx)f={}>5E2~DI zcyRmsIXC`jsUWNHa_-bP8s#`+=JlHD^A17l3W&&6wG4q{RVUo$s-j}kIP73lwd7wq zrWSi=!@uMNqxr^r8UI4x-!w|=sXe@j`p;#8gF5lUeN zb?i3PYJqd=EB1U%m*^>YeWi>|xVlrxDU3v;;C=roL*=rH6auyv){R?7^jgZyFdb8- zaYb{>A}H}E86OPZ%r(FChs_^=JUH2~crcRSkns_CykdMpSf72*U*&|3?Oa6?%_xo) zCf%`Qq@fJ5AO}L*ZoM$pyDRUVt8QZGII+DhM>6zFYyIVsQS=!f(}7P6>ths1`8EY+ z8C~p*+H=XP`D|SvYUr0b_3rCmrPu)tC+s6kM>$mZxLLN6QleN^GNRQb#)U~a(saMiTp{@zl8;Ms;wWXfedQ^OeNv?>4)Wxql()>qVx_yqs zn_Hsi2{^ltiOPZGj#hC$gy+*dK~$3%eNS;fSqx{|H z?HSg0yvTUq>z^@-lzf&8YbrR@RSL6rH!hooveAm@oK%!t5*Nm zKvLM`b(nv1S_7N&S`2i+N6gDP^qM7!&q;<71`A>SPHURT51g6as8^b-?H7$!O1Pz4 zqBB`Qvr~1~Is;9A!wQdfm9te@h9w-9=(?p8e|MB0hFmHh5qkHFM~Hb_X3SPi1N}4HG@o}aTH%S zo}lO|lvZ_J1GR5|?SAzb*d9hd;^i%(g;3>HsgGB+=6jQA?Qz{BZrO=S4ow^lYaeM4 z8(O;>9 zLnOVh;LmopDgB|DTu{WA)1-X8=(CH`@bpcy(M7TC*kkZB6hhrgi@og!tKgmN?}rpo zp1Jf+wrHjH$IYclKzy2!13UObDHzQ{Kp1alL$1IN^t5XzN&%X#r1h_ycjMjk(qy)p z;TY(g-%c?;Gm|kfYHf6JJcb|eNoawnsh|YTG)!7Inu{bPFO)nU0+>?cXgvIav@nj~ z8hRFR;1faGKYuc@2mZjzNjFkM<-);N{FVOXhe`P!!jCff^VB|h_Yowp6Av!%F=2bt z&kjThFKPK=lBTT36L4d&4?Cd5+jShDfFb%SFLl~0zwEacxER_bjevsW zddlH#9QY)Aa#7~8Dw8$PE&O*<(8d8js_UJYl_NC713Mj&L{@c6OrzWT3=E6Cr2X8m z2*!BPMVAV0!J9cul2qI~S(Q2J_EI-pcDgTJR0;ml8K(K2MdE30X3>@uVIhoRYIFQH{?HR(>os?EuFQlesFl3JzWeL6Lgw3rc8Kd*`%-)$Cg-JYD8}yK9`zs14WUA+XKQiVId9{D`s1U%ti!0&<BcaoQURw@EIu-mDX)Nd|hLkpJsPOmeI@*F}U*&fSwh;0D7L@ z;jN86JT6&uipbbYQi#dvy!2(wdym?FUozG|YyT%;(QtQB`r^nXAYgl@1BvJ@`u*VF zsZ)|7XK}X*Key4oR8<$WN|nq(h=0K_kGs>qe=t~Gu(g%?jA_DeuDwgnwf6Dm)BY3g zuFlQXdbSTGR@d0$Yt-I~w8h9+hGed~)N)+Ry~TamDJR*1;rb1YxcGow)&3xzsAHkQ%={^i z_m9QItJ=4(+QWz!ELI3%WLEa`#h4tl0&r{IDyu^j3}pkX0CD;>1IiDEd( zPX=)O8-2DK1SLhMg%g(z5nTBqL(yfoD>S`tym3myi_d_q224qDay#y9f#Jd)a3V#F z<)KS2zM!p@s{DyXi5z17&A>t0!p`WsW%fMK$CE0l&wN=S7OP5&Kpq<5!G#+l2D()l zUjrCl6r05v9A3AaknXZ&>#E0+P}N-~NE$b7V@%P`I9so_!K zQ%EyMsj>sf!T~+693LY99Y~PZ=poy}WdE5mKeblFE{zl0nATM~DvM9G6db)?@9e%IR z;)3_?pxdJxULFd+8)INgne_5-t{qGB)te^O+g>M}tG6M@+OYV?4wrq{oL}#g%)8u& zVv~1KgJ?F#WtdDduMn=C|5Y`jUZ*McwvIT6wPq}4xZnrXWV{xN;6oHzUcV$9>tl@W+sLzrmxQU zhxW9rLaA$dgV~*VSUWF$Wxpf0ogZZ$4fyW#jbtd89JcEOcS@C=(YA6xU$W2Uz8<)e z^<*Z;AyAjqAPOkNIG&JIb!vxY(;bdfbVjcfh&MLxptWf)_)|O04ZdeVF>9s^#>meq zF-{}*Ejj`#s-4>cwI{`SCErBoZo_rGh?P^%6x3?;iAN!e+QhZ)the9ERzP}AQh>{l zVSKlTTQ8m0 zoAzPaKVnw?hUN?;^oL&4gg)7j-fZ~#j^)E|c4lt=;DwT^hlV9OGY{_%N&d1&))x|c zM@}BGUQ!9>+;cOTHv@24CffB1bJ;>|VwBfFO;t~E6g~A6>XB<#q<(CfHVfC;%lGJ$ z9)ztpA%oa$Z?2vn+ixwu9WA&bB%iv^a{2mn4oJils3%|ux$YwOl*-7bo=!Mnz;0@{ zpQZQ?yPFOAr)qX^HfMh5(p*)Y + {# Javascript #}