From 1c2336be6060b844add9987cb8b289052f38998d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 12 Jul 2024 09:52:07 -0400 Subject: [PATCH] Closes #16776: Extend PluginTemplateExtension to render custom alerts for objects (#16889) * Closes #16776: Extend PluginTemplateExtension to render custom alerts for objects * Fix bug in _get_registered_content() --- docs/plugins/development/views.md | 5 +- netbox/netbox/plugins/templates.py | 51 +++++++++++++------ .../tests/dummy_plugin/template_content.py | 9 ++-- netbox/templates/base/layout.html | 4 ++ netbox/templates/generic/object.html | 4 ++ netbox/utilities/templatetags/plugins.py | 26 ++++++---- 6 files changed, 69 insertions(+), 30 deletions(-) diff --git a/docs/plugins/development/views.md b/docs/plugins/development/views.md index 9781cfa55..cbf920ad5 100644 --- a/docs/plugins/development/views.md +++ b/docs/plugins/development/views.md @@ -196,11 +196,12 @@ 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 | +| `list_buttons()` | List view | Add buttons to the top of the page | +| `buttons()` | Object view | Add buttons to the top of the page | +| `alerts()` | Object view | Inject content at the top of the page | | `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." diff --git a/netbox/netbox/plugins/templates.py b/netbox/netbox/plugins/templates.py index 5fa1959b8..e1f4b7a47 100644 --- a/netbox/netbox/plugins/templates.py +++ b/netbox/netbox/plugins/templates.py @@ -38,6 +38,10 @@ class PluginTemplateExtension: return get_template(template_name).render({**self.context, **extra_context}) + # + # Global methods + # + def navbar(self): """ Content that will be rendered inside the top navigation menu. Content should be returned as an HTML @@ -45,6 +49,37 @@ class PluginTemplateExtension: """ raise NotImplementedError + # + # Object list views + # + + def list_buttons(self): + """ + Buttons that will be rendered and added to the existing list of buttons on the list view. 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 + + # + # Object detail views + # + + def buttons(self): + """ + Buttons that will be rendered and added to the existing list of buttons on the detail page view. 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 alerts(self): + """ + Arbitrary content to be inserted at the top of an object's detail view. 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 @@ -65,19 +100,3 @@ class PluginTemplateExtension: HTML string. Note that content does not need to be marked as safe because this is automatically handled. """ raise NotImplementedError - - def buttons(self): - """ - Buttons that will be rendered and added to the existing list of buttons on the detail page view. 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 list_buttons(self): - """ - Buttons that will be rendered and added to the existing list of buttons on the list view. 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 diff --git a/netbox/netbox/tests/dummy_plugin/template_content.py b/netbox/netbox/tests/dummy_plugin/template_content.py index b7157e370..e9a6b9da1 100644 --- a/netbox/netbox/tests/dummy_plugin/template_content.py +++ b/netbox/netbox/tests/dummy_plugin/template_content.py @@ -10,6 +10,12 @@ class GlobalContent(PluginTemplateExtension): class SiteContent(PluginTemplateExtension): models = ['dcim.site'] + def buttons(self): + return "SITE CONTENT - BUTTONS" + + def alerts(self): + return "SITE CONTENT - ALERTS" + def left_page(self): return "SITE CONTENT - LEFT PAGE" @@ -19,9 +25,6 @@ class SiteContent(PluginTemplateExtension): def full_width_page(self): return "SITE CONTENT - FULL WIDTH PAGE" - def buttons(self): - return "SITE CONTENT - BUTTONS" - def list_buttons(self): return "SITE CONTENT - LIST BUTTONS" diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index 40e371826..9ab80e0c1 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -110,6 +110,10 @@ Blocks:
+ {# Page alerts #} + {% block alerts %}{% endblock %} + {# /Page alerts #} + {# Page content #} {% block content %}{% endblock %} {# /Page content #} diff --git a/netbox/templates/generic/object.html b/netbox/templates/generic/object.html index bf0e1ae9a..e0995f360 100644 --- a/netbox/templates/generic/object.html +++ b/netbox/templates/generic/object.html @@ -111,6 +111,10 @@ Context: {% endblock tabs %} +{% block alerts %} + {% plugin_alerts object %} +{% endblock alerts %} + {% block content %}{% endblock %} {% block modals %} diff --git a/netbox/utilities/templatetags/plugins.py b/netbox/utilities/templatetags/plugins.py index 3a5eb7346..16e65d697 100644 --- a/netbox/utilities/templatetags/plugins.py +++ b/netbox/utilities/templatetags/plugins.py @@ -23,7 +23,7 @@ def _get_registered_content(obj, method, template_context): } template_extensions = list(registry['plugins']['template_extensions'].get(None, [])) - if obj is not None: + if hasattr(obj, '_meta'): model_name = obj._meta.label_lower template_extensions.extend(registry['plugins']['template_extensions'].get(model_name, [])) for template_extension in template_extensions: @@ -53,6 +53,14 @@ def plugin_navbar(context): return _get_registered_content(None, 'navbar', context) +@register.simple_tag(takes_context=True) +def plugin_list_buttons(context, model): + """ + Render all list buttons registered by plugins + """ + return _get_registered_content(model, 'list_buttons', context) + + @register.simple_tag(takes_context=True) def plugin_buttons(context, obj): """ @@ -61,6 +69,14 @@ def plugin_buttons(context, obj): return _get_registered_content(obj, 'buttons', context) +@register.simple_tag(takes_context=True) +def plugin_alerts(context, obj): + """ + Render all object alerts registered by plugins + """ + return _get_registered_content(obj, 'alerts', context) + + @register.simple_tag(takes_context=True) def plugin_left_page(context, obj): """ @@ -83,11 +99,3 @@ def plugin_full_width_page(context, obj): Render all full width page content registered by plugins """ return _get_registered_content(obj, 'full_width_page', context) - - -@register.simple_tag(takes_context=True) -def plugin_list_buttons(context, model): - """ - Render all list buttons registered by plugins - """ - return _get_registered_content(model, 'list_buttons', context)