2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-30 20:46:47 +00:00

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.urls import include, re_path
from django.db.utils import OperationalError, ProgrammingError from django.db.utils import OperationalError, ProgrammingError
import InvenTree.helpers
from plugin.models import PluginConfig, PluginSetting from plugin.models import PluginConfig, PluginSetting
from plugin.urls import PLUGIN_BASE from plugin.urls import PLUGIN_BASE
from plugin.helpers import MixinImplementationError, MixinNotImplementedError from plugin.helpers import MixinImplementationError, MixinNotImplementedError
@ -563,19 +565,32 @@ class PanelMixin:
This method is provided with: This method is provided with:
- page: The name of the page e.g. 'part-detail' - view : The View object which is being rendered
- instance: The model instance specific to the page - request : The HTTPRequest object
- request: The request object responsible for the page load
It must return a list of CustomPanel class instances (see below).
Note that as this is called dynamically (per request), Note that as this is called dynamically (per request),
then the actual panels returned can vary depending on the particular request or page 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: class MixinMeta:
MIXIN_NAME = 'Panel' MIXIN_NAME = 'Panel'
@ -584,5 +599,38 @@ class PanelMixin:
super().__init__() super().__init__()
self.add_mixin('panel', True, __class__) 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") 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 # 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. # but we could enhance those to check signatures, run the plugin against a whitelist etc.
logger.info(f'Loading integration plugin {plugin.PLUGIN_NAME}') logger.info(f'Loading integration plugin {plugin.PLUGIN_NAME}')
try: try:
plugin = plugin() plugin = plugin()
except Exception as error: except Exception as error:
# log error and raise it -> disable plugin # log error and raise it -> disable plugin
handle_error(error, log_name='init') 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 plugin.is_package = was_packaged
if plugin_db_setting: if plugin_db_setting:
plugin.pk = plugin_db_setting.pk 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 import IntegrationPluginBase
from plugin.mixins import PanelMixin from plugin.mixins import PanelMixin
from part.views import PartDetail
from stock.views import StockLocationDetail
class CustomPanelSample(PanelMixin, IntegrationPluginBase): class CustomPanelSample(PanelMixin, IntegrationPluginBase):
""" """
@ -15,8 +18,51 @@ class CustomPanelSample(PanelMixin, IntegrationPluginBase):
PLUGIN_SLUG = "panel" PLUGIN_SLUG = "panel"
PLUGIN_TITLE = "Custom Panel Example" 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