Adds a new "Panel" mixin which can render custom panels on given pages

- Adds item to sidebar menu
- Adds panel content
- Runs custom javascript when the page is loaded
This commit is contained in:
Oliver Walters 2022-05-06 22:49:51 +10:00
parent 28e16616e5
commit 7b8a10173d
3 changed files with 112 additions and 15 deletions

View File

@ -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': '<b>Hello world</b>',
}
"""
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")

View File

@ -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

View File

@ -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': '<b>Hello world!</b>',
'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 []
# This panel will *only* display on the PartDetail view
if isinstance(view, PartDetail):
panels.append({
'title': 'Custom Part Panel',
'icon': 'fas fa-shapes',
'content': '<em>This content only appears on the PartDetail page, you know!</em>',
})
# 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': '<h4>I have no children!</h4>'
})
else:
print("abcdefgh")
except:
print("error could not get object!")
pass
return panels