mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 07:05:41 +00:00 
			
		
		
		
	Remove plugins.txt support
This commit is contained in:
		@@ -402,5 +402,7 @@ The following [plugin](../extend/plugins.md) configuration options are available
 | 
				
			|||||||
| --- | --- | --- | --- |
 | 
					| --- | --- | --- | --- |
 | 
				
			||||||
| INVENTREE_PLUGINS_ENABLED | plugins_enabled | Enable plugin support | False |
 | 
					| INVENTREE_PLUGINS_ENABLED | plugins_enabled | Enable plugin support | False |
 | 
				
			||||||
| INVENTREE_PLUGIN_NOINSTALL | plugin_noinstall | Disable Plugin installation via API - only use plugins.txt file | False |
 | 
					| INVENTREE_PLUGIN_NOINSTALL | plugin_noinstall | Disable Plugin installation via API - only use plugins.txt file | False |
 | 
				
			||||||
| INVENTREE_PLUGIN_FILE | plugins_plugin_file | Location of plugin installation file | *Not specified* |
 | 
					 | 
				
			||||||
| INVENTREE_PLUGIN_DIR | plugins_plugin_dir | Location of external plugin directory | *Not specified* |
 | 
					| INVENTREE_PLUGIN_DIR | plugins_plugin_dir | Location of external plugin directory | *Not specified* |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					!!! info "Plugins Directory"
 | 
				
			||||||
 | 
					    If the `INVENTREE_PLUGIN_DIR` environment variable is not specified, the default location is the `plugins` directory within the InvenTree installation.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -283,37 +283,6 @@ def get_backup_dir(create=True):
 | 
				
			|||||||
    return bd
 | 
					    return bd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_plugin_file():
 | 
					 | 
				
			||||||
    """Returns the path of the InvenTree plugins specification file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Note: It will be created if it does not already exist!
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    # Check if the plugin.txt file (specifying required plugins) is specified
 | 
					 | 
				
			||||||
    plugin_file = get_setting('INVENTREE_PLUGIN_FILE', 'plugin_file')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not plugin_file:
 | 
					 | 
				
			||||||
        # If not specified, look in the same directory as the configuration file
 | 
					 | 
				
			||||||
        config_dir = get_config_file().parent
 | 
					 | 
				
			||||||
        plugin_file = config_dir.joinpath('plugins.txt')
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
        # Make sure we are using a modern Path object
 | 
					 | 
				
			||||||
        plugin_file = Path(plugin_file)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not plugin_file.exists():
 | 
					 | 
				
			||||||
        logger.warning(
 | 
					 | 
				
			||||||
            'Plugin configuration file does not exist - creating default file'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        logger.info("Creating plugin file at '%s'", plugin_file)
 | 
					 | 
				
			||||||
        ensure_dir(plugin_file.parent)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # If opening the file fails (no write permission, for example), then this will throw an error
 | 
					 | 
				
			||||||
        plugin_file.write_text(
 | 
					 | 
				
			||||||
            '# InvenTree Plugins (uses PIP framework to install)\n\n'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return plugin_file
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def get_plugin_dir():
 | 
					def get_plugin_dir():
 | 
				
			||||||
    """Returns the path of the custom plugins directory."""
 | 
					    """Returns the path of the custom plugins directory."""
 | 
				
			||||||
    return get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir')
 | 
					    return get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,8 +143,6 @@ PLUGINS_INSTALL_DISABLED = get_boolean_setting(
 | 
				
			|||||||
    'INVENTREE_PLUGIN_NOINSTALL', 'plugin_noinstall', False
 | 
					    'INVENTREE_PLUGIN_NOINSTALL', 'plugin_noinstall', False
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PLUGIN_FILE = config.get_plugin_file()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Plugin test settings
 | 
					# Plugin test settings
 | 
				
			||||||
PLUGIN_TESTING = get_setting(
 | 
					PLUGIN_TESTING = get_setting(
 | 
				
			||||||
    'INVENTREE_PLUGIN_TESTING', 'PLUGIN_TESTING', TESTING
 | 
					    'INVENTREE_PLUGIN_TESTING', 'PLUGIN_TESTING', TESTING
 | 
				
			||||||
@@ -160,8 +158,6 @@ PLUGIN_RETRY = get_setting(
 | 
				
			|||||||
    'INVENTREE_PLUGIN_RETRY', 'PLUGIN_RETRY', 3, typecast=int
 | 
					    'INVENTREE_PLUGIN_RETRY', 'PLUGIN_RETRY', 3, typecast=int
 | 
				
			||||||
)  # How often should plugin loading be tried?
 | 
					)  # How often should plugin loading be tried?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PLUGIN_FILE_CHECKED = False  # Was the plugin file checked?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
STATICFILES_DIRS = []
 | 
					STATICFILES_DIRS = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Translated Template settings
 | 
					# Translated Template settings
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1180,23 +1180,6 @@ class TestSettings(InvenTreeTestCase):
 | 
				
			|||||||
        # make sure to clean up
 | 
					        # make sure to clean up
 | 
				
			||||||
        settings.TESTING_ENV = False
 | 
					        settings.TESTING_ENV = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_initial_install(self):
 | 
					 | 
				
			||||||
        """Test if install of plugins on startup works."""
 | 
					 | 
				
			||||||
        from plugin import registry
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not settings.DOCKER:
 | 
					 | 
				
			||||||
            # Check an install run
 | 
					 | 
				
			||||||
            response = registry.install_plugin_file()
 | 
					 | 
				
			||||||
            self.assertEqual(response, 'first_run')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Set dynamic setting to True and rerun to launch install
 | 
					 | 
				
			||||||
            InvenTreeSetting.set_setting('PLUGIN_ON_STARTUP', True, self.user)
 | 
					 | 
				
			||||||
            registry.reload_plugins(full_reload=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Check that there was another run
 | 
					 | 
				
			||||||
        response = registry.install_plugin_file()
 | 
					 | 
				
			||||||
        self.assertEqual(response, True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_helpers_cfg_file(self):
 | 
					    def test_helpers_cfg_file(self):
 | 
				
			||||||
        """Test get_config_file."""
 | 
					        """Test get_config_file."""
 | 
				
			||||||
        # normal run - not configured
 | 
					        # normal run - not configured
 | 
				
			||||||
@@ -1216,24 +1199,6 @@ class TestSettings(InvenTreeTestCase):
 | 
				
			|||||||
                str(config.get_config_file()).lower(),
 | 
					                str(config.get_config_file()).lower(),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_helpers_plugin_file(self):
 | 
					 | 
				
			||||||
        """Test get_plugin_file."""
 | 
					 | 
				
			||||||
        # normal run - not configured
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        valid = ['inventree/plugins.txt', 'inventree/data/plugins.txt']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            any(opt in str(config.get_plugin_file()).lower() for opt in valid)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # with env set
 | 
					 | 
				
			||||||
        with self.in_env_context({
 | 
					 | 
				
			||||||
            'INVENTREE_PLUGIN_FILE': '_testfolder/my_special_plugins.txt'
 | 
					 | 
				
			||||||
        }):
 | 
					 | 
				
			||||||
            self.assertIn(
 | 
					 | 
				
			||||||
                '_testfolder/my_special_plugins.txt', str(config.get_plugin_file())
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_helpers_setting(self):
 | 
					    def test_helpers_setting(self):
 | 
				
			||||||
        """Test get_setting."""
 | 
					        """Test get_setting."""
 | 
				
			||||||
        TEST_ENV_NAME = '123TEST'
 | 
					        TEST_ENV_NAME = '123TEST'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,7 +91,6 @@ tracing:
 | 
				
			|||||||
# Set this variable to True to enable InvenTree Plugins, or use the environment variable INVENTREE_PLUGINS_ENABLED
 | 
					# Set this variable to True to enable InvenTree Plugins, or use the environment variable INVENTREE_PLUGINS_ENABLED
 | 
				
			||||||
plugins_enabled: False
 | 
					plugins_enabled: False
 | 
				
			||||||
#plugin_noinstall: True
 | 
					#plugin_noinstall: True
 | 
				
			||||||
#plugin_file: '/path/to/plugins.txt'
 | 
					 | 
				
			||||||
#plugin_dir: '/path/to/plugins/'
 | 
					#plugin_dir: '/path/to/plugins/'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Set this variable to True to enable auto-migrations, or use the environment variable INVENTREE_AUTO_UPDATE
 | 
					# Set this variable to True to enable auto-migrations, or use the environment variable INVENTREE_AUTO_UPDATE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,18 +40,6 @@ class PluginAppConfig(AppConfig):
 | 
				
			|||||||
            logger.info('Loading InvenTree plugins')
 | 
					            logger.info('Loading InvenTree plugins')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not registry.is_loading:
 | 
					            if not registry.is_loading:
 | 
				
			||||||
                # this is the first startup
 | 
					 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                    from common.models import InvenTreeSetting
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if InvenTreeSetting.get_setting(
 | 
					 | 
				
			||||||
                        'PLUGIN_ON_STARTUP', create=False, cache=False
 | 
					 | 
				
			||||||
                    ):
 | 
					 | 
				
			||||||
                        # make sure all plugins are installed
 | 
					 | 
				
			||||||
                        registry.install_plugin_file()
 | 
					 | 
				
			||||||
                except Exception:  # pragma: no cover
 | 
					 | 
				
			||||||
                    pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # Perform a full reload of the plugin registry
 | 
					                # Perform a full reload of the plugin registry
 | 
				
			||||||
                registry.reload_plugins(
 | 
					                registry.reload_plugins(
 | 
				
			||||||
                    full_reload=True, force_reload=True, collect=True
 | 
					                    full_reload=True, force_reload=True, collect=True
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,10 +177,20 @@ def get_modules(pkg, path=None):
 | 
				
			|||||||
    elif type(path) is not list:
 | 
					    elif type(path) is not list:
 | 
				
			||||||
        path = [path]
 | 
					        path = [path]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for finder, name, _ in pkgutil.walk_packages(path):
 | 
					    try:
 | 
				
			||||||
 | 
					        packages = pkgutil.walk_packages(path)
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        raise IntegrationPluginError(pkg.__name__, str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        for finder, name, _ in packages:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                if sys.version_info < (3, 12):
 | 
					                if sys.version_info < (3, 12):
 | 
				
			||||||
 | 
					                    try:
 | 
				
			||||||
                        module = finder.find_module(name).load_module(name)
 | 
					                        module = finder.find_module(name).load_module(name)
 | 
				
			||||||
 | 
					                    except Exception as e:
 | 
				
			||||||
 | 
					                        raise IntegrationPluginError(name, str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    spec = finder.find_spec(name)
 | 
					                    spec = finder.find_spec(name)
 | 
				
			||||||
                    module = module_from_spec(spec)
 | 
					                    module = module_from_spec(spec)
 | 
				
			||||||
@@ -206,8 +216,8 @@ def get_classes(module) -> list:
 | 
				
			|||||||
    """Get all classes in a given module."""
 | 
					    """Get all classes in a given module."""
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        return inspect.getmembers(module, inspect.isclass)
 | 
					        return inspect.getmembers(module, inspect.isclass)
 | 
				
			||||||
    except Exception:
 | 
					    except Exception as e:
 | 
				
			||||||
        return []
 | 
					        raise IntegrationPluginError(module.__name__, str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_plugins(pkg, baseclass, path=None):
 | 
					def get_plugins(pkg, baseclass, path=None):
 | 
				
			||||||
@@ -223,7 +233,12 @@ def get_plugins(pkg, baseclass, path=None):
 | 
				
			|||||||
    # Iterate through each module in the package
 | 
					    # Iterate through each module in the package
 | 
				
			||||||
    for mod in modules:
 | 
					    for mod in modules:
 | 
				
			||||||
        # Iterate through each class in the module
 | 
					        # Iterate through each class in the module
 | 
				
			||||||
        for item in get_classes(mod):
 | 
					        try:
 | 
				
			||||||
 | 
					            classes = get_classes(mod)
 | 
				
			||||||
 | 
					        except IntegrationPluginError:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for item in classes:
 | 
				
			||||||
            plugin = item[1]
 | 
					            plugin = item[1]
 | 
				
			||||||
            if issubclass(plugin, baseclass) and plugin.NAME:
 | 
					            if issubclass(plugin, baseclass) and plugin.NAME:
 | 
				
			||||||
                plugins.append(plugin)
 | 
					                plugins.append(plugin)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -155,98 +155,6 @@ def plugins_dir():
 | 
				
			|||||||
    return pd.absolute()
 | 
					    return pd.absolute()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def install_plugins_file():
 | 
					 | 
				
			||||||
    """Install plugins from the plugins file."""
 | 
					 | 
				
			||||||
    logger.info('Installing plugins from plugins file')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pf = settings.PLUGIN_FILE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not pf or not pf.exists():
 | 
					 | 
				
			||||||
        logger.warning('Plugin file %s does not exist', str(pf))
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    plugin_dir = plugins_dir()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cmd = ['install', '-U', '--target', str(plugin_dir), '-r', str(pf)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        pip_command(*cmd)
 | 
					 | 
				
			||||||
    except subprocess.CalledProcessError as error:
 | 
					 | 
				
			||||||
        output = error.output.decode('utf-8')
 | 
					 | 
				
			||||||
        logger.exception('Plugin file installation failed: %s', str(output))
 | 
					 | 
				
			||||||
        log_error('pip')
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
    except Exception as exc:
 | 
					 | 
				
			||||||
        logger.exception('Plugin file installation failed: %s', exc)
 | 
					 | 
				
			||||||
        log_error('pip')
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Update static files
 | 
					 | 
				
			||||||
    plugin.staticfiles.collect_plugins_static_files()
 | 
					 | 
				
			||||||
    plugin.staticfiles.clear_plugins_static_files()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # At this point, the plugins file has been installed
 | 
					 | 
				
			||||||
    return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def update_plugins_file(install_name, remove=False):
 | 
					 | 
				
			||||||
    """Add a plugin to the plugins file."""
 | 
					 | 
				
			||||||
    logger.info('Adding plugin to plugins file: %s', install_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pf = settings.PLUGIN_FILE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not pf or not pf.exists():
 | 
					 | 
				
			||||||
        logger.warning('Plugin file %s does not exist', str(pf))
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def compare_line(line: str):
 | 
					 | 
				
			||||||
        """Check if a line in the file matches the installname."""
 | 
					 | 
				
			||||||
        return line.strip().split('==')[0] == install_name.split('==')[0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # First, read in existing plugin file
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        with pf.open(mode='r') as f:
 | 
					 | 
				
			||||||
            lines = f.readlines()
 | 
					 | 
				
			||||||
    except Exception as exc:
 | 
					 | 
				
			||||||
        logger.exception('Failed to read plugins file: %s', str(exc))
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Reconstruct output file
 | 
					 | 
				
			||||||
    output = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    found = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Check if plugin is already in file
 | 
					 | 
				
			||||||
    for line in lines:
 | 
					 | 
				
			||||||
        # Ignore processing for any commented lines
 | 
					 | 
				
			||||||
        if line.strip().startswith('#'):
 | 
					 | 
				
			||||||
            output.append(line)
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if compare_line(line):
 | 
					 | 
				
			||||||
            found = True
 | 
					 | 
				
			||||||
            if not remove:
 | 
					 | 
				
			||||||
                # Replace line with new install name
 | 
					 | 
				
			||||||
                output.append(install_name)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            output.append(line)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Append plugin to file
 | 
					 | 
				
			||||||
    if not found and not remove:
 | 
					 | 
				
			||||||
        output.append(install_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Write file back to disk
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        with pf.open(mode='w') as f:
 | 
					 | 
				
			||||||
            for line in output:
 | 
					 | 
				
			||||||
                f.write(line)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if not line.endswith('\n'):
 | 
					 | 
				
			||||||
                    f.write('\n')
 | 
					 | 
				
			||||||
    except Exception as exc:
 | 
					 | 
				
			||||||
        logger.exception('Failed to add plugin to plugins file: %s', str(exc))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def install_plugin(url=None, packagename=None, user=None, version=None):
 | 
					def install_plugin(url=None, packagename=None, user=None, version=None):
 | 
				
			||||||
    """Install a plugin into the python virtual environment.
 | 
					    """Install a plugin into the python virtual environment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -309,9 +217,6 @@ def install_plugin(url=None, packagename=None, user=None, version=None):
 | 
				
			|||||||
    except subprocess.CalledProcessError as error:
 | 
					    except subprocess.CalledProcessError as error:
 | 
				
			||||||
        handle_pip_error(error, 'plugin_install')
 | 
					        handle_pip_error(error, 'plugin_install')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Save plugin to plugins file
 | 
					 | 
				
			||||||
    update_plugins_file(full_pkg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Reload the plugin registry, to discover the new plugin
 | 
					    # Reload the plugin registry, to discover the new plugin
 | 
				
			||||||
    from plugin.registry import registry
 | 
					    from plugin.registry import registry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -376,9 +281,6 @@ def uninstall_plugin(cfg: plugin.models.PluginConfig, user=None, delete_config=T
 | 
				
			|||||||
        # No matching install target found
 | 
					        # No matching install target found
 | 
				
			||||||
        raise ValidationError(_('Plugin installation not found'))
 | 
					        raise ValidationError(_('Plugin installation not found'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Update the plugins file
 | 
					 | 
				
			||||||
    update_plugins_file(package_name, remove=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if delete_config:
 | 
					    if delete_config:
 | 
				
			||||||
        # Remove the plugin configuration from the database
 | 
					        # Remove the plugin configuration from the database
 | 
				
			||||||
        cfg.delete()
 | 
					        cfg.delete()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -428,19 +428,6 @@ class PluginsRegistry:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.mixin_modules = collected_mixins
 | 
					        self.mixin_modules = collected_mixins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def install_plugin_file(self):
 | 
					 | 
				
			||||||
        """Make sure all plugins are installed in the current environment."""
 | 
					 | 
				
			||||||
        if settings.PLUGIN_FILE_CHECKED:
 | 
					 | 
				
			||||||
            logger.info('Plugin file was already checked')
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        from plugin.installer import install_plugins_file
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if install_plugins_file():
 | 
					 | 
				
			||||||
            settings.PLUGIN_FILE_CHECKED = True
 | 
					 | 
				
			||||||
            return 'first_run'
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # endregion
 | 
					    # endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # region general internal loading / activating / deactivating / unloading
 | 
					    # region general internal loading / activating / deactivating / unloading
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,6 @@
 | 
				
			|||||||
        {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_URL" icon="fa-link" %}
 | 
					        {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_URL" icon="fa-link" %}
 | 
				
			||||||
        {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_NAVIGATION" icon="fa-sitemap" %}
 | 
					        {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_NAVIGATION" icon="fa-sitemap" %}
 | 
				
			||||||
        {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_APP" icon="fa-rocket" %}
 | 
					        {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_APP" icon="fa-rocket" %}
 | 
				
			||||||
        {% include "InvenTree/settings/setting.html" with key="PLUGIN_ON_STARTUP" %}
 | 
					 | 
				
			||||||
    </tbody>
 | 
					    </tbody>
 | 
				
			||||||
</table>
 | 
					</table>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user