mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Implement new approach for plugin settings
- URL specifies plugin slug and setting key
This commit is contained in:
		| @@ -465,12 +465,35 @@ class NotificationUserSettingsApiTest(InvenTreeAPITestCase): | |||||||
| class PluginSettingsApiTest(InvenTreeAPITestCase): | class PluginSettingsApiTest(InvenTreeAPITestCase): | ||||||
|     """Tests for the plugin settings API""" |     """Tests for the plugin settings API""" | ||||||
|  |  | ||||||
|  |     def test_plugin_list(self): | ||||||
|  |         """List installed plugins via API""" | ||||||
|  |         url = reverse('api-plugin-list') | ||||||
|  |  | ||||||
|  |         response = self.get(url, expected_code=200) | ||||||
|  |  | ||||||
|     def test_api_list(self): |     def test_api_list(self): | ||||||
|         """Test list URL""" |         """Test list URL""" | ||||||
|         url = reverse('api-plugin-setting-list') |         url = reverse('api-plugin-setting-list') | ||||||
|  |  | ||||||
|         self.get(url, expected_code=200) |         self.get(url, expected_code=200) | ||||||
|  |  | ||||||
|  |     def test_invalid_plugin_slug(self): | ||||||
|  |         """Test that an invalid plugin slug returns a 404""" | ||||||
|  |          | ||||||
|  |         url = reverse('api-plugin-setting-detail', kwargs={'plugin': 'doesnotexist', 'key': 'doesnotmatter'}) | ||||||
|  |  | ||||||
|  |         response = self.get(url, expected_code=404) | ||||||
|  |  | ||||||
|  |         self.assertIn("Plugin 'doesnotexist' not installed", str(response.data)) | ||||||
|  |  | ||||||
|  |     def test_invalid_setting_key(self): | ||||||
|  |         """Test that an invalid setting key returns a 404""" | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  |     def test_uninitialized_setting(self): | ||||||
|  |         """Test that requesting an uninitialized setting creates the setting""" | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  |  | ||||||
| class WebhookMessageTests(TestCase): | class WebhookMessageTests(TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|   | |||||||
| @@ -71,10 +71,9 @@ class LabelPrintMixin: | |||||||
|  |  | ||||||
|         plugin_key = request.query_params.get('plugin', None) |         plugin_key = request.query_params.get('plugin', None) | ||||||
|  |  | ||||||
|         for slug, plugin in registry.plugins.items(): |         plugin = registry.get_plugin(plugin_key) | ||||||
|  |  | ||||||
|             if slug == plugin_key and plugin.mixin_enabled('labels'): |  | ||||||
|  |  | ||||||
|  |         if plugin: | ||||||
|             config = plugin.plugin_config() |             config = plugin.plugin_config() | ||||||
|  |  | ||||||
|             if config and config.active: |             if config and config.active: | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ from django.urls import include, re_path | |||||||
| from rest_framework import generics | from rest_framework import generics | ||||||
| from rest_framework import status | from rest_framework import status | ||||||
| from rest_framework import permissions | from rest_framework import permissions | ||||||
|  | from rest_framework.exceptions import NotFound | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
|  |  | ||||||
| from django_filters.rest_framework import DjangoFilterBackend | from django_filters.rest_framework import DjangoFilterBackend | ||||||
| @@ -17,6 +18,7 @@ from django_filters.rest_framework import DjangoFilterBackend | |||||||
| from common.api import GlobalSettingsPermissions | from common.api import GlobalSettingsPermissions | ||||||
| from plugin.models import PluginConfig, PluginSetting | from plugin.models import PluginConfig, PluginSetting | ||||||
| import plugin.serializers as PluginSerializers | import plugin.serializers as PluginSerializers | ||||||
|  | from plugin.registry import registry | ||||||
|  |  | ||||||
|  |  | ||||||
| class PluginList(generics.ListAPIView): | class PluginList(generics.ListAPIView): | ||||||
| @@ -120,6 +122,34 @@ class PluginSettingDetail(generics.RetrieveUpdateAPIView): | |||||||
|     queryset = PluginSetting.objects.all() |     queryset = PluginSetting.objects.all() | ||||||
|     serializer_class = PluginSerializers.PluginSettingSerializer |     serializer_class = PluginSerializers.PluginSettingSerializer | ||||||
|  |  | ||||||
|  |     def get_object(self): | ||||||
|  |         """ | ||||||
|  |         Lookup the plugin setting object, based on the URL. | ||||||
|  |         The URL provides the 'slug' of the plugin, and the 'key' of the setting. | ||||||
|  |  | ||||||
|  |         Both the 'slug' and 'key' must be valid, else a 404 error is raised | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         plugin_slug = self.kwargs['plugin'] | ||||||
|  |         key = self.kwargs['key'] | ||||||
|  |  | ||||||
|  |         # Check that the 'plugin' specified is valid! | ||||||
|  |         if not PluginConfig.objects.filter(key=plugin_slug).exists(): | ||||||
|  |             raise NotFound(detail=f"Plugin '{plugin_slug}' not installed") | ||||||
|  |  | ||||||
|  |         # Get the list of settings available for the specified plugin | ||||||
|  |         plugin = registry.get_plugin(plugin_slug) | ||||||
|  |  | ||||||
|  |         if plugin is None: | ||||||
|  |             raise NotFound(detail=f"Plugin '{plugin_slug}' not found") | ||||||
|  |  | ||||||
|  |         settings = getattr(plugin, 'SETTINGS', {}) | ||||||
|  |  | ||||||
|  |         if key not in settings: | ||||||
|  |             raise NotFound(detail=f"Plugin '{plugin_slug}' has no setting matching '{key}'") | ||||||
|  |          | ||||||
|  |         return PluginSetting.get_setting_object(key, plugin=plugin) | ||||||
|  |  | ||||||
|     # Staff permission required |     # Staff permission required | ||||||
|     permission_classes = [ |     permission_classes = [ | ||||||
|         GlobalSettingsPermissions, |         GlobalSettingsPermissions, | ||||||
| @@ -130,7 +160,7 @@ plugin_api_urls = [ | |||||||
|  |  | ||||||
|     # Plugin settings URLs |     # Plugin settings URLs | ||||||
|     re_path(r'^settings/', include([ |     re_path(r'^settings/', include([ | ||||||
|         re_path(r'^(?P<pk>\d+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail'), |         re_path(r'^(?P<plugin>\w+)/(?P<key>\w+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail'), | ||||||
|         re_path(r'^.*$', PluginSettingList.as_view(), name='api-plugin-setting-list'), |         re_path(r'^.*$', PluginSettingList.as_view(), name='api-plugin-setting-list'), | ||||||
|     ])), |     ])), | ||||||
|  |  | ||||||
|   | |||||||
| @@ -63,6 +63,17 @@ class PluginsRegistry: | |||||||
|         # mixins |         # mixins | ||||||
|         self.mixins_settings = {} |         self.mixins_settings = {} | ||||||
|  |  | ||||||
|  |     def get_plugin(self, slug): | ||||||
|  |         """ | ||||||
|  |         Lookup plugin by slug (unique key). | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         if slug not in self.plugins: | ||||||
|  |             logger.warning(f"Plugin registry has no record of plugin '{slug}'") | ||||||
|  |             return None | ||||||
|  |          | ||||||
|  |         return self.plugins[slug] | ||||||
|  |  | ||||||
|     def call_plugin_function(self, slug, func, *args, **kwargs): |     def call_plugin_function(self, slug, func, *args, **kwargs): | ||||||
|         """ |         """ | ||||||
|         Call a member function (named by 'func') of the plugin named by 'slug'. |         Call a member function (named by 'func') of the plugin named by 'slug'. | ||||||
| @@ -73,7 +84,15 @@ class PluginsRegistry: | |||||||
|         Instead, any error messages are returned to the worker. |         Instead, any error messages are returned to the worker. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         plugin = self.plugins[slug] |         plugin = self.get_plugin(slug) | ||||||
|  |  | ||||||
|  |         if not plugin: | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         # Check that the plugin is enabled | ||||||
|  |         config = plugin.plugin_config() | ||||||
|  |  | ||||||
|  |         if config and config.active: | ||||||
|  |  | ||||||
|             plugin_func = getattr(plugin, func) |             plugin_func = getattr(plugin, func) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ | |||||||
|     <td> |     <td> | ||||||
|         {% if setting.is_bool %} |         {% if setting.is_bool %} | ||||||
|         <div class='form-check form-switch'> |         <div class='form-check form-switch'> | ||||||
|             <input class='form-check-input boolean-setting' fieldname='{{ setting.key.upper }}' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' id='setting-value-{{ setting.key.upper }}' type='checkbox' {% if setting.as_bool %}checked=''{% endif %} {% if plugin %}plugin='{{ plugin.pk }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}{% if notification_setting %}notification='{{request.user.id}}'{% endif %}> |             <input class='form-check-input boolean-setting' fieldname='{{ setting.key.upper }}' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' id='setting-value-{{ setting.key.upper }}' type='checkbox' {% if setting.as_bool %}checked=''{% endif %} {% if plugin %}plugin='{{ plugin.slug }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}{% if notification_setting %}notification='{{request.user.id}}'{% endif %}> | ||||||
|         </div> |         </div> | ||||||
|         {% else %} |         {% else %} | ||||||
|         <div id='setting-{{ setting.pk }}'> |         <div id='setting-{{ setting.pk }}'> | ||||||
| @@ -41,7 +41,7 @@ | |||||||
|             </span> |             </span> | ||||||
|             {{ setting.units }} |             {{ setting.units }} | ||||||
|             <div class='btn-group float-right'> |             <div class='btn-group float-right'> | ||||||
|                 <button class='btn btn-outline-secondary btn-small btn-edit-setting' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' title='{% trans "Edit setting" %}' {% if plugin %}plugin='{{ plugin.pk }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}> |                 <button class='btn btn-outline-secondary btn-small btn-edit-setting' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' title='{% trans "Edit setting" %}' {% if plugin %}plugin='{{ plugin.slug }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}> | ||||||
|                     <span class='fas fa-edit icon-green'></span> |                     <span class='fas fa-edit icon-green'></span> | ||||||
|                 </button> |                 </button> | ||||||
|             </div> |             </div> | ||||||
|   | |||||||
| @@ -67,7 +67,6 @@ | |||||||
| $('table').find('.boolean-setting').change(function() { | $('table').find('.boolean-setting').change(function() { | ||||||
|  |  | ||||||
|     var setting = $(this).attr('setting'); |     var setting = $(this).attr('setting'); | ||||||
|     var pk = $(this).attr('pk'); |  | ||||||
|     var plugin = $(this).attr('plugin'); |     var plugin = $(this).attr('plugin'); | ||||||
|     var user = $(this).attr('user'); |     var user = $(this).attr('user'); | ||||||
|     var notification = $(this).attr('notification'); |     var notification = $(this).attr('notification'); | ||||||
| @@ -78,7 +77,7 @@ $('table').find('.boolean-setting').change(function() { | |||||||
|     var url = `/api/settings/global/${setting}/`; |     var url = `/api/settings/global/${setting}/`; | ||||||
|  |  | ||||||
|     if (plugin) { |     if (plugin) { | ||||||
|         url = `/api/plugin/settings/${pk}/`; |         url = `/api/plugin/settings/${plugin}/${setting}/`; | ||||||
|     } else if (user) { |     } else if (user) { | ||||||
|         url = `/api/settings/user/${setting}/`; |         url = `/api/settings/user/${setting}/`; | ||||||
|     } else if (notification) { |     } else if (notification) { | ||||||
| @@ -105,7 +104,6 @@ $('table').find('.boolean-setting').change(function() { | |||||||
| // Callback for when non-boolean settings are edited | // Callback for when non-boolean settings are edited | ||||||
| $('table').find('.btn-edit-setting').click(function() { | $('table').find('.btn-edit-setting').click(function() { | ||||||
|     var setting = $(this).attr('setting'); |     var setting = $(this).attr('setting'); | ||||||
|     var pk = $(this).attr('pk'); |  | ||||||
|     var plugin = $(this).attr('plugin'); |     var plugin = $(this).attr('plugin'); | ||||||
|     var is_global = true; |     var is_global = true; | ||||||
|     var notification = $(this).attr('notification'); |     var notification = $(this).attr('notification'); | ||||||
| @@ -122,13 +120,11 @@ $('table').find('.btn-edit-setting').click(function() { | |||||||
|         title = '{% trans "Edit Notification Setting" %}'; |         title = '{% trans "Edit Notification Setting" %}'; | ||||||
|     } else if (is_global) { |     } else if (is_global) { | ||||||
|         title = '{% trans "Edit Global Setting" %}'; |         title = '{% trans "Edit Global Setting" %}'; | ||||||
|         pk = setting; |  | ||||||
|     } else { |     } else { | ||||||
|         title = '{% trans "Edit User Setting" %}'; |         title = '{% trans "Edit User Setting" %}'; | ||||||
|         pk = setting; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     editSetting(pk, { |     editSetting(setting, { | ||||||
|         plugin: plugin, |         plugin: plugin, | ||||||
|         global: is_global, |         global: is_global, | ||||||
|         notification: notification, |         notification: notification, | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ const plugins_enabled = false; | |||||||
|  * Interactively edit a setting value. |  * Interactively edit a setting value. | ||||||
|  * Launches a modal dialog form to adjut the value of the setting. |  * Launches a modal dialog form to adjut the value of the setting. | ||||||
|  */ |  */ | ||||||
| function editSetting(pk, options={}) { | function editSetting(key, options={}) { | ||||||
|  |  | ||||||
|     // Is this a global setting or a user setting? |     // Is this a global setting or a user setting? | ||||||
|     var global = options.global || false; |     var global = options.global || false; | ||||||
| @@ -44,13 +44,13 @@ function editSetting(pk, options={}) { | |||||||
|     var url = ''; |     var url = ''; | ||||||
|  |  | ||||||
|     if (plugin) { |     if (plugin) { | ||||||
|         url = `/api/plugin/settings/${pk}/`; |         url = `/api/plugin/settings/${plugin}/${key}/`; | ||||||
|     } else if (notification) { |     } else if (notification) { | ||||||
|         url = `/api/settings/notification/${pk}/`; |         url = `/api/settings/notification/${pk}/`; | ||||||
|     } else if (global) { |     } else if (global) { | ||||||
|         url = `/api/settings/global/${pk}/`; |         url = `/api/settings/global/${key}/`; | ||||||
|     } else { |     } else { | ||||||
|         url = `/api/settings/user/${pk}/`; |         url = `/api/settings/user/${key}/`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     var reload_required = false; |     var reload_required = false; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user