From 4242546270a996e84943e1b3ae05b35f2a31f3ac Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Jun 2024 13:51:40 -0400 Subject: [PATCH 01/17] Fixes #16376: Log changes on terminating objects when attaching a cable --- netbox/dcim/models/cables.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 64f0b8560..7afead829 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -355,11 +355,11 @@ class CableTermination(ChangeLoggedModel): super().save(*args, **kwargs) # Set the cable on the terminating object - termination_model = self.termination._meta.model - termination_model.objects.filter(pk=self.termination_id).update( - cable=self.cable, - cable_end=self.cable_end - ) + termination = self.termination._meta.model.objects.get(pk=self.termination_id) + termination.snapshot() + termination.cable = self.cable + termination.cable_end = self.cable_end + termination.save() def delete(self, *args, **kwargs): From 81f0a4050528636c8f023c2a674dbcaf4a076312 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 05:02:18 +0000 Subject: [PATCH 02/17] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 58 ++++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index af150e24c..2c459a302 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-04 05:02+0000\n" +"POT-Creation-Date: 2024-06-05 05:02+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -965,7 +965,7 @@ msgstr "" #: netbox/extras/forms/filtersets.py:143 netbox/extras/forms/filtersets.py:183 #: netbox/extras/forms/filtersets.py:199 netbox/extras/forms/filtersets.py:230 #: netbox/extras/forms/filtersets.py:254 netbox/extras/forms/filtersets.py:450 -#: netbox/extras/forms/filtersets.py:488 netbox/ipam/forms/filtersets.py:99 +#: netbox/extras/forms/filtersets.py:485 netbox/ipam/forms/filtersets.py:99 #: netbox/ipam/forms/filtersets.py:266 netbox/ipam/forms/filtersets.py:307 #: netbox/ipam/forms/filtersets.py:382 netbox/ipam/forms/filtersets.py:475 #: netbox/ipam/forms/filtersets.py:534 netbox/ipam/forms/filtersets.py:552 @@ -1577,9 +1577,9 @@ msgid "Creation" msgstr "" #: netbox/core/forms/filtersets.py:71 netbox/extras/forms/filtersets.py:470 -#: netbox/extras/forms/filtersets.py:513 netbox/extras/tables/tables.py:183 +#: netbox/extras/forms/filtersets.py:510 netbox/extras/tables/tables.py:183 #: netbox/extras/tables/tables.py:504 netbox/templates/core/job.html:20 -#: netbox/templates/extras/objectchange.html:51 +#: netbox/templates/extras/objectchange.html:52 #: netbox/tenancy/tables/contacts.py:90 netbox/vpn/tables/l2vpn.py:59 msgid "Object Type" msgstr "" @@ -1619,9 +1619,9 @@ msgstr "" #: netbox/core/forms/filtersets.py:123 netbox/dcim/forms/bulk_edit.py:361 #: netbox/dcim/forms/filtersets.py:353 netbox/dcim/forms/filtersets.py:397 #: netbox/dcim/forms/model_forms.py:258 netbox/extras/forms/filtersets.py:465 -#: netbox/extras/forms/filtersets.py:508 +#: netbox/extras/forms/filtersets.py:505 #: netbox/templates/dcim/rackreservation.html:58 -#: netbox/templates/extras/objectchange.html:35 +#: netbox/templates/extras/objectchange.html:36 #: netbox/templates/extras/savedfilter.html:21 #: netbox/templates/inc/user_menu.html:15 netbox/templates/users/token.html:21 #: netbox/templates/users/user.html:6 netbox/templates/users/user.html:14 @@ -1976,7 +1976,7 @@ msgstr "" #: netbox/extras/tables/tables.py:509 netbox/extras/tables/tables.py:574 #: netbox/netbox/tables/tables.py:243 netbox/templates/extras/eventrule.html:84 #: netbox/templates/extras/journalentry.html:18 -#: netbox/templates/extras/objectchange.html:57 +#: netbox/templates/extras/objectchange.html:58 #: netbox/tenancy/tables/contacts.py:93 netbox/vpn/tables/l2vpn.py:64 msgid "Object" msgstr "" @@ -4172,7 +4172,7 @@ msgid "Connection" msgstr "" #: netbox/dcim/forms/filtersets.py:1254 netbox/extras/forms/bulk_edit.py:316 -#: netbox/extras/forms/bulk_import.py:242 netbox/extras/forms/filtersets.py:476 +#: netbox/extras/forms/bulk_import.py:242 netbox/extras/forms/filtersets.py:473 #: netbox/extras/forms/model_forms.py:551 netbox/extras/tables/tables.py:512 #: netbox/templates/extras/journalentry.html:30 msgid "Kind" @@ -7144,23 +7144,23 @@ msgstr "" msgid "Tenant groups" msgstr "" -#: netbox/extras/forms/filtersets.py:454 netbox/extras/forms/filtersets.py:492 +#: netbox/extras/forms/filtersets.py:454 netbox/extras/forms/filtersets.py:489 msgid "After" msgstr "" -#: netbox/extras/forms/filtersets.py:459 netbox/extras/forms/filtersets.py:497 +#: netbox/extras/forms/filtersets.py:459 netbox/extras/forms/filtersets.py:494 msgid "Before" msgstr "" -#: netbox/extras/forms/filtersets.py:487 netbox/extras/tables/tables.py:456 +#: netbox/extras/forms/filtersets.py:484 netbox/extras/tables/tables.py:456 #: netbox/extras/tables/tables.py:542 netbox/extras/tables/tables.py:567 -#: netbox/templates/extras/objectchange.html:31 +#: netbox/templates/extras/objectchange.html:32 msgid "Time" msgstr "" -#: netbox/extras/forms/filtersets.py:501 netbox/extras/forms/model_forms.py:282 +#: netbox/extras/forms/filtersets.py:498 netbox/extras/forms/model_forms.py:282 #: netbox/extras/tables/tables.py:470 netbox/templates/extras/eventrule.html:77 -#: netbox/templates/extras/objectchange.html:45 +#: netbox/templates/extras/objectchange.html:46 msgid "Action" msgstr "" @@ -8256,7 +8256,7 @@ msgid "Full Name" msgstr "" #: netbox/extras/tables/tables.py:483 -#: netbox/templates/extras/objectchange.html:67 +#: netbox/templates/extras/objectchange.html:68 msgid "Request ID" msgstr "" @@ -10275,7 +10275,7 @@ msgid "Journal Entries" msgstr "" #: netbox/netbox/navigation/menu.py:359 -#: netbox/templates/extras/objectchange.html:8 +#: netbox/templates/extras/objectchange.html:9 #: netbox/templates/extras/objectchange_list.html:4 msgid "Change Log" msgstr "" @@ -10734,8 +10734,8 @@ msgstr "" #: netbox/templates/extras/configcontext.html:70 #: netbox/templates/extras/eventrule.html:72 #: netbox/templates/extras/htmx/script_result.html:56 -#: netbox/templates/extras/objectchange.html:123 -#: netbox/templates/extras/objectchange.html:141 +#: netbox/templates/extras/objectchange.html:124 +#: netbox/templates/extras/objectchange.html:142 #: netbox/templates/extras/webhook.html:67 #: netbox/templates/extras/webhook.html:79 #: netbox/templates/inc/panel_table.html:13 @@ -12308,48 +12308,48 @@ msgstr "" msgid "New Journal Entry" msgstr "" -#: netbox/templates/extras/objectchange.html:28 +#: netbox/templates/extras/objectchange.html:29 #: netbox/templates/users/objectpermission.html:42 msgid "Change" msgstr "" -#: netbox/templates/extras/objectchange.html:78 +#: netbox/templates/extras/objectchange.html:79 msgid "Difference" msgstr "" -#: netbox/templates/extras/objectchange.html:81 +#: netbox/templates/extras/objectchange.html:82 msgid "Previous" msgstr "" -#: netbox/templates/extras/objectchange.html:84 +#: netbox/templates/extras/objectchange.html:85 msgid "Next" msgstr "" -#: netbox/templates/extras/objectchange.html:92 +#: netbox/templates/extras/objectchange.html:93 msgid "Object Created" msgstr "" -#: netbox/templates/extras/objectchange.html:94 +#: netbox/templates/extras/objectchange.html:95 msgid "Object Deleted" msgstr "" -#: netbox/templates/extras/objectchange.html:96 +#: netbox/templates/extras/objectchange.html:97 msgid "No Changes" msgstr "" -#: netbox/templates/extras/objectchange.html:110 +#: netbox/templates/extras/objectchange.html:111 msgid "Pre-Change Data" msgstr "" -#: netbox/templates/extras/objectchange.html:121 +#: netbox/templates/extras/objectchange.html:122 msgid "Warning: Comparing non-atomic change to previous change record" msgstr "" -#: netbox/templates/extras/objectchange.html:130 +#: netbox/templates/extras/objectchange.html:131 msgid "Post-Change Data" msgstr "" -#: netbox/templates/extras/objectchange.html:153 +#: netbox/templates/extras/objectchange.html:162 #, python-format msgid "See All %(count)s Changes" msgstr "" From c27cb6f153062b1a0d8a7f819db358763d1205f5 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 5 Jun 2024 09:02:05 -0400 Subject: [PATCH 03/17] Fix styling of object jobs table --- netbox/templates/core/object_jobs.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/core/object_jobs.html b/netbox/templates/core/object_jobs.html index 7d8c0a3b7..14e31e5be 100644 --- a/netbox/templates/core/object_jobs.html +++ b/netbox/templates/core/object_jobs.html @@ -5,7 +5,7 @@
-
+
{% render_table table 'inc/table.html' %} {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
From 50169365a9b7007eddf8bb1dd4f419141963fa50 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Jun 2024 14:24:57 -0400 Subject: [PATCH 04/17] Closes #16359: Add navbar() method to PluginTemplateExtension --- docs/plugins/development/views.md | 3 +++ netbox/netbox/plugins/registration.py | 10 +--------- netbox/netbox/plugins/templates.py | 10 +++++++++- netbox/netbox/tests/dummy_plugin/template_content.py | 8 +++++++- netbox/netbox/tests/test_plugins.py | 3 ++- netbox/templates/base/layout.html | 7 ++++++- netbox/utilities/templatetags/plugins.py | 10 +++++++++- 7 files changed, 37 insertions(+), 14 deletions(-) diff --git a/docs/plugins/development/views.md b/docs/plugins/development/views.md index 3c13a6fcb..f6624f42c 100644 --- a/docs/plugins/development/views.md +++ b/docs/plugins/development/views.md @@ -195,12 +195,15 @@ Plugins can inject custom content into certain areas of core NetBox views. This | Method | View | Description | |---------------------|-------------|-----------------------------------------------------| +| `navbar()` | All | Inject content inside the top navigation bar | | `left_page()` | Object view | Inject content on the left side of the page | | `right_page()` | Object view | Inject content on the right side of the page | | `full_width_page()` | Object view | Inject content across the entire bottom of the page | | `buttons()` | Object view | Add buttons to the top of the page | | `list_buttons()` | List view | Add buttons to the top of the page | +!!! info "The `navbar()` method was introduced in NetBox v4.1." + Additionally, a `render()` method is available for convenience. This method accepts the name of a template to render, and any additional context data you want to pass. Its use is optional, however. When a PluginTemplateExtension is instantiated, context data is assigned to `self.context`. Available data include: diff --git a/netbox/netbox/plugins/registration.py b/netbox/netbox/plugins/registration.py index d27bb67ca..fbece12e5 100644 --- a/netbox/netbox/plugins/registration.py +++ b/netbox/netbox/plugins/registration.py @@ -32,21 +32,13 @@ def register_template_extensions(class_list): template_extension=template_extension ) ) - if template_extension.model is None: - raise TypeError( - _("PluginTemplateExtension class {template_extension} does not define a valid model!").format( - template_extension=template_extension - ) - ) registry['plugins']['template_extensions'][template_extension.model].append(template_extension) def register_menu(menu): if not isinstance(menu, PluginMenu): - raise TypeError(_("{item} must be an instance of netbox.plugins.PluginMenuItem").format( - item=menu_link - )) + raise TypeError(_("{item} must be an instance of netbox.plugins.PluginMenuItem").format(item=menu)) registry['plugins']['menus'].append(menu) diff --git a/netbox/netbox/plugins/templates.py b/netbox/netbox/plugins/templates.py index 85229dbaf..ccd549160 100644 --- a/netbox/netbox/plugins/templates.py +++ b/netbox/netbox/plugins/templates.py @@ -14,7 +14,8 @@ class PluginTemplateExtension: The `model` attribute on the class defines the which model detail page this class renders content for. It should be set as a string in the form '.'. render() provides the following context data: - * object - The object being viewed + * object - The object being viewed (object views only) + * model - The type of object being viewed (list views only) * request - The current request * settings - Global NetBox settings * config - Plugin-specific configuration parameters @@ -36,6 +37,13 @@ class PluginTemplateExtension: return get_template(template_name).render({**self.context, **extra_context}) + def navbar(self): + """ + Content that will be rendered inside the top navigation menu. Content should be returned as an HTML + string. Note that content does not need to be marked as safe because this is automatically handled. + """ + raise NotImplementedError + def left_page(self): """ Content that will be rendered on the left of the detail page view. Content should be returned as an diff --git a/netbox/netbox/tests/dummy_plugin/template_content.py b/netbox/netbox/tests/dummy_plugin/template_content.py index b63338f2f..764faa60e 100644 --- a/netbox/netbox/tests/dummy_plugin/template_content.py +++ b/netbox/netbox/tests/dummy_plugin/template_content.py @@ -1,6 +1,12 @@ from netbox.plugins.templates import PluginTemplateExtension +class GlobalContent(PluginTemplateExtension): + + def navbar(self): + return "GLOBAL CONTENT - NAVBAR" + + class SiteContent(PluginTemplateExtension): model = 'dcim.site' @@ -20,4 +26,4 @@ class SiteContent(PluginTemplateExtension): return "SITE CONTENT - LIST BUTTONS" -template_extensions = [SiteContent] +template_extensions = [GlobalContent, SiteContent] diff --git a/netbox/netbox/tests/test_plugins.py b/netbox/netbox/tests/test_plugins.py index 9ce20e204..c85f257fa 100644 --- a/netbox/netbox/tests/test_plugins.py +++ b/netbox/netbox/tests/test_plugins.py @@ -99,8 +99,9 @@ class PluginTest(TestCase): """ Check that plugin TemplateExtensions are registered. """ - from netbox.tests.dummy_plugin.template_content import SiteContent + from netbox.tests.dummy_plugin.template_content import GlobalContent, SiteContent + self.assertIn(GlobalContent, registry['plugins']['template_extensions'][None]) self.assertIn(SiteContent, registry['plugins']['template_extensions']['dcim.site']) def test_registered_columns(self): diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index d53591cb4..940f74346 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -2,6 +2,7 @@ {% extends 'base/base.html' %} {% load helpers %} {% load navigation %} +{% load plugins %} {% load static %} {% load i18n %} @@ -51,8 +52,12 @@ Blocks: