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

1"""Sample plugin which demonstrates user interface integrations.""" 

2 

3import random 

4import time 

5 

6from django.utils.translation import gettext_lazy as _ 

7 

8from InvenTree.version import INVENTREE_SW_VERSION 

9from part.models import Part 

10from plugin import InvenTreePlugin 

11from plugin.mixins import SettingsMixin, UserInterfaceMixin 

12 

13 

14class SampleUserInterfacePlugin(SettingsMixin, UserInterfaceMixin, InvenTreePlugin): 

15 """A sample plugin which demonstrates user interface integrations.""" 

16 

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' 

22 

23 ADMIN_SOURCE = 'ui_settings.js' 

24 

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 } 

51 

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 ] 

65 

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 {} 

70 

71 # First, add a custom panel which will appear on every type of page 

72 # This panel will contain a simple message 

73 

74 target_model = context.get('target_model', None) 

75 target_id = context.get('target_id', None) 

76 

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 }) 

84 

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 }) 

100 

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 

109 

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 }) 

121 

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 }) 

131 

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 }) 

141 

142 return panels 

143 

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 ] 

174 

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 }) 

185 

186 return items 

187 

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 ] 

202 

203 return [] 

204 

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 ] 

217 

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 ] 

228 

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 ] 

239 

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'}