mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 12:36:45 +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:
parent
28e16616e5
commit
7b8a10173d
@ -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,26 +565,72 @@ 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'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
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")
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user