Răsfoiți Sursa

Reorganize plugin resources

jeremystretch 3 ani în urmă
părinte
comite
e7f54c5867

+ 4 - 191
netbox/extras/plugins/__init__.py

@@ -1,17 +1,15 @@
 import collections
 import collections
-import inspect
-from packaging import version
 
 
 from django.apps import AppConfig
 from django.apps import AppConfig
 from django.core.exceptions import ImproperlyConfigured
 from django.core.exceptions import ImproperlyConfigured
-from django.template.loader import get_template
 from django.utils.module_loading import import_string
 from django.utils.module_loading import import_string
+from packaging import version
 
 
-from netbox.navigation import MenuGroup
 from netbox.registry import registry
 from netbox.registry import registry
 from netbox.search import register_search
 from netbox.search import register_search
-from utilities.choices import ButtonColorChoices
-
+from .navigation import *
+from .registration import *
+from .templates import *
 
 
 # Initialize plugin registry
 # Initialize plugin registry
 registry['plugins'] = {
 registry['plugins'] = {
@@ -142,188 +140,3 @@ class PluginConfig(AppConfig):
         for setting, value in cls.default_settings.items():
         for setting, value in cls.default_settings.items():
             if setting not in user_config:
             if setting not in user_config:
                 user_config[setting] = value
                 user_config[setting] = value
-
-
-#
-# Template content injection
-#
-
-class PluginTemplateExtension:
-    """
-    This class is used to register plugin content to be injected into core NetBox templates. It contains methods
-    that are overridden by plugin authors to return template content.
-
-    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
-    * request - The current request
-    * settings - Global NetBox settings
-    * config - Plugin-specific configuration parameters
-    """
-    model = None
-
-    def __init__(self, context):
-        self.context = context
-
-    def render(self, template_name, extra_context=None):
-        """
-        Convenience method for rendering the specified Django template using the default context data. An additional
-        context dictionary may be passed as `extra_context`.
-        """
-        if extra_context is None:
-            extra_context = {}
-        elif not isinstance(extra_context, dict):
-            raise TypeError("extra_context must be a dictionary")
-
-        return get_template(template_name).render({**self.context, **extra_context})
-
-    def left_page(self):
-        """
-        Content that will be rendered on the left of 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 right_page(self):
-        """
-        Content that will be rendered on the right of 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 full_width_page(self):
-        """
-        Content that will be rendered within the full width of 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 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 register_template_extensions(class_list):
-    """
-    Register a list of PluginTemplateExtension classes
-    """
-    # Validation
-    for template_extension in class_list:
-        if not inspect.isclass(template_extension):
-            raise TypeError(f"PluginTemplateExtension class {template_extension} was passed as an instance!")
-        if not issubclass(template_extension, PluginTemplateExtension):
-            raise TypeError(f"{template_extension} is not a subclass of extras.plugins.PluginTemplateExtension!")
-        if template_extension.model is None:
-            raise TypeError(f"PluginTemplateExtension class {template_extension} does not define a valid model!")
-
-        registry['plugins']['template_extensions'][template_extension.model].append(template_extension)
-
-
-#
-# Navigation menu links
-#
-
-class PluginMenu:
-    icon_class = 'mdi mdi-puzzle'
-
-    def __init__(self, label, groups, icon_class=None):
-        self.label = label
-        self.groups = [
-            MenuGroup(label, items) for label, items in groups
-        ]
-        if icon_class is not None:
-            self.icon_class = icon_class
-
-
-class PluginMenuItem:
-    """
-    This class represents a navigation menu item. This constitutes primary link and its text, but also allows for
-    specifying additional link buttons that appear to the right of the item in the van menu.
-
-    Links are specified as Django reverse URL strings.
-    Buttons are each specified as a list of PluginMenuButton instances.
-    """
-    permissions = []
-    buttons = []
-
-    def __init__(self, link, link_text, permissions=None, buttons=None):
-        self.link = link
-        self.link_text = link_text
-        if permissions is not None:
-            if type(permissions) not in (list, tuple):
-                raise TypeError("Permissions must be passed as a tuple or list.")
-            self.permissions = permissions
-        if buttons is not None:
-            if type(buttons) not in (list, tuple):
-                raise TypeError("Buttons must be passed as a tuple or list.")
-            self.buttons = buttons
-
-
-class PluginMenuButton:
-    """
-    This class represents a button within a PluginMenuItem. Note that button colors should come from
-    ButtonColorChoices.
-    """
-    color = ButtonColorChoices.DEFAULT
-    permissions = []
-
-    def __init__(self, link, title, icon_class, color=None, permissions=None):
-        self.link = link
-        self.title = title
-        self.icon_class = icon_class
-        if permissions is not None:
-            if type(permissions) not in (list, tuple):
-                raise TypeError("Permissions must be passed as a tuple or list.")
-            self.permissions = permissions
-        if color is not None:
-            if color not in ButtonColorChoices.values():
-                raise ValueError("Button color must be a choice within ButtonColorChoices.")
-            self.color = color
-
-
-def register_menu(menu):
-    if not isinstance(menu, PluginMenu):
-        raise TypeError(f"{menu} must be an instance of extras.plugins.PluginMenu")
-    registry['plugins']['menus'].append(menu)
-
-
-def register_menu_items(section_name, class_list):
-    """
-    Register a list of PluginMenuItem instances for a given menu section (e.g. plugin name)
-    """
-    # Validation
-    for menu_link in class_list:
-        if not isinstance(menu_link, PluginMenuItem):
-            raise TypeError(f"{menu_link} must be an instance of extras.plugins.PluginMenuItem")
-        for button in menu_link.buttons:
-            if not isinstance(button, PluginMenuButton):
-                raise TypeError(f"{button} must be an instance of extras.plugins.PluginMenuButton")
-
-    registry['plugins']['menu_items'][section_name] = class_list
-
-
-#
-# GraphQL schemas
-#
-
-def register_graphql_schema(graphql_schema):
-    """
-    Register a GraphQL schema class for inclusion in NetBox's GraphQL API.
-    """
-    registry['plugins']['graphql_schemas'].append(graphql_schema)
-
-
-#
-# User preferences
-#
-
-def register_user_preferences(plugin_name, preferences):
-    """
-    Register a list of user preferences defined by a plugin.
-    """
-    registry['plugins']['preferences'][plugin_name] = preferences

+ 66 - 0
netbox/extras/plugins/navigation.py

@@ -0,0 +1,66 @@
+from netbox.navigation import MenuGroup
+from utilities.choices import ButtonColorChoices
+
+__all__ = (
+    'PluginMenu',
+    'PluginMenuButton',
+    'PluginMenuItem',
+)
+
+
+class PluginMenu:
+    icon_class = 'mdi mdi-puzzle'
+
+    def __init__(self, label, groups, icon_class=None):
+        self.label = label
+        self.groups = [
+            MenuGroup(label, items) for label, items in groups
+        ]
+        if icon_class is not None:
+            self.icon_class = icon_class
+
+
+class PluginMenuItem:
+    """
+    This class represents a navigation menu item. This constitutes primary link and its text, but also allows for
+    specifying additional link buttons that appear to the right of the item in the van menu.
+
+    Links are specified as Django reverse URL strings.
+    Buttons are each specified as a list of PluginMenuButton instances.
+    """
+    permissions = []
+    buttons = []
+
+    def __init__(self, link, link_text, permissions=None, buttons=None):
+        self.link = link
+        self.link_text = link_text
+        if permissions is not None:
+            if type(permissions) not in (list, tuple):
+                raise TypeError("Permissions must be passed as a tuple or list.")
+            self.permissions = permissions
+        if buttons is not None:
+            if type(buttons) not in (list, tuple):
+                raise TypeError("Buttons must be passed as a tuple or list.")
+            self.buttons = buttons
+
+
+class PluginMenuButton:
+    """
+    This class represents a button within a PluginMenuItem. Note that button colors should come from
+    ButtonColorChoices.
+    """
+    color = ButtonColorChoices.DEFAULT
+    permissions = []
+
+    def __init__(self, link, title, icon_class, color=None, permissions=None):
+        self.link = link
+        self.title = title
+        self.icon_class = icon_class
+        if permissions is not None:
+            if type(permissions) not in (list, tuple):
+                raise TypeError("Permissions must be passed as a tuple or list.")
+            self.permissions = permissions
+        if color is not None:
+            if color not in ButtonColorChoices.values():
+                raise ValueError("Button color must be a choice within ButtonColorChoices.")
+            self.color = color

+ 64 - 0
netbox/extras/plugins/registration.py

@@ -0,0 +1,64 @@
+import inspect
+
+from netbox.registry import registry
+from .navigation import PluginMenu, PluginMenuButton, PluginMenuItem
+from .templates import PluginTemplateExtension
+
+__all__ = (
+    'register_graphql_schema',
+    'register_menu',
+    'register_menu_items',
+    'register_template_extensions',
+    'register_user_preferences',
+)
+
+
+def register_template_extensions(class_list):
+    """
+    Register a list of PluginTemplateExtension classes
+    """
+    # Validation
+    for template_extension in class_list:
+        if not inspect.isclass(template_extension):
+            raise TypeError(f"PluginTemplateExtension class {template_extension} was passed as an instance!")
+        if not issubclass(template_extension, PluginTemplateExtension):
+            raise TypeError(f"{template_extension} is not a subclass of extras.plugins.PluginTemplateExtension!")
+        if template_extension.model is None:
+            raise TypeError(f"PluginTemplateExtension class {template_extension} does not define a valid model!")
+
+        registry['plugins']['template_extensions'][template_extension.model].append(template_extension)
+
+
+def register_menu(menu):
+    if not isinstance(menu, PluginMenu):
+        raise TypeError(f"{menu} must be an instance of extras.plugins.PluginMenu")
+    registry['plugins']['menus'].append(menu)
+
+
+def register_menu_items(section_name, class_list):
+    """
+    Register a list of PluginMenuItem instances for a given menu section (e.g. plugin name)
+    """
+    # Validation
+    for menu_link in class_list:
+        if not isinstance(menu_link, PluginMenuItem):
+            raise TypeError(f"{menu_link} must be an instance of extras.plugins.PluginMenuItem")
+        for button in menu_link.buttons:
+            if not isinstance(button, PluginMenuButton):
+                raise TypeError(f"{button} must be an instance of extras.plugins.PluginMenuButton")
+
+    registry['plugins']['menu_items'][section_name] = class_list
+
+
+def register_graphql_schema(graphql_schema):
+    """
+    Register a GraphQL schema class for inclusion in NetBox's GraphQL API.
+    """
+    registry['plugins']['graphql_schemas'].append(graphql_schema)
+
+
+def register_user_preferences(plugin_name, preferences):
+    """
+    Register a list of user preferences defined by a plugin.
+    """
+    registry['plugins']['preferences'][plugin_name] = preferences

+ 65 - 0
netbox/extras/plugins/templates.py

@@ -0,0 +1,65 @@
+from django.template.loader import get_template
+
+__all__ = (
+    'PluginTemplateExtension',
+)
+
+
+class PluginTemplateExtension:
+    """
+    This class is used to register plugin content to be injected into core NetBox templates. It contains methods
+    that are overridden by plugin authors to return template content.
+
+    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
+    * request - The current request
+    * settings - Global NetBox settings
+    * config - Plugin-specific configuration parameters
+    """
+    model = None
+
+    def __init__(self, context):
+        self.context = context
+
+    def render(self, template_name, extra_context=None):
+        """
+        Convenience method for rendering the specified Django template using the default context data. An additional
+        context dictionary may be passed as `extra_context`.
+        """
+        if extra_context is None:
+            extra_context = {}
+        elif not isinstance(extra_context, dict):
+            raise TypeError("extra_context must be a dictionary")
+
+        return get_template(template_name).render({**self.context, **extra_context})
+
+    def left_page(self):
+        """
+        Content that will be rendered on the left of 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 right_page(self):
+        """
+        Content that will be rendered on the right of 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 full_width_page(self):
+        """
+        Content that will be rendered within the full width of 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 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