2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 12:06:44 +00:00

Adds a new InvenTreePluginMixin mixin class for enabling custom plugin rendering on a page

- Any view which needs custom plugin code must implement this mixin
- Initially implement for the PartDetail page
This commit is contained in:
Oliver Walters 2022-05-06 22:52:52 +10:00
parent 7b8a10173d
commit c80b36fc2f
8 changed files with 74 additions and 10 deletions

View File

@ -38,6 +38,8 @@ from part.models import PartCategory
from common.models import InvenTreeSetting, ColorTheme from common.models import InvenTreeSetting, ColorTheme
from users.models import check_user_role, RuleSet from users.models import check_user_role, RuleSet
from plugin.registry import registry
from .forms import DeleteForm, EditUserForm, SetPasswordForm from .forms import DeleteForm, EditUserForm, SetPasswordForm
from .forms import SettingCategorySelectForm from .forms import SettingCategorySelectForm
from .helpers import str2bool from .helpers import str2bool
@ -56,6 +58,38 @@ def auth_request(request):
return HttpResponse(status=403) return HttpResponse(status=403)
class InvenTreePluginMixin:
"""
Custom view mixin which adds context data to the view,
based on loaded plugins.
This allows rendered pages to be augmented by loaded plugins.
"""
def get_plugin_panels(self):
"""
Return a list of extra 'plugin panels' associated with this view
"""
panels = []
for plug in registry.with_mixin('panel'):
panels += plug.render_panels(self, self.request)
return panels
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
if settings.PLUGINS_ENABLED:
ctx['plugin_panels'] = self.get_plugin_panels()
return ctx
class InvenTreeRoleMixin(PermissionRequiredMixin): class InvenTreeRoleMixin(PermissionRequiredMixin):
""" """
Permission class based on user roles, not user 'permissions'. Permission class based on user roles, not user 'permissions'.

View File

@ -397,9 +397,11 @@
</div> </div>
<table class='table table-condensed table-striped' id='manufacturer-part-table' data-toolbar='#manufacturer-button-toolbar'></table> <table class='table table-condensed table-striped' id='manufacturer-part-table' data-toolbar='#manufacturer-button-toolbar'></table>
</div> </div>
</div> </div>
</div> </div>
{% include "panel/plugin_panels.html" %}
{% endblock %} {% endblock %}
{% block js_load %} {% block js_load %}
@ -1083,4 +1085,6 @@
} }
}); });
{% include "panel/plugin_javascript.html" %}
{% endblock %} {% endblock %}

View File

@ -58,3 +58,5 @@
{% include "sidebar_item.html" with label="part-attachments" text=text icon="fa-paperclip" %} {% include "sidebar_item.html" with label="part-attachments" text=text icon="fa-paperclip" %}
{% trans "Notes" as text %} {% trans "Notes" as text %}
{% include "sidebar_item.html" with label="part-notes" text=text icon="fa-clipboard" %} {% include "sidebar_item.html" with label="part-notes" text=text icon="fa-clipboard" %}
{% include "panel/plugin_menu_items.html" %}

View File

@ -49,7 +49,7 @@ from order.models import PurchaseOrderLineItem
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.views import QRCodeView from InvenTree.views import QRCodeView
from InvenTree.views import InvenTreeRoleMixin from InvenTree.views import InvenTreeRoleMixin, InvenTreePluginMixin
from InvenTree.helpers import str2bool from InvenTree.helpers import str2bool
@ -365,7 +365,7 @@ class PartImportAjax(FileManagementAjaxView, PartImport):
return PartImport.validate(self, self.steps.current, form, **kwargs) return PartImport.validate(self, self.steps.current, form, **kwargs)
class PartDetail(InvenTreeRoleMixin, DetailView): class PartDetail(InvenTreeRoleMixin, InvenTreePluginMixin, DetailView):
""" Detail view for Part object """ Detail view for Part object
""" """

View File

@ -46,9 +46,6 @@ class CustomPanelSample(PanelMixin, IntegrationPluginBase):
# This panel will *only* display on the StockLocation view, # This panel will *only* display on the StockLocation view,
# and *only* if the StockLocation has *no* child locations # and *only* if the StockLocation has *no* child locations
if isinstance(view, StockLocationDetail): if isinstance(view, StockLocationDetail):
print("yep, stocklocation view!")
try: try:
loc = view.get_object() loc = view.get_object()
@ -58,11 +55,7 @@ class CustomPanelSample(PanelMixin, IntegrationPluginBase):
'icon': 'fa-user', 'icon': 'fa-user',
'content': '<h4>I have no children!</h4>' 'content': '<h4>I have no children!</h4>'
}) })
else:
print("abcdefgh")
except: except:
print("error could not get object!")
pass pass
return panels return panels

View File

@ -0,0 +1,10 @@
{% if plugin_panels %}
// Run custom javascript when plugin panels are loaded
{% for panel in plugin_panels %}
{% if panel.javascript %}
onPanelLoad('{{ panel.key }}', function() {
{{ panel.javascript | safe }}
});
{% endif %}
{% endfor %}
{% endif %}

View File

@ -0,0 +1,3 @@
{% for panel in plugin_panels %}
{% include "sidebar_item.html" with label=panel.key text=panel.title icon=panel.icon %}
{% endfor %}

View File

@ -0,0 +1,18 @@
{% for panel in plugin_panels %}
<div class='panel panel-hidden' id='panel-{{ panel.key }}'>
<div class='panel-heading'>
<div class='d-flex flex-wrap'>
<h4>{{ panel.title }}</h4>
{% include "spacer.html" %}
<div class='btn-group' role='group'>
<!-- TODO: Implement custom action buttons for plugin panels -->
</div>
</div>
</div>
<div class='panel-content'>
{{ panel.content | safe }}
</div>
</div>
{% endfor %}