From 038d7e0fa670405eb2c6b4c135a1453e7c994445 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 19 Nov 2021 15:20:00 -0500 Subject: [PATCH 01/23] Add missing HTTP_X_FORWARDED_FOR See discussion [here](https://github.com/netbox-community/netbox/discussions/7876) for background. From the [doc](https://netbox.readthedocs.io/en/stable/customization/custom-scripts/) i should be able to access `META.HTTP_X_FORWARDED_FOR` but i was not able to since they were not being sent downstream --- netbox/utilities/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/utilities/constants.py b/netbox/utilities/constants.py index c3fbd0687..08e9dd9cf 100644 --- a/netbox/utilities/constants.py +++ b/netbox/utilities/constants.py @@ -57,6 +57,7 @@ HTTP_REQUEST_META_SAFE_COPY = [ 'HTTP_HOST', 'HTTP_REFERER', 'HTTP_USER_AGENT', + 'HTTP_X_FORWARDED_FOR', 'QUERY_STRING', 'REMOTE_ADDR', 'REMOTE_HOST', From 2ec64a2ea25848e1c4770a65c99920da599553d3 Mon Sep 17 00:00:00 2001 From: rizlas Date: Tue, 14 Dec 2021 10:17:00 +0100 Subject: [PATCH 02/23] Get_Environment from napalm should not need any decoding --- netbox/dcim/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index f359f0f24..74f06c64f 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -501,7 +501,7 @@ class DeviceViewSet(ConfigContextQuerySetMixin, CustomFieldModelViewSet): response[method] = {'error': 'Only get_* NAPALM methods are supported'} continue try: - response[method] = decode_dict(getattr(d, method)()) + response[method] = decode_dict(getattr(d, method)(), decode_keys=False) except NotImplementedError: response[method] = {'error': 'Method {} not implemented for NAPALM driver {}'.format(method, driver)} except Exception as e: From 3a54ecb5224253ef2be808276fa58484d1773292 Mon Sep 17 00:00:00 2001 From: kkthxbye-code Date: Mon, 20 Dec 2021 23:31:24 +0100 Subject: [PATCH 03/23] Fix #8097: Re-fix markdown table rendering --- netbox/project-static/dist/netbox-dark.css | Bin 788892 -> 789153 bytes netbox/project-static/dist/netbox-light.css | Bin 493648 -> 493807 bytes netbox/project-static/dist/netbox-print.css | Bin 1623714 -> 1624275 bytes netbox/project-static/styles/netbox.scss | 13 +++++++++++++ netbox/templates/circuits/provider.html | 4 ++-- netbox/templates/extras/report_result.html | 2 +- netbox/templates/extras/script_result.html | 2 +- netbox/templates/inc/panels/comments.html | 2 +- netbox/utilities/templatetags/helpers.py | 4 ++++ 9 files changed, 22 insertions(+), 5 deletions(-) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index 25017505e04a3c541e8e65211b4287e20f1af581..e711685bf40042d2b0918218882f0acba69a0d49 100644 GIT binary patch delta 179 zcmbO;*F7M2#)7Pc1l7LFFq7OocVEj%e~5_(0cc`2zysVTa-iAC8d z`Q>>EC5cHnsnZjaSoN*3NM=+gF7M2#)7Pc1l7LFFq7OocVEj%e~+cVjDESLca(hJN0 diff --git a/netbox/project-static/dist/netbox-light.css b/netbox/project-static/dist/netbox-light.css index 07ad0dba297c4e12560fd426b5479997128fef43..10c15397d1e4569f878cdc5a43c32ba37163dc76 100644 GIT binary patch delta 177 zcmcaGLGJxTxrP?T7N!>F7M2#)7Pc+yY;E;=MX7lysYR(Ny19u(*(v$uc?uL%rvl;r1H85LA06zAt;rYI<F7M2#)7Pc+yY;D_@wy}FL0RXDv3Pu0` diff --git a/netbox/project-static/dist/netbox-print.css b/netbox/project-static/dist/netbox-print.css index a09f4922203efc024056c4451260fe9ca2fd7bd7..4562597d87cbb286d15773b5aaa93fe207d7989f 100644 GIT binary patch delta 356 zcmZ3~n|yg>E(=Yz!SDM_=%pr(XN=pJ$OG#o NOC Contact - {{ object.noc_contact|render_markdown|placeholder }} + {{ object.noc_contact|render_markdown|placeholder }} Admin Contact - {{ object.admin_contact|render_markdown|placeholder }} + {{ object.admin_contact|render_markdown|placeholder }} Circuits diff --git a/netbox/templates/extras/report_result.html b/netbox/templates/extras/report_result.html index 90726d287..e2f89a180 100644 --- a/netbox/templates/extras/report_result.html +++ b/netbox/templates/extras/report_result.html @@ -78,7 +78,7 @@ {% endif %} - {{ message|render_markdown }} + {{ message|render_markdown }} {% endfor %} {% endfor %} diff --git a/netbox/templates/extras/script_result.html b/netbox/templates/extras/script_result.html index 3cbd0c611..15c446492 100644 --- a/netbox/templates/extras/script_result.html +++ b/netbox/templates/extras/script_result.html @@ -74,7 +74,7 @@ {{ forloop.counter }} {% log_level log.status %} - {{ log.message|render_markdown }} + {{ log.message|render_markdown }} {% empty %} diff --git a/netbox/templates/inc/panels/comments.html b/netbox/templates/inc/panels/comments.html index bfacb25bf..3219a25a5 100644 --- a/netbox/templates/inc/panels/comments.html +++ b/netbox/templates/inc/panels/comments.html @@ -4,7 +4,7 @@
Comments
-
+
{% if object.comments %} {{ object.comments|render_markdown }} {% else %} diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py index db9c40fc5..47e341400 100644 --- a/netbox/utilities/templatetags/helpers.py +++ b/netbox/utilities/templatetags/helpers.py @@ -59,6 +59,10 @@ def render_markdown(value): # Render Markdown html = markdown(value, extensions=['fenced_code', 'tables', StrikethroughExtension()]) + # If the string is not empty wrap it in rendered-markdown to style tables + if html: + html = f'
{html}
' + return mark_safe(html) From 39a0b15df4b93d6c3b6b19cc02752bc19cc173fa Mon Sep 17 00:00:00 2001 From: rizlas Date: Tue, 21 Dec 2021 17:15:54 +0100 Subject: [PATCH 04/23] Update netbox/dcim/api/views.py Test without decode_dict function Co-authored-by: Jeremy Stretch --- netbox/dcim/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 74f06c64f..03e7e51cc 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -501,7 +501,7 @@ class DeviceViewSet(ConfigContextQuerySetMixin, CustomFieldModelViewSet): response[method] = {'error': 'Only get_* NAPALM methods are supported'} continue try: - response[method] = decode_dict(getattr(d, method)(), decode_keys=False) + response[method] = getattr(d, method)() except NotImplementedError: response[method] = {'error': 'Method {} not implemented for NAPALM driver {}'.format(method, driver)} except Exception as e: From d275538116f8d2c7994e1bdd737b2ac94c7f050c Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 21 Dec 2021 11:53:31 -0500 Subject: [PATCH 05/23] Changelog & cleanup for #7246, #8097 --- docs/release-notes/version-3.1.md | 2 ++ netbox/dcim/api/views.py | 4 ++-- netbox/utilities/utils.py | 39 ------------------------------- 3 files changed, 4 insertions(+), 41 deletions(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index a09b43400..9856544ba 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -4,7 +4,9 @@ ### Bug Fixes +* [#7246](https://github.com/netbox-community/netbox/issues/7246) - Don't attempt to URL-decode NAPALM response payloads * [#7962](https://github.com/netbox-community/netbox/issues/7962) - Fix user menu under report/script result view +* [#8097](https://github.com/netbox-community/netbox/issues/8097) - Fix styling of Markdown tables * [#8131](https://github.com/netbox-community/netbox/issues/8131) - Restore annotation of available IPs under prefix IPs view --- diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 03e7e51cc..5830396ce 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -15,14 +15,14 @@ from circuits.models import Circuit from dcim import filtersets from dcim.models import * from extras.api.views import ConfigContextQuerySetMixin, CustomFieldModelViewSet -from ipam.models import Prefix, VLAN, ASN +from ipam.models import Prefix, VLAN from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired from netbox.api.exceptions import ServiceUnavailable from netbox.api.metadata import ContentTypeMetadata from netbox.api.views import ModelViewSet from netbox.config import get_config from utilities.api import get_serializer_for_model -from utilities.utils import count_related, decode_dict +from utilities.utils import count_related from virtualization.models import VirtualMachine from . import serializers from .exceptions import MissingFilterException diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index 3234135fb..ce1f6a111 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -288,45 +288,6 @@ def flatten_dict(d, prefix='', separator='.'): return ret -def decode_dict(encoded_dict: Dict, *, decode_keys: bool = True) -> Dict: - """ - Recursively URL decode string keys and values of a dict. - - For example, `{'1%2F1%2F1': {'1%2F1%2F2': ['1%2F1%2F3', '1%2F1%2F4']}}` would - become: `{'1/1/1': {'1/1/2': ['1/1/3', '1/1/4']}}` - - :param encoded_dict: Dictionary to be decoded. - :param decode_keys: (Optional) Enable/disable decoding of dict keys. - """ - - def decode_value(value: Any, _decode_keys: bool) -> Any: - """ - Handle URL decoding of any supported value type. - """ - # Decode string values. - if isinstance(value, str): - return urllib.parse.unquote(value) - # Recursively decode each list item. - elif isinstance(value, list): - return [decode_value(v, _decode_keys) for v in value] - # Recursively decode each tuple item. - elif isinstance(value, Tuple): - return tuple(decode_value(v, _decode_keys) for v in value) - # Recursively decode each dict key/value pair. - elif isinstance(value, dict): - # Don't decode keys, if `decode_keys` is false. - if not _decode_keys: - return {k: decode_value(v, _decode_keys) for k, v in value.items()} - return {urllib.parse.unquote(k): decode_value(v, _decode_keys) for k, v in value.items()} - return value - - if not decode_keys: - # Don't decode keys, if `decode_keys` is false. - return {k: decode_value(v, decode_keys) for k, v in encoded_dict.items()} - - return {urllib.parse.unquote(k): decode_value(v, decode_keys) for k, v in encoded_dict.items()} - - def array_to_string(array): """ Generate an efficient, human-friendly string from a set of integers. Intended for use with ArrayField. From ceb941df811a32bbc18b84d701f3a7fd738ff921 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 21 Dec 2021 13:00:52 -0500 Subject: [PATCH 06/23] Closes #8135: Append version when fetching static assets --- netbox/templates/base/base.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/netbox/templates/base/base.html b/netbox/templates/base/base.html index 50bf7133c..6e71b3995 100644 --- a/netbox/templates/base/base.html +++ b/netbox/templates/base/base.html @@ -104,23 +104,23 @@ {# Static resources #} @@ -129,7 +129,7 @@ {# Javascript #} From 8e95ac42c2bdb27f627e92a2cef5a3eab0257c47 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 21 Dec 2021 13:05:38 -0500 Subject: [PATCH 07/23] Closes #8100: Add "other" choice for FHRP group protocol --- docs/release-notes/version-3.1.md | 4 ++++ netbox/ipam/choices.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 9856544ba..ad69b73b0 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -2,6 +2,10 @@ ## v3.1.3 (FUTURE) +### Enhancements + +* [#8100](https://github.com/netbox-community/netbox/issues/8100) - Add "other" choice for FHRP group protocol + ### Bug Fixes * [#7246](https://github.com/netbox-community/netbox/issues/7246) - Don't attempt to URL-decode NAPALM response payloads diff --git a/netbox/ipam/choices.py b/netbox/ipam/choices.py index 638ef62f6..526ef07d9 100644 --- a/netbox/ipam/choices.py +++ b/netbox/ipam/choices.py @@ -135,6 +135,7 @@ class FHRPGroupProtocolChoices(ChoiceSet): PROTOCOL_HSRP = 'hsrp' PROTOCOL_GLBP = 'glbp' PROTOCOL_CARP = 'carp' + PROTOCOL_OTHER = 'other' CHOICES = ( (PROTOCOL_VRRP2, 'VRRPv2'), @@ -142,6 +143,7 @@ class FHRPGroupProtocolChoices(ChoiceSet): (PROTOCOL_HSRP, 'HSRP'), (PROTOCOL_GLBP, 'GLBP'), (PROTOCOL_CARP, 'CARP'), + (PROTOCOL_OTHER, 'Other'), ) From 373cc74a338c5c182cdeed8bdc16744f4a0e3518 Mon Sep 17 00:00:00 2001 From: thatmattlove Date: Tue, 21 Dec 2021 11:11:33 -0700 Subject: [PATCH 08/23] Fixes #8134: reinitialize event listeners when HTMX swaps elements --- docs/release-notes/version-3.1.md | 1 + netbox/project-static/dist/netbox.js | Bin 374756 -> 374954 bytes netbox/project-static/dist/netbox.js.map | Bin 344228 -> 344416 bytes netbox/project-static/src/htmx.ts | 23 +++++++++++++++++++++++ netbox/project-static/src/netbox.ts | 2 ++ 5 files changed, 26 insertions(+) create mode 100644 netbox/project-static/src/htmx.ts diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index ad69b73b0..5ff6ea1bb 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -5,6 +5,7 @@ ### Enhancements * [#8100](https://github.com/netbox-community/netbox/issues/8100) - Add "other" choice for FHRP group protocol +* [#8134](https://github.com/netbox-community/netbox/issues/8134) - Fix issue where HTMX-swapped UI elements needed to be reinitialized ### Bug Fixes diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 95fd99270f6418fc224d471cf1054aa65323fd0b..c15316ce806af857ea5c84e8f2596fc225e5f072 100644 GIT binary patch delta 31115 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 delta 30966 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 diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 6fbe0874b1025b63008a8cc41168eb25890d4295..ebab0d3a7ab270fc60f314a51f3a5809be939a9d 100644 GIT binary patch delta 185 zcmZ3|DEgpDbi-GBmW-0zist9`?a%EQftU%1nYTZ;XW90jKS19}C(+R{(^<#a(OKVV zdY~MuIHzNvJ4n#LX?lSwtH|`ZDy$r_K*yE?QXUBs1KI?X_SeaD0_)EOQu$6g-p+0XF4Gs-v&Kz-V9&}r J{hvMSG5}o8Iu8H< delta 49 zcmaFRB)X(gbi-HsW_E{mb_YfvW&&d7?d%RLEB-Tk>pM*kcVLa1uAsuoHvNDD>jD6% CyASIC diff --git a/netbox/project-static/src/htmx.ts b/netbox/project-static/src/htmx.ts new file mode 100644 index 000000000..70ed4f534 --- /dev/null +++ b/netbox/project-static/src/htmx.ts @@ -0,0 +1,23 @@ +import { getElements, isTruthy } from './util'; +import { initButtons } from './buttons'; + +function initDepedencies(): void { + for (const init of [initButtons]) { + init(); + } +} + +/** + * Hook into HTMX's event system to reinitialize specific native event listeners when HTMX swaps + * elements. + */ +export function initHtmx(): void { + for (const element of getElements('[hx-target]')) { + const targetSelector = element.getAttribute('hx-target'); + if (isTruthy(targetSelector)) { + for (const target of getElements(targetSelector)) { + target.addEventListener('htmx:afterSettle', initDepedencies); + } + } + } +} diff --git a/netbox/project-static/src/netbox.ts b/netbox/project-static/src/netbox.ts index 79c196b96..c178a2dbd 100644 --- a/netbox/project-static/src/netbox.ts +++ b/netbox/project-static/src/netbox.ts @@ -12,6 +12,7 @@ import { initInterfaceTable } from './tables'; import { initSideNav } from './sidenav'; import { initRackElevation } from './racks'; import { initLinks } from './links'; +import { initHtmx } from './htmx'; function initDocument(): void { for (const init of [ @@ -29,6 +30,7 @@ function initDocument(): void { initSideNav, initRackElevation, initLinks, + initHtmx, ]) { init(); } From 344fb638fd11ce97b35f4b0365bfa16a24d7711d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 21 Dec 2021 13:17:54 -0500 Subject: [PATCH 09/23] Fixes #8127: Fix disassociation of interface under IP address edit view --- docs/release-notes/version-3.1.md | 3 ++- netbox/ipam/forms/models.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 5ff6ea1bb..2128abdb6 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -5,14 +5,15 @@ ### Enhancements * [#8100](https://github.com/netbox-community/netbox/issues/8100) - Add "other" choice for FHRP group protocol -* [#8134](https://github.com/netbox-community/netbox/issues/8134) - Fix issue where HTMX-swapped UI elements needed to be reinitialized ### Bug Fixes * [#7246](https://github.com/netbox-community/netbox/issues/7246) - Don't attempt to URL-decode NAPALM response payloads * [#7962](https://github.com/netbox-community/netbox/issues/7962) - Fix user menu under report/script result view * [#8097](https://github.com/netbox-community/netbox/issues/8097) - Fix styling of Markdown tables +* [#8127](https://github.com/netbox-community/netbox/issues/8127) - Fix disassociation of interface under IP address edit view * [#8131](https://github.com/netbox-community/netbox/issues/8131) - Restore annotation of available IPs under prefix IPs view +* [#8134](https://github.com/netbox-community/netbox/issues/8134) - Fix bulk editing of objects within dynamic tables --- diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index 319d8671e..c5e3146e9 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -471,6 +471,8 @@ class IPAddressForm(TenancyForm, CustomFieldModelForm): }) elif selected_objects: self.instance.assigned_object = self.cleaned_data[selected_objects[0]] + else: + self.instance.assigned_object = None # Primary IP assignment is only available if an interface has been assigned. interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface') From f1350a10229ee30dd57e37651d5c633858528c1c Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 21 Dec 2021 13:57:12 -0500 Subject: [PATCH 10/23] FIxes #7972: Standardize name of RemoteUserBackend logger --- docs/release-notes/version-3.1.md | 1 + netbox/netbox/authentication.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 2128abdb6..5d445959e 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -10,6 +10,7 @@ * [#7246](https://github.com/netbox-community/netbox/issues/7246) - Don't attempt to URL-decode NAPALM response payloads * [#7962](https://github.com/netbox-community/netbox/issues/7962) - Fix user menu under report/script result view +* [#7972](https://github.com/netbox-community/netbox/issues/7972) - Standardize name of `RemoteUserBackend` logger * [#8097](https://github.com/netbox-community/netbox/issues/8097) - Fix styling of Markdown tables * [#8127](https://github.com/netbox-community/netbox/issues/8127) - Fix disassociation of interface under IP address edit view * [#8131](https://github.com/netbox-community/netbox/issues/8131) - Restore annotation of available IPs under prefix IPs view diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py index a67ec451d..acb04ce34 100644 --- a/netbox/netbox/authentication.py +++ b/netbox/netbox/authentication.py @@ -105,7 +105,7 @@ class RemoteUserBackend(_RemoteUserBackend): return settings.REMOTE_AUTH_AUTO_CREATE_USER def configure_groups(self, user, remote_groups): - logger = logging.getLogger('netbox.authentication.RemoteUserBackend') + logger = logging.getLogger('netbox.auth.RemoteUserBackend') # Assign default groups to the user group_list = [] @@ -141,7 +141,7 @@ class RemoteUserBackend(_RemoteUserBackend): Return None if ``create_unknown_user`` is ``False`` and a ``User`` object with the given username is not found in the database. """ - logger = logging.getLogger('netbox.authentication.RemoteUserBackend') + logger = logging.getLogger('netbox.auth.RemoteUserBackend') logger.debug( f"trying to authenticate {remote_user} with groups {remote_groups}") if not remote_user: @@ -173,7 +173,7 @@ class RemoteUserBackend(_RemoteUserBackend): return None def _is_superuser(self, user): - logger = logging.getLogger('netbox.authentication.RemoteUserBackend') + logger = logging.getLogger('netbox.auth.RemoteUserBackend') superuser_groups = settings.REMOTE_AUTH_SUPERUSER_GROUPS logger.debug(f"Superuser Groups: {superuser_groups}") superusers = settings.REMOTE_AUTH_SUPERUSERS @@ -189,7 +189,7 @@ class RemoteUserBackend(_RemoteUserBackend): return bool(result) def _is_staff(self, user): - logger = logging.getLogger('netbox.authentication.RemoteUserBackend') + logger = logging.getLogger('netbox.auth.RemoteUserBackend') staff_groups = settings.REMOTE_AUTH_STAFF_GROUPS logger.debug(f"Superuser Groups: {staff_groups}") staff_users = settings.REMOTE_AUTH_STAFF_USERS @@ -204,7 +204,7 @@ class RemoteUserBackend(_RemoteUserBackend): return bool(result) def configure_user(self, request, user): - logger = logging.getLogger('netbox.authentication.RemoteUserBackend') + logger = logging.getLogger('netbox.auth.RemoteUserBackend') if not settings.REMOTE_AUTH_GROUP_SYNC_ENABLED: # Assign default groups to the user group_list = [] From d4b6fe14c3212f7c9e67e313dc655bd6c1d1de51 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 21 Dec 2021 14:04:15 -0500 Subject: [PATCH 11/23] Fixes #8138: Fix alignment of tags panel within IP address view --- netbox/templates/ipam/ipaddress.html | 254 +++++++++++++-------------- 1 file changed, 123 insertions(+), 131 deletions(-) diff --git a/netbox/templates/ipam/ipaddress.html b/netbox/templates/ipam/ipaddress.html index 624735a95..cb1281e53 100644 --- a/netbox/templates/ipam/ipaddress.html +++ b/netbox/templates/ipam/ipaddress.html @@ -13,143 +13,135 @@ {% block content %}
-
-
- IP Address -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FamilyIPv{{ object.family }}
VRF - {% if object.vrf %} - {{ object.vrf }} - {% else %} - Global - {% endif %} -
Tenant - {% if object.tenant %} - {% if object.tenant.group %} - {{ object.tenant.group }} / - {% endif %} - {{ object.tenant }} - {% else %} - None - {% endif %} -
Status - {{ object.get_status_display }} -
Role - {% if object.role %} - {{ object.get_role_display }} - {% else %} - None - {% endif %} -
DNS Name{{ object.dns_name|placeholder }}
Description{{ object.description|placeholder }}
Assignment - {% if object.assigned_object %} - {% if object.assigned_object.parent_object %} - {{ object.assigned_object.parent_object }} / - {% endif %} - {{ object.assigned_object }} +
+
+ IP Address +
+
+ + + + + + + + - - - - - - - - - -
FamilyIPv{{ object.family }}
VRF + {% if object.vrf %} + {{ object.vrf }} {% else %} - + Global {% endif %} -
NAT (inside) - {% if object.nat_inside %} - {{ object.nat_inside }} - {% if object.nat_inside.assigned_object %} - ({{ object.nat_inside.assigned_object.parent_object }}) - {% endif %} - {% else %} - None - {% endif %} -
NAT (outside) - {% if object.nat_outside %} - {{ object.nat_outside }} - {% else %} - None - {% endif %} -
-
-
- {% include 'inc/panels/custom_fields.html' %} - - {% plugin_left_page object %} - - -
Tenant + {% if object.tenant %} + {% if object.tenant.group %} + {{ object.tenant.group }} / + {% endif %} + {{ object.tenant }} + {% else %} + None + {% endif %} +
Status + {{ object.get_status_display }} +
Role + {% if object.role %} + {{ object.get_role_display }} + {% else %} + None + {% endif %} +
DNS Name{{ object.dns_name|placeholder }}
Description{{ object.description|placeholder }}
Assignment + {% if object.assigned_object %} + {% if object.assigned_object.parent_object %} + {{ object.assigned_object.parent_object }} / + {% endif %} + {{ object.assigned_object }} {% else %} - href="{% url 'ipam:ipaddress_list' %}?address={{ object.address.ip }}&vrf_id=null" + {% endif %} - >Show all - - {% endif %} - -
- {% render_table duplicate_ips_table 'inc/table.html' %} -
- - {% endif %} -
- {% include 'inc/panel_table.html' with table=related_ips_table heading='Related IP Addresses' %} -
- {% plugin_right_page object %} +
NAT (inside) + {% if object.nat_inside %} + {{ object.nat_inside }} + {% if object.nat_inside.assigned_object %} + ({{ object.nat_inside.assigned_object.parent_object }}) + {% endif %} + {% else %} + None + {% endif %} +
NAT (outside) + {% if object.nat_outside %} + {{ object.nat_outside }} + {% else %} + None + {% endif %} +
+
+
+ {% include 'inc/panels/tags.html' %} + {% include 'inc/panels/custom_fields.html' %} + {% plugin_left_page object %}
-
- -
-
- {% include 'inc/panels/tags.html' %} +
+ {% include 'inc/panel_table.html' with table=parent_prefixes_table heading='Parent Prefixes' %} + {% if duplicate_ips_table.rows %} + {# Custom version of panel_table.html #} +
+
+ Duplicate IP Addresses + {% if more_duplicate_ips %} +
+ Show all +
+ {% endif %} +
+
+ {% render_table duplicate_ips_table 'inc/table.html' %} +
+
+ {% endif %} +
+ {% include 'inc/panel_table.html' with table=related_ips_table heading='Related IP Addresses' %}
- + {% plugin_right_page object %} +
From 275560698fefe079421a9d549602a8e1fd4c1a07 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 21 Dec 2021 14:10:12 -0500 Subject: [PATCH 12/23] Fixes #8139: Fix rendering of table configuration form under VM interfaces view --- docs/release-notes/version-3.1.md | 1 + netbox/templates/virtualization/virtualmachine/interfaces.html | 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 5d445959e..40abe7e15 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -15,6 +15,7 @@ * [#8127](https://github.com/netbox-community/netbox/issues/8127) - Fix disassociation of interface under IP address edit view * [#8131](https://github.com/netbox-community/netbox/issues/8131) - Restore annotation of available IPs under prefix IPs view * [#8134](https://github.com/netbox-community/netbox/issues/8134) - Fix bulk editing of objects within dynamic tables +* [#8139](https://github.com/netbox-community/netbox/issues/8139) - Fix rendering of table configuration form under VM interfaces view --- diff --git a/netbox/templates/virtualization/virtualmachine/interfaces.html b/netbox/templates/virtualization/virtualmachine/interfaces.html index cbfec8172..6b3e70c7f 100644 --- a/netbox/templates/virtualization/virtualmachine/interfaces.html +++ b/netbox/templates/virtualization/virtualmachine/interfaces.html @@ -5,7 +5,7 @@ {% block content %}
{% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="VMInterfaceTable_config" %} + {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineVMInterfaceTable_config" %}
From fb4511d09963e7b86809d0e3511bb290d2531fa8 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 22 Dec 2021 10:55:06 -0500 Subject: [PATCH 13/23] Fixes #8140: Restore missing fields on wireless LAN & link REST API serializers --- docs/release-notes/version-3.1.md | 1 + netbox/wireless/api/serializers.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 40abe7e15..618da9411 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -16,6 +16,7 @@ * [#8131](https://github.com/netbox-community/netbox/issues/8131) - Restore annotation of available IPs under prefix IPs view * [#8134](https://github.com/netbox-community/netbox/issues/8134) - Fix bulk editing of objects within dynamic tables * [#8139](https://github.com/netbox-community/netbox/issues/8139) - Fix rendering of table configuration form under VM interfaces view +* [#8140](https://github.com/netbox-community/netbox/issues/8140) - Restore missing fields on wireless LAN & link REST API serializers --- diff --git a/netbox/wireless/api/serializers.py b/netbox/wireless/api/serializers.py index 68e8181f1..f1fa6d58d 100644 --- a/netbox/wireless/api/serializers.py +++ b/netbox/wireless/api/serializers.py @@ -40,6 +40,7 @@ class WirelessLANSerializer(PrimaryModelSerializer): model = WirelessLAN fields = [ 'id', 'url', 'display', 'ssid', 'description', 'group', 'vlan', 'auth_type', 'auth_cipher', 'auth_psk', + 'description', 'tags', 'custom_fields', 'created', 'last_updated', ] @@ -55,5 +56,5 @@ class WirelessLinkSerializer(PrimaryModelSerializer): model = WirelessLink fields = [ 'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'description', 'auth_type', - 'auth_cipher', 'auth_psk', + 'auth_cipher', 'auth_psk', 'description', 'tags', 'custom_fields', 'created', 'last_updated', ] From 0a7372460f6751f921804222555efb04dce7edfb Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 22 Dec 2021 12:48:24 -0500 Subject: [PATCH 14/23] Changelog for #7887 --- docs/release-notes/version-3.1.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 618da9411..c748bff14 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -9,6 +9,7 @@ ### Bug Fixes * [#7246](https://github.com/netbox-community/netbox/issues/7246) - Don't attempt to URL-decode NAPALM response payloads +* [#7887](https://github.com/netbox-community/netbox/issues/7887) - Forward `HTTP_X_FORWARDED_FOR` to custom scripts * [#7962](https://github.com/netbox-community/netbox/issues/7962) - Fix user menu under report/script result view * [#7972](https://github.com/netbox-community/netbox/issues/7972) - Standardize name of `RemoteUserBackend` logger * [#8097](https://github.com/netbox-community/netbox/issues/8097) - Fix styling of Markdown tables From 37065b7c504976ba76d887bad60797c6dcff995b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 22 Dec 2021 14:47:42 -0500 Subject: [PATCH 15/23] Remove obsolete template --- netbox/templates/inc/plugin_menu_items.html | 30 --------------------- 1 file changed, 30 deletions(-) delete mode 100644 netbox/templates/inc/plugin_menu_items.html diff --git a/netbox/templates/inc/plugin_menu_items.html b/netbox/templates/inc/plugin_menu_items.html deleted file mode 100644 index b6e5e18a1..000000000 --- a/netbox/templates/inc/plugin_menu_items.html +++ /dev/null @@ -1,30 +0,0 @@ -{% load helpers %} - From f369b5f588ad29d9de0f36cfdce586bd3769e7eb Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 22 Dec 2021 15:05:24 -0500 Subject: [PATCH 16/23] Reorganize & clean up templatetag templates --- netbox/templates/base/layout.html | 3 +- netbox/templates/base/sidenav.html | 2 +- netbox/templates/home.html | 1 - .../form_helpers}/render_custom_fields.html | 0 .../form_helpers}/render_errors.html | 0 .../templates/form_helpers}/render_field.html | 0 .../templates/form_helpers}/render_form.html | 0 .../templates/helpers}/applied_filters.html | 0 .../templates/helpers}/badge.html | 0 .../templates/helpers}/table_config_form.html | 0 .../templates/helpers}/tag.html | 0 .../templates/helpers}/utilization_graph.html | 0 .../navigation/{nav_items.html => menu.html} | 0 netbox/utilities/templatetags/form_helpers.py | 74 ++++++++++--------- netbox/utilities/templatetags/get_status.py | 21 ------ netbox/utilities/templatetags/helpers.py | 10 +-- .../templatetags/{nav.py => navigation.py} | 2 +- .../{search_options.py => search.py} | 0 18 files changed, 49 insertions(+), 64 deletions(-) rename netbox/{templates/utilities => utilities/templates/form_helpers}/render_custom_fields.html (100%) rename netbox/{templates/utilities => utilities/templates/form_helpers}/render_errors.html (100%) rename netbox/{templates/utilities => utilities/templates/form_helpers}/render_field.html (100%) rename netbox/{templates/utilities => utilities/templates/form_helpers}/render_form.html (100%) rename netbox/{templates/utilities/templatetags => utilities/templates/helpers}/applied_filters.html (100%) rename netbox/{templates/utilities/templatetags => utilities/templates/helpers}/badge.html (100%) rename netbox/{templates/utilities/templatetags => utilities/templates/helpers}/table_config_form.html (100%) rename netbox/{templates/utilities/templatetags => utilities/templates/helpers}/tag.html (100%) rename netbox/{templates/utilities/templatetags => utilities/templates/helpers}/utilization_graph.html (100%) rename netbox/utilities/templates/navigation/{nav_items.html => menu.html} (100%) delete mode 100644 netbox/utilities/templatetags/get_status.py rename netbox/utilities/templatetags/{nav.py => navigation.py} (81%) rename netbox/utilities/templatetags/{search_options.py => search.py} (100%) diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index 38c1dc21b..d6a21c2b5 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -1,8 +1,7 @@ {# Base layout for the core NetBox UI w/navbar and page content #} {% extends 'base/base.html' %} {% load helpers %} -{% load nav %} -{% load search_options %} +{% load search %} {% load static %} {% block layout %} diff --git a/netbox/templates/base/sidenav.html b/netbox/templates/base/sidenav.html index 5790e8c44..1df20e6de 100644 --- a/netbox/templates/base/sidenav.html +++ b/netbox/templates/base/sidenav.html @@ -1,4 +1,4 @@ -{% load nav %} +{% load navigation %} {% load static %}