diff --git a/InvenTree/plugin/builtin/integration/mixins.py b/InvenTree/plugin/builtin/integration/mixins.py index dce48304ec..57c9fb3f0a 100644 --- a/InvenTree/plugin/builtin/integration/mixins.py +++ b/InvenTree/plugin/builtin/integration/mixins.py @@ -9,6 +9,8 @@ import requests from django.urls import include, re_path from django.db.utils import OperationalError, ProgrammingError +import InvenTree.helpers + from plugin.models import PluginConfig, PluginSetting from plugin.urls import PLUGIN_BASE from plugin.helpers import MixinImplementationError, MixinNotImplementedError @@ -563,26 +565,72 @@ class PanelMixin: This method is provided with: - - page: The name of the page e.g. 'part-detail' - - instance: The model instance specific to the page - - request: The request object responsible for the page load - - It must return a list of CustomPanel class instances (see below). + - view : The View object which is being rendered + - request : The HTTPRequest object Note that as this is called dynamically (per request), then the actual panels returned can vary depending on the particular request or page - """ + The 'get_custom_panels' method must return a list of dict objects, each with the following keys: - class CustomPanel: - ... + - title : The title of the panel, to appear in the sidebar menu + - description : Extra descriptive text (optional) + - icon : The icon to appear in the sidebar menu + - content : The HTML content to appear in the panel, OR + - content_template : A template file which will be rendered to produce the panel content + - javascript : The javascript content to be rendered when the panel is loade, OR + - javascript_template : A template file which will be rendered to produce javascript + + e.g. + + { + 'title': "Updates", + 'description': "Latest updates for this part", + 'javascript': 'alert("You just loaded this panel!")', + 'content': 'Hello world', + } + + """ class MixinMeta: MIXIN_NAME = 'Panel' - + def __init__(self): super().__init__() self.add_mixin('panel', True, __class__) - - def get_custom_panels(self, page, instance, request): + + def render_panels(self, view, request): + + panels = [] + + for panel in self.get_custom_panels(view, request): + + if 'content_template' in panel: + # TODO: Render the actual content + ... + + if 'javascript_template' in panel: + # TODO: Render the actual content + ... + + # Check for required keys + required_keys = ['title', 'content'] + + if any([key not in panel for key in required_keys]): + logger.warning(f"Custom panel for plugin '{__class__}' is missing a required key") + continue + + # Add some information on this plugin + panel['plugin'] = self + panel['slug'] = self.slug + + # Add a 'key' for the panel, which is mostly guaranteed to be unique + panel['key'] = InvenTree.helpers.generateTestKey(self.slug + panel.get('title', 'panel')) + + panels.append(panel) + + return panels + + def get_custom_panels(self, view, request): + """ This method *must* be implemented by the plugin class """ raise NotImplementedError(f"{__class__} is missing the 'get_custom_panels' method") diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 1249d95aa3..45961d7a8b 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -307,14 +307,17 @@ class PluginsRegistry: # TODO check more stuff -> as of Nov 2021 there are not many checks in place # but we could enhance those to check signatures, run the plugin against a whitelist etc. logger.info(f'Loading integration plugin {plugin.PLUGIN_NAME}') + try: plugin = plugin() except Exception as error: # log error and raise it -> disable plugin handle_error(error, log_name='init') - logger.info(f'Loaded integration plugin {plugin.slug}') + logger.debug(f'Loaded integration plugin {plugin.PLUGIN_NAME}') + plugin.is_package = was_packaged + if plugin_db_setting: plugin.pk = plugin_db_setting.pk diff --git a/InvenTree/plugin/samples/integration/custom_panel_sample.py b/InvenTree/plugin/samples/integration/custom_panel_sample.py index c2bae15548..5cca44f524 100644 --- a/InvenTree/plugin/samples/integration/custom_panel_sample.py +++ b/InvenTree/plugin/samples/integration/custom_panel_sample.py @@ -5,6 +5,9 @@ Sample plugin which renders custom panels on certain pages from plugin import IntegrationPluginBase from plugin.mixins import PanelMixin +from part.views import PartDetail +from stock.views import StockLocationDetail + class CustomPanelSample(PanelMixin, IntegrationPluginBase): """ @@ -15,8 +18,51 @@ class CustomPanelSample(PanelMixin, IntegrationPluginBase): PLUGIN_SLUG = "panel" PLUGIN_TITLE = "Custom Panel Example" - def get_custom_panels(self, page, instance, request): + def get_custom_panels(self, view, request): - print("get_custom_panels:") + panels = [ + { + # This 'hello world' panel will be displayed on any view which implements custom panels + 'title': 'Hello World', + 'icon': 'fas fa-boxes', + 'content': 'Hello world!', + 'description': 'A simple panel which renders hello world', + 'javascript': 'alert("Hello world");', + }, + { + # This panel will not be displayed, as it is missing the 'content' key + 'title': 'No Content', + } + ] - return [] \ No newline at end of file + # This panel will *only* display on the PartDetail view + if isinstance(view, PartDetail): + panels.append({ + 'title': 'Custom Part Panel', + 'icon': 'fas fa-shapes', + 'content': 'This content only appears on the PartDetail page, you know!', + }) + + # This panel will *only* display on the StockLocation view, + # and *only* if the StockLocation has *no* child locations + if isinstance(view, StockLocationDetail): + + print("yep, stocklocation view!") + + try: + loc = view.get_object() + + if not loc.get_descendants(include_self=False).exists(): + panels.append({ + 'title': 'Childless', + 'icon': 'fa-user', + 'content': '