Просмотр исходного кода

Closes #16359: Add navbar() method to PluginTemplateExtension

Jeremy Stretch 1 год назад
Родитель
Сommit
50169365a9

+ 3 - 0
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:

+ 1 - 9
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)
 
 

+ 9 - 1
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 '<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

+ 7 - 1
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]

+ 2 - 1
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):

+ 6 - 1
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:
       <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>

+ 9 - 1
netbox/utilities/templatetags/plugins.py

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