Closes #16359: Add navbar() method to PluginTemplateExtension

This commit is contained in:
Jeremy Stretch 2024-06-04 14:24:57 -04:00
parent 87109f5539
commit 50169365a9
7 changed files with 37 additions and 14 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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 '<app_label>.<model_name>'. 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

View File

@ -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]

View File

@ -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):

View File

@ -2,6 +2,7 @@
{% extends 'base/base.html' %}
{% load helpers %}
{% load navigation %}
{% load plugins %}
{% load static %}
{% load i18n %}
@ -51,8 +52,12 @@ Blocks:
<div class="container-fluid">
<div class="navbar-nav flex-row align-items-center order-md-last">
{# Plugin content #}
{% plugin_navbar %}
{# Dark/light mode toggle #}
<div class="d-none d-md-flex">
<div class="d-none d-md-flex ms-2">
<button class="btn color-mode-toggle hide-theme-dark" title="{% trans "Enable dark mode" %}" data-bs-toggle="tooltip" data-bs-placement="bottom">
<i class="mdi mdi-lightbulb"></i>
</button>

View File

@ -22,7 +22,7 @@ def _get_registered_content(obj, method, template_context):
'perms': template_context['perms'],
}
model_name = obj._meta.label_lower
model_name = obj._meta.label_lower if obj is not None else None
template_extensions = registry['plugins']['template_extensions'].get(model_name, [])
for template_extension in template_extensions:
@ -43,6 +43,14 @@ def _get_registered_content(obj, method, template_context):
return mark_safe(html)
@register.simple_tag(takes_context=True)
def plugin_navbar(context):
"""
Render any navbar content embedded by plugins
"""
return _get_registered_content(None, 'navbar', context)
@register.simple_tag(takes_context=True)
def plugin_buttons(context, obj):
"""