Skip to content

Commit

Permalink
Closes #16359: Add navbar() method to PluginTemplateExtension
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Jun 4, 2024
1 parent 87109f5 commit bb6efa5
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 14 deletions.
3 changes: 3 additions & 0 deletions docs/plugins/development/views.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 1 addition & 9 deletions netbox/netbox/plugins/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
10 changes: 9 additions & 1 deletion netbox/netbox/plugins/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,6 +37,13 @@ def render(self, template_name, extra_context=None):

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
Expand Down
8 changes: 7 additions & 1 deletion netbox/netbox/tests/dummy_plugin/template_content.py
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -20,4 +26,4 @@ def list_buttons(self):
return "SITE CONTENT - LIST BUTTONS"


template_extensions = [SiteContent]
template_extensions = [GlobalContent, SiteContent]
3 changes: 2 additions & 1 deletion netbox/netbox/tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ def test_template_extensions(self):
"""
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):
Expand Down
7 changes: 6 additions & 1 deletion netbox/templates/base/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{% extends 'base/base.html' %}
{% load helpers %}
{% load navigation %}
{% load plugins %}
{% load static %}
{% load i18n %}

Expand Down Expand Up @@ -51,8 +52,12 @@ <h1 class="navbar-brand navbar-brand-autodark">
<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>
Expand Down
10 changes: 9 additions & 1 deletion netbox/utilities/templatetags/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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):
"""
Expand Down

0 comments on commit bb6efa5

Please sign in to comment.