mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 07:05:41 +00:00 
			
		
		
		
	refactor plugin loading
This commit is contained in:
		@@ -262,7 +262,7 @@ INSTALLED_APPS = [
 | 
				
			|||||||
    'report.apps.ReportConfig',
 | 
					    'report.apps.ReportConfig',
 | 
				
			||||||
    'stock.apps.StockConfig',
 | 
					    'stock.apps.StockConfig',
 | 
				
			||||||
    'users.apps.UsersConfig',
 | 
					    'users.apps.UsersConfig',
 | 
				
			||||||
    'plugin.apps.PluginConfig',
 | 
					    'plugin.apps.PluginAppConfig',
 | 
				
			||||||
    'InvenTree.apps.InvenTreeConfig',       # InvenTree app runs last
 | 
					    'InvenTree.apps.InvenTreeConfig',       # InvenTree app runs last
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Third part add-ons
 | 
					    # Third part add-ons
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,55 +20,23 @@ from plugin.integration import IntegrationPluginBase
 | 
				
			|||||||
logger = logging.getLogger('inventree')
 | 
					logger = logging.getLogger('inventree')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PluginConfig(AppConfig):
 | 
					class PluginAppConfig(AppConfig):
 | 
				
			||||||
    name = 'plugin'
 | 
					    name = 'plugin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ready(self):
 | 
					    def ready(self):
 | 
				
			||||||
        from common.models import InvenTreeSetting
 | 
					        self.collect_plugins()
 | 
				
			||||||
        from plugin.models import PluginConfig
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Collect plugins from paths
 | 
					 | 
				
			||||||
        for plugin in settings.PLUGIN_DIRS:
 | 
					 | 
				
			||||||
            modules = inventree_plugins.get_plugins(importlib.import_module(plugin), IntegrationPluginBase, True)
 | 
					 | 
				
			||||||
            if modules:
 | 
					 | 
				
			||||||
                [settings.PLUGINS.append(item) for item in modules]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Collect plugins from setup entry points
 | 
					 | 
				
			||||||
        for entry in metadata.entry_points().get('inventree_plugins', []):
 | 
					 | 
				
			||||||
            plugin = entry.load()
 | 
					 | 
				
			||||||
            plugin.is_package = True
 | 
					 | 
				
			||||||
            settings.PLUGINS.append(plugin)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Log found plugins
 | 
					 | 
				
			||||||
        logger.info(f'Found {len(settings.PLUGINS)} plugins!')
 | 
					 | 
				
			||||||
        logger.info(", ".join([a.__module__ for a in settings.PLUGINS]))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            logger.info('Starting plugin initialisation')
 | 
					            # we are using the db from here - so for migrations etc we need to try this block
 | 
				
			||||||
            # Initialize integration plugins
 | 
					            self.init_plugins()
 | 
				
			||||||
            for plugin in inventree_plugins.load_integration_plugins():
 | 
					            self.activate_plugins()
 | 
				
			||||||
                # check if package
 | 
					        except (OperationalError, ProgrammingError):
 | 
				
			||||||
                was_packaged = getattr(plugin, 'is_package', False)
 | 
					            # Exception if the database has not been migrated yet
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # check if activated
 | 
					    def activate_plugins(self):
 | 
				
			||||||
                # these checks only use attributes - never use plugin supplied functions -> that would lead to arbitrary code execution!!
 | 
					        """fullfill integrations for all activated plugins"""
 | 
				
			||||||
                plug_name = plugin.PLUGIN_NAME
 | 
					        from common.models import InvenTreeSetting
 | 
				
			||||||
                plug_key = plugin.PLUGIN_SLUG if getattr(plugin, 'PLUGIN_SLUG', None) else plug_name
 | 
					 | 
				
			||||||
                plugin_db_setting, _ = PluginConfig.objects.get_or_create(key=plug_key, name=plug_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if plugin_db_setting.active:
 | 
					 | 
				
			||||||
                    # init package
 | 
					 | 
				
			||||||
                    # now we can be sure that an admin has activated the plugin -> as of Nov 2021 there are not many checks in place
 | 
					 | 
				
			||||||
                    # but we could enhance those to check signatures, run the plugin against a whitelist etc.
 | 
					 | 
				
			||||||
                    logger.info(f'Loading integration plugin {plugin.PLUGIN_NAME}')
 | 
					 | 
				
			||||||
                    plugin = plugin()
 | 
					 | 
				
			||||||
                    logger.info(f'Loaded integration plugin {plugin.slug}')
 | 
					 | 
				
			||||||
                    plugin.is_package = was_packaged
 | 
					 | 
				
			||||||
                    # safe reference
 | 
					 | 
				
			||||||
                    settings.INTEGRATION_PLUGINS[plugin.slug] = plugin
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    # save for later reference
 | 
					 | 
				
			||||||
                    settings.INTEGRATION_PLUGINS_INACTIVE[plug_key] = plugin_db_setting
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # activate integrations
 | 
					        # activate integrations
 | 
				
			||||||
        plugins = settings.INTEGRATION_PLUGINS.items()
 | 
					        plugins = settings.INTEGRATION_PLUGINS.items()
 | 
				
			||||||
@@ -111,6 +79,51 @@ class PluginConfig(AppConfig):
 | 
				
			|||||||
                apps.apps_ready = apps.models_ready = apps.loading = apps.ready = False
 | 
					                apps.apps_ready = apps.models_ready = apps.loading = apps.ready = False
 | 
				
			||||||
                apps.clear_cache()
 | 
					                apps.clear_cache()
 | 
				
			||||||
                apps.populate(settings.INSTALLED_APPS)
 | 
					                apps.populate(settings.INSTALLED_APPS)
 | 
				
			||||||
        except (OperationalError, ProgrammingError):
 | 
					
 | 
				
			||||||
            # Exception if the database has not been migrated yet
 | 
					    def init_plugins(self):
 | 
				
			||||||
            pass
 | 
					        """initialise all found plugins"""
 | 
				
			||||||
 | 
					        from plugin.models import PluginConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logger.info('Starting plugin initialisation')
 | 
				
			||||||
 | 
					            # Initialize integration plugins
 | 
				
			||||||
 | 
					        for plugin in inventree_plugins.load_integration_plugins():
 | 
				
			||||||
 | 
					                # check if package
 | 
				
			||||||
 | 
					            was_packaged = getattr(plugin, 'is_package', False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # check if activated
 | 
				
			||||||
 | 
					                # these checks only use attributes - never use plugin supplied functions -> that would lead to arbitrary code execution!!
 | 
				
			||||||
 | 
					            plug_name = plugin.PLUGIN_NAME
 | 
				
			||||||
 | 
					            plug_key = plugin.PLUGIN_SLUG if getattr(plugin, 'PLUGIN_SLUG', None) else plug_name
 | 
				
			||||||
 | 
					            plugin_db_setting, _ = PluginConfig.objects.get_or_create(key=plug_key, name=plug_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if plugin_db_setting.active:
 | 
				
			||||||
 | 
					                    # init package
 | 
				
			||||||
 | 
					                    # now we can be sure that an admin has activated the plugin -> as of Nov 2021 there are not many checks in place
 | 
				
			||||||
 | 
					                    # but we could enhance those to check signatures, run the plugin against a whitelist etc.
 | 
				
			||||||
 | 
					                logger.info(f'Loading integration plugin {plugin.PLUGIN_NAME}')
 | 
				
			||||||
 | 
					                plugin = plugin()
 | 
				
			||||||
 | 
					                logger.info(f'Loaded integration plugin {plugin.slug}')
 | 
				
			||||||
 | 
					                plugin.is_package = was_packaged
 | 
				
			||||||
 | 
					                    # safe reference
 | 
				
			||||||
 | 
					                settings.INTEGRATION_PLUGINS[plugin.slug] = plugin
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                    # save for later reference
 | 
				
			||||||
 | 
					                settings.INTEGRATION_PLUGINS_INACTIVE[plug_key] = plugin_db_setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def collect_plugins(self):
 | 
				
			||||||
 | 
					        """collect integration plugins from all possible ways of loading"""
 | 
				
			||||||
 | 
					        # Collect plugins from paths
 | 
				
			||||||
 | 
					        for plugin in settings.PLUGIN_DIRS:
 | 
				
			||||||
 | 
					            modules = inventree_plugins.get_plugins(importlib.import_module(plugin), IntegrationPluginBase, True)
 | 
				
			||||||
 | 
					            if modules:
 | 
				
			||||||
 | 
					                [settings.PLUGINS.append(item) for item in modules]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Collect plugins from setup entry points
 | 
				
			||||||
 | 
					        for entry in metadata.entry_points().get('inventree_plugins', []):
 | 
				
			||||||
 | 
					            plugin = entry.load()
 | 
				
			||||||
 | 
					            plugin.is_package = True
 | 
				
			||||||
 | 
					            settings.PLUGINS.append(plugin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Log found plugins
 | 
				
			||||||
 | 
					        logger.info(f'Found {len(settings.PLUGINS)} plugins!')
 | 
				
			||||||
 | 
					        logger.info(", ".join([a.__module__ for a in settings.PLUGINS]))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user