Coverage for src/backend/InvenTree/plugin/samples/integration/user_interface_sample.py: 0%
55 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""Sample plugin which demonstrates user interface integrations."""
3import random
4import time
6from django.utils.translation import gettext_lazy as _
8from InvenTree.version import INVENTREE_SW_VERSION
9from part.models import Part
10from plugin import InvenTreePlugin
11from plugin.mixins import SettingsMixin, UserInterfaceMixin
14class SampleUserInterfacePlugin(SettingsMixin, UserInterfaceMixin, InvenTreePlugin):
15 """A sample plugin which demonstrates user interface integrations."""
17 NAME = 'SampleUI'
18 SLUG = 'sampleui'
19 TITLE = 'Sample User Interface Plugin'
20 DESCRIPTION = 'A sample plugin which demonstrates user interface integrations'
21 VERSION = '2.0'
23 ADMIN_SOURCE = 'ui_settings.js'
25 SETTINGS = {
26 'ENABLE_PART_PANELS': {
27 'name': _('Enable Part Panels'),
28 'description': _('Enable custom panels for Part views'),
29 'default': True,
30 'validator': bool,
31 },
32 'ENABLE_PURCHASE_ORDER_PANELS': {
33 'name': _('Enable Purchase Order Panels'),
34 'description': _('Enable custom panels for Purchase Order views'),
35 'default': False,
36 'validator': bool,
37 },
38 'ENABLE_BROKEN_PANELS': {
39 'name': _('Enable Broken Panels'),
40 'description': _('Enable broken panels for testing'),
41 'default': True,
42 'validator': bool,
43 },
44 'ENABLE_DYNAMIC_PANEL': {
45 'name': _('Enable Dynamic Panel'),
46 'description': _('Enable dynamic panels for testing'),
47 'default': True,
48 'validator': bool,
49 },
50 }
52 def get_ui_spotlight_actions(self, request, context, **kwargs):
53 """Return a list of custom actions to be injected into the UI spotlight."""
54 return [
55 {
56 'key': 'sample-action',
57 'title': 'Sample Action',
58 'description': 'This is a sample action for the spotlight search',
59 'icon': 'ti:search:outline',
60 'source': self.plugin_static_file(
61 'sample_action.js:performSampleAction'
62 ),
63 }
64 ]
66 def get_ui_panels(self, request, context, **kwargs):
67 """Return a list of custom panels to be injected into the UI."""
68 panels = []
69 context = context or {}
71 # First, add a custom panel which will appear on every type of page
72 # This panel will contain a simple message
74 target_model = context.get('target_model', None)
75 target_id = context.get('target_id', None)
77 # A broken panel which tries to load a non-existent JS file
78 if target_id is not None and self.get_setting('ENABLE_BROKEN_PANElS'):
79 panels.append({
80 'key': 'broken-panel',
81 'title': 'Broken Panel',
82 'source': '/this/does/not/exist.js',
83 })
85 # A dynamic panel which will be injected into the UI (loaded from external file)
86 # Note that we additionally provide some "context" data to the front-end render function
87 if self.get_setting('ENABLE_DYNAMIC_PANEL'):
88 panels.append({
89 'key': 'dynamic-panel',
90 'title': 'Dynamic Panel',
91 'source': self.plugin_static_file('sample_panel.js'),
92 'icon': 'ti:wave-saw-tool:outline',
93 'context': {
94 'version': INVENTREE_SW_VERSION,
95 'plugin_version': self.VERSION,
96 'random': random.randint(1, 100),
97 'time': time.time(),
98 },
99 })
101 # Next, add a custom panel which will appear on the 'part' page
102 # Note that this content is rendered from a template file,
103 # using the django templating system
104 if self.get_setting('ENABLE_PART_PANELS') and target_model == 'part':
105 try:
106 part = Part.objects.get(pk=target_id)
107 except (Part.DoesNotExist, ValueError):
108 part = None
110 # Only display this panel for "active" parts
111 if part and part.active:
112 panels.append({
113 'key': 'part-panel',
114 'title': _('Part Panel'),
115 'source': self.plugin_static_file(
116 'sample_panel.js:renderPartPanel'
117 ),
118 'icon': 'ti:package:outline',
119 'context': {'part_name': part.name if part else ''},
120 })
122 # Next, add a custom panel which will appear on the 'purchaseorder' page
123 if target_model == 'purchaseorder' and self.get_setting(
124 'ENABLE_PURCHASE_ORDER_PANELS'
125 ):
126 panels.append({
127 'key': 'purchase_order_panel',
128 'title': 'Purchase Order Panel',
129 'source': self.plugin_static_file('sample_panel.js:renderPoPanel'),
130 })
132 # Admin panel - only visible to admin users
133 if request.user.is_superuser:
134 panels.append({
135 'key': 'admin-panel',
136 'title': 'Admin Panel',
137 'source': self.plugin_static_file(
138 'sample_panel.js:renderAdminOnlyPanel'
139 ),
140 })
142 return panels
144 def get_ui_dashboard_items(self, request, context, **kwargs):
145 """Return a list of custom dashboard items."""
146 items = [
147 {
148 'key': 'broken-dashboard-item',
149 'title': _('Broken Dashboard Item'),
150 'description': _(
151 'This is a broken dashboard item - it will not render!'
152 ),
153 'source': '/this/does/not/exist.js',
154 },
155 {
156 'key': 'sample-dashboard-item',
157 'title': _('Sample Dashboard Item'),
158 'description': _(
159 'This is a sample dashboard item. It renders a simple string of HTML content.'
160 ),
161 'source': self.plugin_static_file('sample_dashboard_item.js'),
162 },
163 {
164 'key': 'dynamic-dashboard-item',
165 'title': _('Context Dashboard Item'),
166 'description': 'A dashboard item which passes context data from the server',
167 'source': self.plugin_static_file(
168 'sample_dashboard_item.js:renderContextItem'
169 ),
170 'context': {'foo': 'bar', 'hello': 'world'},
171 'options': {'width': 3, 'height': 2},
172 },
173 ]
175 # Admin item - only visible to users with superuser access
176 if request.user.is_superuser:
177 items.append({
178 'key': 'admin-dashboard-item',
179 'title': _('Admin Dashboard Item'),
180 'description': _('This is an admin-only dashboard item.'),
181 'source': self.plugin_static_file('admin_dashboard_item.js'),
182 'options': {'width': 4, 'height': 2},
183 'context': {'secret-key': 'this-is-a-secret'},
184 })
186 return items
188 def get_ui_template_editors(self, request, context, **kwargs):
189 """Return a list of custom template editors."""
190 # If the context is a label template, return a custom template editor
191 if context.get('template_type') == 'labeltemplate':
192 return [
193 {
194 'key': 'sample-template-editor',
195 'title': 'Sample Template Editor',
196 'icon': 'ti:tags:outline',
197 'source': self.plugin_static_file(
198 'sample_template.js:getTemplateEditor'
199 ),
200 }
201 ]
203 return []
205 def get_ui_template_previews(self, request, context, **kwargs):
206 """Return a list of custom template previews."""
207 return [
208 {
209 'key': 'sample-template-preview',
210 'title': 'Sample Template Preview',
211 'icon': 'ti:category:outline',
212 'source': self.plugin_static_file(
213 'sample_preview.js:getTemplatePreview'
214 ),
215 }
216 ]
218 def get_ui_navigation_items(self, request, context, **kwargs):
219 """Return a list of custom navigation items."""
220 return [
221 {
222 'key': 'sample-nav-item',
223 'title': 'Sample Nav Item',
224 'icon': 'ti:menu',
225 'options': {'url': '/sample/page/'},
226 }
227 ]
229 def get_ui_primary_actions(self, request, context, **kwargs):
230 """Return a list of custom primary action buttons."""
231 return [
232 {
233 'key': 'sample-primary-action',
234 'title': 'Sample Primary Action',
235 'icon': 'ti:plus:outline',
236 'options': {'url': '/core/sample-primary-action/', 'color': 'orange'},
237 }
238 ]
240 def get_admin_context(self) -> dict:
241 """Return custom context data which can be rendered in the admin panel."""
242 return {'apple': 'banana', 'foo': 'bar', 'hello': 'world'}