mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Adds API endpoints for viewing and updating plugin settings
A lot of code updates / refactoring here to get this to work as expected
This commit is contained in:
		| @@ -12,11 +12,15 @@ import common.models | |||||||
| INVENTREE_SW_VERSION = "0.6.0 dev" | INVENTREE_SW_VERSION = "0.6.0 dev" | ||||||
|  |  | ||||||
| # InvenTree API version | # InvenTree API version | ||||||
| INVENTREE_API_VERSION = 22 | INVENTREE_API_VERSION = 23 | ||||||
|  |  | ||||||
| """ | """ | ||||||
| Increment this API version number whenever there is a significant change to the API that any clients need to know about | Increment this API version number whenever there is a significant change to the API that any clients need to know about | ||||||
|  |  | ||||||
|  | v23 -> 2022-02-02 | ||||||
|  |     - Adds API endpoints for managing plugin classes | ||||||
|  |     - Adds API endpoints for managing plugin settings | ||||||
|  |  | ||||||
| v22 -> 2021-12-20 | v22 -> 2021-12-20 | ||||||
|     - Adds API endpoint to "merge" multiple stock items |     - Adds API endpoint to "merge" multiple stock items | ||||||
|  |  | ||||||
|   | |||||||
| @@ -71,7 +71,7 @@ class BaseInvenTreeSetting(models.Model): | |||||||
|         super().save() |         super().save() | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def allValues(cls, user=None, plugin=None, exclude_hidden=False): |     def allValues(cls, user=None, exclude_hidden=False): | ||||||
|         """ |         """ | ||||||
|         Return a dict of "all" defined global settings. |         Return a dict of "all" defined global settings. | ||||||
|  |  | ||||||
| @@ -86,10 +86,6 @@ class BaseInvenTreeSetting(models.Model): | |||||||
|         if user is not None: |         if user is not None: | ||||||
|             results = results.filter(user=user) |             results = results.filter(user=user) | ||||||
|  |  | ||||||
|         # Optionally filter by plugin |  | ||||||
|         if plugin is not None: |  | ||||||
|             results = results.filter(plugin=plugin) |  | ||||||
|  |  | ||||||
|         # Query the database |         # Query the database | ||||||
|         settings = {} |         settings = {} | ||||||
|  |  | ||||||
| @@ -238,16 +234,12 @@ class BaseInvenTreeSetting(models.Model): | |||||||
|  |  | ||||||
|         settings = cls.objects.all() |         settings = cls.objects.all() | ||||||
|  |  | ||||||
|  |         # Filter by user | ||||||
|         user = kwargs.get('user', None) |         user = kwargs.get('user', None) | ||||||
|  |  | ||||||
|         if user is not None: |         if user is not None: | ||||||
|             settings = settings.filter(user=user) |             settings = settings.filter(user=user) | ||||||
|  |  | ||||||
|         plugin = kwargs.get('plugin', None) |  | ||||||
|  |  | ||||||
|         if plugin is not None: |  | ||||||
|             settings = settings.filter(plugin=plugin) |  | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             setting = settings.filter(**cls.get_filters(key, **kwargs)).first() |             setting = settings.filter(**cls.get_filters(key, **kwargs)).first() | ||||||
|         except (ValueError, cls.DoesNotExist): |         except (ValueError, cls.DoesNotExist): | ||||||
| @@ -255,6 +247,16 @@ class BaseInvenTreeSetting(models.Model): | |||||||
|         except (IntegrityError, OperationalError): |         except (IntegrityError, OperationalError): | ||||||
|             setting = None |             setting = None | ||||||
|  |  | ||||||
|  |         plugin = kwargs.pop('plugin', None) | ||||||
|  |  | ||||||
|  |         if plugin: | ||||||
|  |             from plugin import InvenTreePlugin | ||||||
|  |  | ||||||
|  |             if issubclass(plugin.__class__, InvenTreePlugin): | ||||||
|  |                 plugin = plugin.plugin_config() | ||||||
|  |  | ||||||
|  |             kwargs['plugin'] = plugin | ||||||
|  |  | ||||||
|         # Setting does not exist! (Try to create it) |         # Setting does not exist! (Try to create it) | ||||||
|         if not setting: |         if not setting: | ||||||
|  |  | ||||||
| @@ -554,7 +556,9 @@ class BaseInvenTreeSetting(models.Model): | |||||||
|  |  | ||||||
|  |  | ||||||
| def settings_group_options(): | def settings_group_options(): | ||||||
|     """build up group tuple for settings based on gour choices""" |     """ | ||||||
|  |     Build up group tuple for settings based on your choices | ||||||
|  |     """ | ||||||
|     return [('', _('No group')), *[(str(a.id), str(a)) for a in Group.objects.all()]] |     return [('', _('No group')), *[(str(a.id), str(a)) for a in Group.objects.all()]] | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
| """ This module provides template tags for extra functionality | """ | ||||||
|  | This module provides template tags for extra functionality, | ||||||
| over and above the built-in Django tags. | over and above the built-in Django tags. | ||||||
| """ | """ | ||||||
|  |  | ||||||
| @@ -22,6 +23,8 @@ import InvenTree.helpers | |||||||
| from common.models import InvenTreeSetting, ColorTheme, InvenTreeUserSetting | from common.models import InvenTreeSetting, ColorTheme, InvenTreeUserSetting | ||||||
| from common.settings import currency_code_default | from common.settings import currency_code_default | ||||||
|  |  | ||||||
|  | from plugin.models import PluginSetting | ||||||
|  |  | ||||||
| register = template.Library() | register = template.Library() | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -223,8 +226,16 @@ def setting_object(key, *args, **kwargs): | |||||||
|     if a user-setting was requested return that |     if a user-setting was requested return that | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|  |     if 'plugin' in kwargs: | ||||||
|  |         # Note, 'plugin' is an instance of an InvenTreePlugin class | ||||||
|  |  | ||||||
|  |         plugin = kwargs['plugin'] | ||||||
|  |  | ||||||
|  |         return PluginSetting.get_setting_object(key, plugin=plugin) | ||||||
|  |  | ||||||
|     if 'user' in kwargs: |     if 'user' in kwargs: | ||||||
|         return InvenTreeUserSetting.get_setting_object(key, user=kwargs['user']) |         return InvenTreeUserSetting.get_setting_object(key, user=kwargs['user']) | ||||||
|  |  | ||||||
|     return InvenTreeSetting.get_setting_object(key) |     return InvenTreeSetting.get_setting_object(key) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
| from .registry import plugins as plugin_reg | from .registry import plugin_registry | ||||||
|  | from .plugin import InvenTreePlugin | ||||||
| from .integration import IntegrationPluginBase | from .integration import IntegrationPluginBase | ||||||
| from .action import ActionPlugin | from .action import ActionPlugin | ||||||
|  |  | ||||||
| __all__ = [ | __all__ = [ | ||||||
|     'plugin_reg', 'IntegrationPluginBase', 'ActionPlugin', |     'ActionPlugin', | ||||||
|  |     'IntegrationPluginBase', | ||||||
|  |     'InvenTreePlugin', | ||||||
|  |     'plugin_registry', | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ from django.contrib import admin | |||||||
| import plugin.models as models | import plugin.models as models | ||||||
| import plugin.registry as registry | import plugin.registry as registry | ||||||
|  |  | ||||||
|  |  | ||||||
| def plugin_update(queryset, new_status: bool): | def plugin_update(queryset, new_status: bool): | ||||||
|     """general function for bulk changing plugins""" |     """general function for bulk changing plugins""" | ||||||
|     apps_changed = False |     apps_changed = False | ||||||
|   | |||||||
| @@ -11,7 +11,8 @@ from rest_framework import generics | |||||||
| from rest_framework import status | from rest_framework import status | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
|  |  | ||||||
| from plugin.models import PluginConfig | from common.api import GlobalSettingsPermissions | ||||||
|  | from plugin.models import PluginConfig, PluginSetting | ||||||
| import plugin.serializers as PluginSerializers | import plugin.serializers as PluginSerializers | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -76,7 +77,46 @@ class PluginInstall(generics.CreateAPIView): | |||||||
|         return serializer.save() |         return serializer.save() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PluginSettingList(generics.ListAPIView): | ||||||
|  |     """ | ||||||
|  |     List endpoint for all plugin related settings. | ||||||
|  |  | ||||||
|  |     - read only | ||||||
|  |     - only accessible by staff users | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     queryset = PluginSetting.objects.all() | ||||||
|  |     serializer_class = PluginSerializers.PluginSettingSerializer | ||||||
|  |  | ||||||
|  |     permission_classes = [ | ||||||
|  |         GlobalSettingsPermissions, | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PluginSettingDetail(generics.RetrieveUpdateAPIView): | ||||||
|  |     """ | ||||||
|  |     Detail endpoint for a plugin-specific setting. | ||||||
|  |  | ||||||
|  |     Note that these cannot be created or deleted via the API | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     queryset = PluginSetting.objects.all() | ||||||
|  |     serializer_class = PluginSerializers.PluginSettingSerializer | ||||||
|  |  | ||||||
|  |     # Staff permission required | ||||||
|  |     permission_classes = [ | ||||||
|  |         GlobalSettingsPermissions, | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
| plugin_api_urls = [ | plugin_api_urls = [ | ||||||
|  |  | ||||||
|  |     # Plugin settings URLs | ||||||
|  |     url(r'^settings/', include([ | ||||||
|  |         url(r'^(?P<pk>\d+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail'), | ||||||
|  |         url(r'^.*$', PluginSettingList.as_view(), name='api-plugin-setting-list'), | ||||||
|  |     ])), | ||||||
|  |  | ||||||
|     # Detail views for a single PluginConfig item |     # Detail views for a single PluginConfig item | ||||||
|     url(r'^(?P<pk>\d+)/', include([ |     url(r'^(?P<pk>\d+)/', include([ | ||||||
|         url(r'^.*$', PluginDetail.as_view(), name='api-plugin-detail'), |         url(r'^.*$', PluginDetail.as_view(), name='api-plugin-detail'), | ||||||
|   | |||||||
| @@ -4,17 +4,17 @@ from __future__ import unicode_literals | |||||||
| from django.apps import AppConfig | from django.apps import AppConfig | ||||||
| from maintenance_mode.core import set_maintenance_mode | from maintenance_mode.core import set_maintenance_mode | ||||||
|  |  | ||||||
| from plugin.registry import plugins | from plugin import plugin_registry | ||||||
|  |  | ||||||
|  |  | ||||||
| class PluginAppConfig(AppConfig): | class PluginAppConfig(AppConfig): | ||||||
|     name = 'plugin' |     name = 'plugin' | ||||||
|  |  | ||||||
|     def ready(self): |     def ready(self): | ||||||
|         if not plugins.is_loading: |         if not plugin_registry.is_loading: | ||||||
|             # this is the first startup |             # this is the first startup | ||||||
|             plugins.collect_plugins() |             plugin_registry.collect_plugins() | ||||||
|             plugins.load_plugins() |             plugin_registry.load_plugins() | ||||||
|  |  | ||||||
|             # drop out of maintenance |             # drop out of maintenance | ||||||
|             # makes sure we did not have an error in reloading and maintenance is still active |             # makes sure we did not have an error in reloading and maintenance is still active | ||||||
|   | |||||||
| @@ -36,17 +36,14 @@ class SettingsMixin: | |||||||
|  |  | ||||||
|         # Find the plugin configuration associated with this plugin |         # Find the plugin configuration associated with this plugin | ||||||
|  |  | ||||||
|         try: |         plugin = self.plugin_config() | ||||||
|             plugin, _ = PluginConfig.objects.get_or_create(key=self.plugin_slug(), name=self.plugin_name()) |  | ||||||
|         except (OperationalError, ProgrammingError) as error: |  | ||||||
|             plugin = None |  | ||||||
|  |  | ||||||
|         if not plugin: |         if plugin: | ||||||
|  |             return PluginSetting.get_setting(key, plugin=plugin, settings=self.settings) | ||||||
|  |         else: | ||||||
|             # Plugin cannot be found, return default value |             # Plugin cannot be found, return default value | ||||||
|             return PluginSetting.get_setting_default(key, settings=self.settings) |             return PluginSetting.get_setting_default(key, settings=self.settings) | ||||||
|  |  | ||||||
|         return PluginSetting.get_setting(key, plugin=plugin, settings=self.settings) |  | ||||||
|  |  | ||||||
|     def set_setting(self, key, value, user): |     def set_setting(self, key, value, user): | ||||||
|         """ |         """ | ||||||
|         Set plugin setting value by key |         Set plugin setting value by key | ||||||
| @@ -54,7 +51,7 @@ class SettingsMixin: | |||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             plugin, _ = PluginConfig.objects.get_or_create(key=self.plugin_slug(), name=self.plugin_name()) |             plugin, _ = PluginConfig.objects.get_or_create(key=self.plugin_slug(), name=self.plugin_name()) | ||||||
|         except (OperationalError, ProgrammingError) as error: |         except (OperationalError, ProgrammingError): | ||||||
|             plugin = None |             plugin = None | ||||||
|  |  | ||||||
|         if not plugin: |         if not plugin: | ||||||
|   | |||||||
| @@ -10,14 +10,14 @@ from django.conf import settings | |||||||
|  |  | ||||||
| # region logging / errors | # region logging / errors | ||||||
| def log_plugin_error(error, reference: str = 'general'): | def log_plugin_error(error, reference: str = 'general'): | ||||||
|     from plugin import plugin_reg |     from plugin import plugin_registry | ||||||
|  |  | ||||||
|     # make sure the registry is set up |     # make sure the registry is set up | ||||||
|     if reference not in plugin_reg.errors: |     if reference not in plugin_registry.errors: | ||||||
|         plugin_reg.errors[reference] = [] |         plugin_registry.errors[reference] = [] | ||||||
|  |  | ||||||
|     # add error to stack |     # add error to stack | ||||||
|     plugin_reg.errors[reference].append(error) |     plugin_registry.errors[reference].append(error) | ||||||
|  |  | ||||||
|  |  | ||||||
| class IntegrationPluginError(Exception): | class IntegrationPluginError(Exception): | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import pathlib | |||||||
|  |  | ||||||
| from django.urls.base import reverse | from django.urls.base import reverse | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.utils.text import slugify |  | ||||||
| from django.utils.translation import ugettext_lazy as _ | from django.utils.translation import ugettext_lazy as _ | ||||||
|  |  | ||||||
| import plugin.plugin as plugin | import plugin.plugin as plugin | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ load templates for loaded plugins | |||||||
| from django.template.loaders.filesystem import Loader as FilesystemLoader | from django.template.loaders.filesystem import Loader as FilesystemLoader | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  |  | ||||||
| from plugin import plugin_reg | from plugin import plugin_registry | ||||||
|  |  | ||||||
|  |  | ||||||
| class PluginTemplateLoader(FilesystemLoader): | class PluginTemplateLoader(FilesystemLoader): | ||||||
| @@ -12,7 +12,7 @@ class PluginTemplateLoader(FilesystemLoader): | |||||||
|     def get_dirs(self): |     def get_dirs(self): | ||||||
|         dirname = 'templates' |         dirname = 'templates' | ||||||
|         template_dirs = [] |         template_dirs = [] | ||||||
|         for plugin in plugin_reg.plugins.values(): |         for plugin in plugin_registry.plugins.values(): | ||||||
|             new_path = Path(plugin.path) / dirname |             new_path = Path(plugin.path) / dirname | ||||||
|             if Path(new_path).is_dir(): |             if Path(new_path).is_dir(): | ||||||
|                 template_dirs.append(new_path) |                 template_dirs.append(new_path) | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ from django.db import models | |||||||
|  |  | ||||||
| import common.models | import common.models | ||||||
|  |  | ||||||
| from plugin import plugin_reg | from plugin import InvenTreePlugin, plugin_registry | ||||||
|  |  | ||||||
|  |  | ||||||
| class PluginConfig(models.Model): | class PluginConfig(models.Model): | ||||||
| @@ -72,7 +72,7 @@ class PluginConfig(models.Model): | |||||||
|         self.__org_active = self.active |         self.__org_active = self.active | ||||||
|  |  | ||||||
|         # append settings from registry |         # append settings from registry | ||||||
|         self.plugin = plugin_reg.plugins.get(self.key, None) |         self.plugin = plugin_registry.plugins.get(self.key, None) | ||||||
|  |  | ||||||
|         def get_plugin_meta(name): |         def get_plugin_meta(name): | ||||||
|             if self.plugin: |             if self.plugin: | ||||||
| @@ -95,10 +95,10 @@ class PluginConfig(models.Model): | |||||||
|  |  | ||||||
|         if not reload: |         if not reload: | ||||||
|             if self.active is False and self.__org_active is True: |             if self.active is False and self.__org_active is True: | ||||||
|                 plugin_reg.reload_plugins() |                 plugin_registry.reload_plugins() | ||||||
|  |  | ||||||
|             elif self.active is True and self.__org_active is False: |             elif self.active is True and self.__org_active is False: | ||||||
|                 plugin_reg.reload_plugins() |                 plugin_registry.reload_plugins() | ||||||
|  |  | ||||||
|         return ret |         return ret | ||||||
|  |  | ||||||
| @@ -113,6 +113,58 @@ class PluginSetting(common.models.BaseInvenTreeSetting): | |||||||
|             ('plugin', 'key'), |             ('plugin', 'key'), | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |     """ | ||||||
|  |     We override the following class methods, | ||||||
|  |     so that we can pass the plugin instance | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def name(self): | ||||||
|  |         return self.__class__.get_setting_name(self.key, plugin=self.plugin) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def default_value(self): | ||||||
|  |         return self.__class__.get_setting_default(self.key, plugin=self.plugin) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def description(self): | ||||||
|  |         return self.__class__.get_setting_description(self.key, plugin=self.plugin) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def units(self): | ||||||
|  |         return self.__class__.get_setting_units(self.key, plugin=self.plugin) | ||||||
|  |  | ||||||
|  |     def choices(self): | ||||||
|  |         return self.__class__.get_setting_choices(self.key, plugin=self.plugin) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def get_setting_definition(cls, key, **kwargs): | ||||||
|  |         """ | ||||||
|  |         In the BaseInvenTreeSetting class, we have a class attribute named 'SETTINGS', | ||||||
|  |         which is a dict object that fully defines all the setting parameters. | ||||||
|  |  | ||||||
|  |         Here, unlike the BaseInvenTreeSetting, we do not know the definitions of all settings | ||||||
|  |         'ahead of time' (as they are defined externally in the plugins). | ||||||
|  |  | ||||||
|  |         Settings can be provided by the caller, as kwargs['settings']. | ||||||
|  |  | ||||||
|  |         If not provided, we'll look at the plugin registry to see what settings are available, | ||||||
|  |         (if the plugin is specified!) | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         if 'settings' not in kwargs: | ||||||
|  |  | ||||||
|  |             plugin = kwargs.pop('plugin', None) | ||||||
|  |  | ||||||
|  |             if plugin: | ||||||
|  |  | ||||||
|  |                 if issubclass(plugin.__class__, InvenTreePlugin): | ||||||
|  |                     plugin = plugin.plugin_config() | ||||||
|  |  | ||||||
|  |                 kwargs['settings'] = plugin_registry.mixins_settings.get(plugin.key, {}) | ||||||
|  |  | ||||||
|  |         return super().get_setting_definition(key, **kwargs) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def get_filters(cls, key, **kwargs): |     def get_filters(cls, key, **kwargs): | ||||||
|         """ |         """ | ||||||
| @@ -124,6 +176,8 @@ class PluginSetting(common.models.BaseInvenTreeSetting): | |||||||
|         plugin = kwargs.get('plugin', None) |         plugin = kwargs.get('plugin', None) | ||||||
|  |  | ||||||
|         if plugin: |         if plugin: | ||||||
|  |             if issubclass(plugin.__class__, InvenTreePlugin): | ||||||
|  |                 plugin = plugin.plugin_config() | ||||||
|             filters['plugin'] = plugin |             filters['plugin'] = plugin | ||||||
|  |  | ||||||
|         return filters |         return filters | ||||||
|   | |||||||
| @@ -1,7 +1,9 @@ | |||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| """Base Class for InvenTree plugins""" | """ | ||||||
|  | Base Class for InvenTree plugins | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | from django.db.utils import OperationalError, ProgrammingError | ||||||
| from django.utils.text import slugify | from django.utils.text import slugify | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -10,6 +12,9 @@ class InvenTreePlugin(): | |||||||
|     Base class for a plugin |     Base class for a plugin | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|     # Override the plugin name for each concrete plugin instance |     # Override the plugin name for each concrete plugin instance | ||||||
|     PLUGIN_NAME = '' |     PLUGIN_NAME = '' | ||||||
|  |  | ||||||
| @@ -36,5 +41,22 @@ class InvenTreePlugin(): | |||||||
|  |  | ||||||
|         return self.PLUGIN_TITLE |         return self.PLUGIN_TITLE | ||||||
|  |  | ||||||
|     def __init__(self): |     def plugin_config(self, raise_error=False): | ||||||
|         pass |         """ | ||||||
|  |         Return the PluginConfig object associated with this plugin | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             import plugin.models | ||||||
|  |  | ||||||
|  |             cfg, _ = plugin.models.PluginConfig.objects.get_or_create( | ||||||
|  |                 key=self.plugin_slug(), | ||||||
|  |                 name=self.plugin_name(), | ||||||
|  |             ) | ||||||
|  |         except (OperationalError, ProgrammingError) as error: | ||||||
|  |             cfg = None | ||||||
|  |  | ||||||
|  |             if raise_error: | ||||||
|  |                 raise error | ||||||
|  |  | ||||||
|  |         return cfg | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ from .helpers import get_plugin_error, IntegrationPluginError | |||||||
| logger = logging.getLogger('inventree') | logger = logging.getLogger('inventree') | ||||||
|  |  | ||||||
|  |  | ||||||
| class Plugins: | class PluginsRegistry: | ||||||
|     def __init__(self) -> None: |     def __init__(self) -> None: | ||||||
|         # plugin registry |         # plugin registry | ||||||
|         self.plugins = {} |         self.plugins = {} | ||||||
| @@ -225,7 +225,8 @@ class Plugins: | |||||||
|                 self.plugins_inactive[plug_key] = plugin_db_setting |                 self.plugins_inactive[plug_key] = plugin_db_setting | ||||||
|  |  | ||||||
|     def _activate_plugins(self, force_reload=False): |     def _activate_plugins(self, force_reload=False): | ||||||
|         """run integration functions for all plugins |         """ | ||||||
|  |         Run integration functions for all plugins | ||||||
|  |  | ||||||
|         :param force_reload: force reload base apps, defaults to False |         :param force_reload: force reload base apps, defaults to False | ||||||
|         :type force_reload: bool, optional |         :type force_reload: bool, optional | ||||||
| @@ -238,13 +239,13 @@ class Plugins: | |||||||
|         self.activate_integration_app(plugins, force_reload=force_reload) |         self.activate_integration_app(plugins, force_reload=force_reload) | ||||||
|  |  | ||||||
|     def _deactivate_plugins(self): |     def _deactivate_plugins(self): | ||||||
|         """run integration deactivation functions for all plugins""" |         """ | ||||||
|  |         Run integration deactivation functions for all plugins | ||||||
|  |         """ | ||||||
|         self.deactivate_integration_app() |         self.deactivate_integration_app() | ||||||
|         self.deactivate_integration_globalsettings() |         self.deactivate_integration_globalsettings() | ||||||
|     # endregion |     # endregion | ||||||
|  |  | ||||||
|     # region specific integrations |  | ||||||
|     # region integration_globalsettings |  | ||||||
|     def activate_integration_globalsettings(self, plugins): |     def activate_integration_globalsettings(self, plugins): | ||||||
|         from common.models import InvenTreeSetting |         from common.models import InvenTreeSetting | ||||||
|  |  | ||||||
| @@ -255,24 +256,16 @@ class Plugins: | |||||||
|                     plugin_setting = plugin.settings |                     plugin_setting = plugin.settings | ||||||
|                     self.mixins_settings[slug] = plugin_setting |                     self.mixins_settings[slug] = plugin_setting | ||||||
|  |  | ||||||
|                     # Add to settings dir |  | ||||||
|                     InvenTreeSetting.SETTINGS.update(plugin_setting) |  | ||||||
|  |  | ||||||
|     def deactivate_integration_globalsettings(self): |     def deactivate_integration_globalsettings(self): | ||||||
|         from common.models import InvenTreeSetting |  | ||||||
|  |  | ||||||
|         # collect all settings |         # collect all settings | ||||||
|         plugin_settings = {} |         plugin_settings = {} | ||||||
|  |  | ||||||
|         for _, plugin_setting in self.mixins_settings.items(): |         for _, plugin_setting in self.mixins_settings.items(): | ||||||
|             plugin_settings.update(plugin_setting) |             plugin_settings.update(plugin_setting) | ||||||
|  |  | ||||||
|         # remove settings |  | ||||||
|         for setting in plugin_settings: |  | ||||||
|             InvenTreeSetting.SETTINGS.pop(setting) |  | ||||||
|  |  | ||||||
|         # clear cache |         # clear cache | ||||||
|         self.mixins_Fsettings = {} |         self.mixins_Fsettings = {} | ||||||
|     # endregion |  | ||||||
|  |  | ||||||
|     # region integration_app |     # region integration_app | ||||||
|     def activate_integration_app(self, plugins, force_reload=False): |     def activate_integration_app(self, plugins, force_reload=False): | ||||||
| @@ -452,4 +445,4 @@ class Plugins: | |||||||
|     # endregion |     # endregion | ||||||
|  |  | ||||||
|  |  | ||||||
| plugins = Plugins() | plugin_registry = PluginsRegistry() | ||||||
|   | |||||||
| @@ -52,6 +52,18 @@ class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixi | |||||||
|             'name': _('Numerical'), |             'name': _('Numerical'), | ||||||
|             'description': _('A numerical setting'), |             'description': _('A numerical setting'), | ||||||
|             'validator': int, |             'validator': int, | ||||||
|  |             'default': 123, | ||||||
|  |         }, | ||||||
|  |         'CHOICE_SETTING': { | ||||||
|  |             'name': _("Choice Setting"), | ||||||
|  |             'description': _('A setting with multiple choices'), | ||||||
|  |             'choices': [ | ||||||
|  |                 ('A', 'Anaconda'), | ||||||
|  |                 ('B', 'Bat'), | ||||||
|  |                 ('C', 'Cat'), | ||||||
|  |                 ('D', 'Dog'), | ||||||
|  |             ], | ||||||
|  |             'default': 'A', | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,9 @@ from django.utils.translation import ugettext_lazy as _ | |||||||
|  |  | ||||||
| from rest_framework import serializers | from rest_framework import serializers | ||||||
|  |  | ||||||
| from plugin.models import PluginConfig | from common.serializers import SettingsSerializer | ||||||
|  |  | ||||||
|  | from plugin.models import PluginConfig, PluginSetting | ||||||
|  |  | ||||||
|  |  | ||||||
| class PluginConfigSerializer(serializers.ModelSerializer): | class PluginConfigSerializer(serializers.ModelSerializer): | ||||||
| @@ -117,3 +119,24 @@ class PluginConfigInstallSerializer(serializers.Serializer): | |||||||
|         # TODO |         # TODO | ||||||
|  |  | ||||||
|         return ret |         return ret | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PluginSettingSerializer(SettingsSerializer): | ||||||
|  |     """ | ||||||
|  |     Serializer for the PluginSetting model | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     plugin = serializers.PrimaryKeyRelatedField(read_only=True) | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         model = PluginSetting | ||||||
|  |         fields = [ | ||||||
|  |             'pk', | ||||||
|  |             'key', | ||||||
|  |             'value', | ||||||
|  |             'name', | ||||||
|  |             'description', | ||||||
|  |             'type', | ||||||
|  |             'choices', | ||||||
|  |             'plugin', | ||||||
|  |         ] | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ from django import template | |||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
|  |  | ||||||
| from common.models import InvenTreeSetting | from common.models import InvenTreeSetting | ||||||
| from plugin import plugin_reg | from plugin import plugin_registry | ||||||
|  |  | ||||||
|  |  | ||||||
| register = template.Library() | register = template.Library() | ||||||
| @@ -16,19 +16,19 @@ register = template.Library() | |||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| def plugin_list(*args, **kwargs): | def plugin_list(*args, **kwargs): | ||||||
|     """ Return a list of all installed integration plugins """ |     """ Return a list of all installed integration plugins """ | ||||||
|     return plugin_reg.plugins |     return plugin_registry.plugins | ||||||
|  |  | ||||||
|  |  | ||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| def inactive_plugin_list(*args, **kwargs): | def inactive_plugin_list(*args, **kwargs): | ||||||
|     """ Return a list of all inactive integration plugins """ |     """ Return a list of all inactive integration plugins """ | ||||||
|     return plugin_reg.plugins_inactive |     return plugin_registry.plugins_inactive | ||||||
|  |  | ||||||
|  |  | ||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| def plugin_settings(plugin, *args, **kwargs): | def plugin_settings(plugin, *args, **kwargs): | ||||||
|     """ Return a list of all custom settings for a plugin """ |     """ Return a list of all custom settings for a plugin """ | ||||||
|     return plugin_reg.mixins_settings.get(plugin) |     return plugin_registry.mixins_settings.get(plugin) | ||||||
|  |  | ||||||
|  |  | ||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| @@ -57,4 +57,4 @@ def safe_url(view_name, *args, **kwargs): | |||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| def plugin_errors(*args, **kwargs): | def plugin_errors(*args, **kwargs): | ||||||
|     """Return all plugin errors""" |     """Return all plugin errors""" | ||||||
|     return plugin_reg.errors |     return plugin_registry.errors | ||||||
|   | |||||||
| @@ -64,14 +64,14 @@ class PluginDetailAPITest(InvenTreeAPITestCase): | |||||||
|         Test the PluginConfig action commands |         Test the PluginConfig action commands | ||||||
|         """ |         """ | ||||||
|         from plugin.models import PluginConfig |         from plugin.models import PluginConfig | ||||||
|         from plugin import plugin_reg |         from plugin import plugin_registry | ||||||
|  |  | ||||||
|         url = reverse('admin:plugin_pluginconfig_changelist') |         url = reverse('admin:plugin_pluginconfig_changelist') | ||||||
|         fixtures = PluginConfig.objects.all() |         fixtures = PluginConfig.objects.all() | ||||||
|  |  | ||||||
|         # check if plugins were registered -> in some test setups the startup has no db access |         # check if plugins were registered -> in some test setups the startup has no db access | ||||||
|         if not fixtures: |         if not fixtures: | ||||||
|             plugin_reg.reload_plugins() |             plugin_registry.reload_plugins() | ||||||
|             fixtures = PluginConfig.objects.all() |             fixtures = PluginConfig.objects.all() | ||||||
|  |  | ||||||
|         print([str(a) for a in fixtures]) |         print([str(a) for a in fixtures]) | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ from plugin.samples.integration.sample import SampleIntegrationPlugin | |||||||
| from plugin.samples.integration.another_sample import WrongIntegrationPlugin, NoIntegrationPlugin | from plugin.samples.integration.another_sample import WrongIntegrationPlugin, NoIntegrationPlugin | ||||||
| # from plugin.plugins import load_action_plugins, load_barcode_plugins | # from plugin.plugins import load_action_plugins, load_barcode_plugins | ||||||
| import plugin.templatetags.plugin_extras as plugin_tags | import plugin.templatetags.plugin_extras as plugin_tags | ||||||
| from plugin import plugin_reg | from plugin import plugin_registry | ||||||
|  |  | ||||||
|  |  | ||||||
| class InvenTreePluginTests(TestCase): | class InvenTreePluginTests(TestCase): | ||||||
| @@ -57,17 +57,17 @@ class PluginTagTests(TestCase): | |||||||
|  |  | ||||||
|     def test_tag_plugin_list(self): |     def test_tag_plugin_list(self): | ||||||
|         """test that all plugins are listed""" |         """test that all plugins are listed""" | ||||||
|         self.assertEqual(plugin_tags.plugin_list(), plugin_reg.plugins) |         self.assertEqual(plugin_tags.plugin_list(), plugin_registry.plugins) | ||||||
|  |  | ||||||
|     def test_tag_incative_plugin_list(self): |     def test_tag_incative_plugin_list(self): | ||||||
|         """test that all inactive plugins are listed""" |         """test that all inactive plugins are listed""" | ||||||
|         self.assertEqual(plugin_tags.inactive_plugin_list(), plugin_reg.plugins_inactive) |         self.assertEqual(plugin_tags.inactive_plugin_list(), plugin_registry.plugins_inactive) | ||||||
|  |  | ||||||
|     def test_tag_plugin_settings(self): |     def test_tag_plugin_settings(self): | ||||||
|         """check all plugins are listed""" |         """check all plugins are listed""" | ||||||
|         self.assertEqual( |         self.assertEqual( | ||||||
|             plugin_tags.plugin_settings(self.sample), |             plugin_tags.plugin_settings(self.sample), | ||||||
|             plugin_reg.mixins_settings.get(self.sample) |             plugin_registry.mixins_settings.get(self.sample) | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def test_tag_mixin_enabled(self): |     def test_tag_mixin_enabled(self): | ||||||
| @@ -89,4 +89,4 @@ class PluginTagTests(TestCase): | |||||||
|  |  | ||||||
|     def test_tag_plugin_errors(self): |     def test_tag_plugin_errors(self): | ||||||
|         """test that all errors are listed""" |         """test that all errors are listed""" | ||||||
|         self.assertEqual(plugin_tags.plugin_errors(), plugin_reg.errors) |         self.assertEqual(plugin_tags.plugin_errors(), plugin_registry.errors) | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ URL lookup for plugin app | |||||||
| """ | """ | ||||||
| from django.conf.urls import url, include | from django.conf.urls import url, include | ||||||
|  |  | ||||||
| from plugin import plugin_reg | from plugin import plugin_registry | ||||||
|  |  | ||||||
|  |  | ||||||
| PLUGIN_BASE = 'plugin'  # Constant for links | PLUGIN_BASE = 'plugin'  # Constant for links | ||||||
| @@ -12,7 +12,7 @@ PLUGIN_BASE = 'plugin'  # Constant for links | |||||||
| def get_plugin_urls(): | def get_plugin_urls(): | ||||||
|     """returns a urlpattern that can be integrated into the global urls""" |     """returns a urlpattern that can be integrated into the global urls""" | ||||||
|     urls = [] |     urls = [] | ||||||
|     for plugin in plugin_reg.plugins.values(): |     for plugin in plugin_registry.plugins.values(): | ||||||
|         if plugin.mixin_enabled('urls'): |         if plugin.mixin_enabled('urls'): | ||||||
|             urls.append(plugin.urlpatterns) |             urls.append(plugin.urlpatterns) | ||||||
|     return url(f'^{PLUGIN_BASE}/', include((urls, 'plugin'))) |     return url(f'^{PLUGIN_BASE}/', include((urls, 'plugin'))) | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
| <table class='table table-striped table-condensed'> | <table class='table table-striped table-condensed'> | ||||||
|     <tbody> |     <tbody> | ||||||
|     {% for setting in plugin_settings %} |     {% for setting in plugin_settings %} | ||||||
|         {% include "InvenTree/settings/setting.html" with key=setting%} |         {% include "InvenTree/settings/setting.html" with key=setting plugin=plugin %} | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
|     </tbody> |     </tbody> | ||||||
| </table> | </table> | ||||||
| @@ -1,7 +1,9 @@ | |||||||
| {% load inventree_extras %} | {% load inventree_extras %} | ||||||
| {% load i18n %} | {% load i18n %} | ||||||
|  |  | ||||||
| {% if user_setting %} | {% if plugin %} | ||||||
|  | {% setting_object key plugin=plugin as setting %} | ||||||
|  | {% elif user_setting %} | ||||||
| {% setting_object key user=request.user as setting %} | {% setting_object key user=request.user as setting %} | ||||||
| {% else %} | {% else %} | ||||||
| {% setting_object key as setting %} | {% setting_object key as setting %} | ||||||
|   | |||||||
| @@ -28,9 +28,13 @@ function editSetting(pk, 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; | ||||||
|  |  | ||||||
|  |     var plugin = options.plugin; | ||||||
|  |  | ||||||
|     var url = ''; |     var url = ''; | ||||||
|  |  | ||||||
|     if (global) { |     if (plugin) { | ||||||
|  |         url = `/api/plugin/settings/${pk}/`; | ||||||
|  |     } else if (global) { | ||||||
|         url = `/api/settings/global/${pk}/`; |         url = `/api/settings/global/${pk}/`; | ||||||
|     } else { |     } else { | ||||||
|         url = `/api/settings/user/${pk}/`; |         url = `/api/settings/user/${pk}/`; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user