From 71a8a13644ea4643202a626d7860ab6f6ac5351d Mon Sep 17 00:00:00 2001 From: John Anderson Date: Sun, 1 Mar 2020 03:24:17 -0500 Subject: [PATCH] add api urls and signals interface for detail route buttons --- netbox/extras/plugins/__init__.py | 0 netbox/extras/plugins/signals.py | 34 +++++++++++++++++++ .../extras/plugins/templatetags/__init__.py | 0 netbox/extras/templatetags/plugins.py | 27 +++++++++++++++ netbox/netbox/settings.py | 12 +++---- netbox/netbox/urls.py | 19 +++++++++-- netbox/templates/dcim/device.html | 2 ++ netbox/templates/dcim/site.html | 2 ++ 8 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 netbox/extras/plugins/__init__.py create mode 100644 netbox/extras/plugins/signals.py create mode 100644 netbox/extras/plugins/templatetags/__init__.py create mode 100644 netbox/extras/templatetags/plugins.py diff --git a/netbox/extras/plugins/__init__.py b/netbox/extras/plugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/netbox/extras/plugins/signals.py b/netbox/extras/plugins/signals.py new file mode 100644 index 000000000..7d0567b1b --- /dev/null +++ b/netbox/extras/plugins/signals.py @@ -0,0 +1,34 @@ +import django.dispatch +from django.dispatch.dispatcher import NO_RECEIVERS + + +class PluginSignal(django.dispatch.Signal): + + def _sorted_receivers(self, sender): + orig_list = self._live_receivers(sender) + sorted_list = sorted( + orig_list, + key=lambda receiver: ( + receiver.__module__, + receiver.__name__, + ) + ) + return sorted_list + + def send(self, sender, **kwargs): + responses = [] + if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: + return responses + + for receiver in self._sorted_receivers(sender): + response = receiver(signal=self, sender=sender, **kwargs) + responses.append((receiver, response)) + return responses + + +""" +This signal collects templates which render buttons for object detail pages +""" +register_detail_page_buttons = PluginSignal( + providing_args=[] +) diff --git a/netbox/extras/plugins/templatetags/__init__.py b/netbox/extras/plugins/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/netbox/extras/templatetags/plugins.py b/netbox/extras/templatetags/plugins.py new file mode 100644 index 000000000..dc3443d0c --- /dev/null +++ b/netbox/extras/templatetags/plugins.py @@ -0,0 +1,27 @@ +from django import template as template_ +from django.template.loader import get_template +from django.utils.safestring import mark_safe + +from extras.plugins.signals import register_detail_page_buttons + + +register = template_.Library() + + +@register.simple_tag() +def plugin_buttons(obj): + """ + Fire signal to collect all buttons registered by plugins + """ + html = '' + responses = register_detail_page_buttons.send(obj) + for receiver, response in responses: + if not isinstance(response, list): + response = [response] + for template in response: + if isinstance(template, str): + template_text = get_template(template).render({'obj': obj}) + html += template_text + + return mark_safe(html) + diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index b65d0abe7..8f1678b95 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -261,7 +261,7 @@ INSTALLED_APPS = [ ] # Middleware -MIDDLEWARE = ( +MIDDLEWARE = [ 'debug_toolbar.middleware.DebugToolbarMiddleware', 'django_prometheus.middleware.PrometheusBeforeMiddleware', 'corsheaders.middleware.CorsMiddleware', @@ -277,7 +277,7 @@ MIDDLEWARE = ( 'utilities.middleware.APIVersionMiddleware', 'extras.middleware.ObjectChangeMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware', -) +] ROOT_URLCONF = 'netbox.urls' @@ -622,12 +622,12 @@ for entry_point in iter_entry_points(group='netbox.plugin', name=None): # Add middleware plugin_middleware = getattr(app_config_meta, 'middleware', []) - if plugin_middleware: + if plugin_middleware and isinstance(plugin_middleware, list): MIDDLEWARE.extend(plugin_middleware) - # Add middleware + # Add installed apps plugin_installed_apps = getattr(app_config_meta, 'installed_apps', []) - if plugin_installed_apps: + if plugin_installed_apps and isinstance(plugin_installed_apps, list): INSTALLED_APPS.extend(plugin_installed_apps) # Verify required configuration settings @@ -637,7 +637,7 @@ for entry_point in iter_entry_points(group='netbox.plugin', name=None): if setting not in PLUGINS_CONFIG[plugin]: raise ImproperlyConfigured( "Plugin {} requires '{}' to be present in the PLUGINS_CONFIG section of configuration.py.".format( - plugin, + plugin, setting ) ) diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index 93bb84e06..8d59a33d1 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -70,18 +70,31 @@ _patterns = [ # Plugins plugin_patterns = [] +plugin_api_patterns = [] for app in apps.get_app_configs(): if hasattr(app, 'NetBoxPluginMeta'): if importlib.util.find_spec('{}.urls'.format(app.name)): urls = importlib.import_module('{}.urls'.format(app.name)) url_slug = getattr(app.NetBoxPluginMeta, 'url_slug', app.label) - plugin_patterns.append( - path('{}/'.format(url_slug), include((urls.urlpatterns, app.label))) - ) + if hasattr(urls, 'urlpatterns'): + plugin_patterns.append( + path('{}/'.format(url_slug), include((urls.urlpatterns, app.label))) + ) + if importlib.util.find_spec('{}.api'.format(app.name)): + if importlib.util.find_spec('{}.api.urls'.format(app.name)): + urls = importlib.import_module('{}.api.urls'.format(app.name)) + if hasattr(urls, 'urlpatterns'): + url_slug = getattr(app.NetBoxPluginMeta, 'url_slug', app.label) + plugin_api_patterns.append( + path('{}/'.format(url_slug), include((urls.urlpatterns, app.label))) + ) _patterns.append( path('plugins/', include((plugin_patterns, 'plugins'))) ) +_patterns.append( + path('api/plugins/', include((plugin_api_patterns, 'plugins-api'))) +) if settings.DEBUG: import debug_toolbar diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 8c457121f..74e2549bd 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -3,6 +3,7 @@ {% load static %} {% load helpers %} {% load custom_links %} +{% load plugins %} {% block title %}{{ device }}{% endblock %} @@ -36,6 +37,7 @@
+ {% plugin_buttons device %} {% if show_graphs %}
+ {% plugin_buttons site %} {% if show_graphs %}