mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[plugins] allow static files for plugins (#7425)
* Add 'clear' option to 'invoke static' * Add functions for copying static files from installed plugins * Collect plugin static files as part of 'invoke static' * Add 'activate' method for PluginConfig * Run as part of `invoke plugins`
This commit is contained in:
parent
66b2976d33
commit
9962d85570
@ -0,0 +1,13 @@
|
|||||||
|
"""Management command to collect plugin static files."""
|
||||||
|
|
||||||
|
from django.core.management import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
"""Collect static files for all installed plugins."""
|
||||||
|
|
||||||
|
def handle(self, *args, **kwargs):
|
||||||
|
"""Run the management command."""
|
||||||
|
from plugin.staticfiles import collect_plugins_static_files
|
||||||
|
|
||||||
|
collect_plugins_static_files()
|
@ -125,7 +125,7 @@ def canAppAccessDatabase(
|
|||||||
excluded_commands.append('test')
|
excluded_commands.append('test')
|
||||||
|
|
||||||
if not allow_plugins:
|
if not allow_plugins:
|
||||||
excluded_commands.extend(['collectstatic'])
|
excluded_commands.extend(['collectstatic', 'collectplugins'])
|
||||||
|
|
||||||
for cmd in excluded_commands:
|
for cmd in excluded_commands:
|
||||||
if cmd in sys.argv:
|
if cmd in sys.argv:
|
||||||
|
@ -12,6 +12,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
import InvenTree.models
|
import InvenTree.models
|
||||||
|
import plugin.staticfiles
|
||||||
from plugin import InvenTreePlugin, registry
|
from plugin import InvenTreePlugin, registry
|
||||||
|
|
||||||
|
|
||||||
@ -186,6 +187,20 @@ class PluginConfig(InvenTree.models.MetadataMixin, models.Model):
|
|||||||
|
|
||||||
return getattr(self.plugin, 'is_package', False)
|
return getattr(self.plugin, 'is_package', False)
|
||||||
|
|
||||||
|
def activate(self, active: bool) -> None:
|
||||||
|
"""Set the 'active' status of this plugin instance."""
|
||||||
|
from InvenTree.tasks import check_for_migrations, offload_task
|
||||||
|
|
||||||
|
if self.active == active:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.active = active
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
if active:
|
||||||
|
offload_task(check_for_migrations)
|
||||||
|
offload_task(plugin.staticfiles.copy_plugin_static_files, self.key)
|
||||||
|
|
||||||
|
|
||||||
class PluginSetting(common.models.BaseInvenTreeSetting):
|
class PluginSetting(common.models.BaseInvenTreeSetting):
|
||||||
"""This model represents settings for individual plugins."""
|
"""This model represents settings for individual plugins."""
|
||||||
|
@ -208,15 +208,7 @@ class PluginActivateSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
"""Apply the new 'active' value to the plugin instance."""
|
"""Apply the new 'active' value to the plugin instance."""
|
||||||
from InvenTree.tasks import check_for_migrations, offload_task
|
instance.activate(validated_data.get('active', True))
|
||||||
|
|
||||||
instance.active = validated_data.get('active', True)
|
|
||||||
instance.save()
|
|
||||||
|
|
||||||
if instance.active:
|
|
||||||
# A plugin has just been activated - check for database migrations
|
|
||||||
offload_task(check_for_migrations)
|
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
92
src/backend/InvenTree/plugin/staticfiles.py
Normal file
92
src/backend/InvenTree/plugin/staticfiles.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
"""Static files management for InvenTree plugins."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||||
|
|
||||||
|
from plugin.registry import registry
|
||||||
|
|
||||||
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
|
|
||||||
|
def clear_static_dir(path, recursive=True):
|
||||||
|
"""Clear the specified directory from the 'static' output directory.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
path: The path to the directory to clear
|
||||||
|
recursive: If True, clear the directory recursively
|
||||||
|
"""
|
||||||
|
if not staticfiles_storage.exists(path):
|
||||||
|
return
|
||||||
|
|
||||||
|
dirs, files = staticfiles_storage.listdir(path)
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
staticfiles_storage.delete(f'{path}/{f}')
|
||||||
|
|
||||||
|
if recursive:
|
||||||
|
for d in dirs:
|
||||||
|
clear_static_dir(f'{path}/{d}', recursive=True)
|
||||||
|
staticfiles_storage.delete(d)
|
||||||
|
|
||||||
|
|
||||||
|
def collect_plugins_static_files():
|
||||||
|
"""Copy static files from all installed plugins into the static directory."""
|
||||||
|
registry.check_reload()
|
||||||
|
|
||||||
|
logger.info('Collecting static files for all installed plugins.')
|
||||||
|
|
||||||
|
for slug in registry.plugins.keys():
|
||||||
|
copy_plugin_static_files(slug)
|
||||||
|
|
||||||
|
|
||||||
|
def copy_plugin_static_files(slug):
|
||||||
|
"""Copy static files for the specified plugin."""
|
||||||
|
registry.check_reload()
|
||||||
|
|
||||||
|
plugin = registry.get_plugin(slug)
|
||||||
|
|
||||||
|
if not plugin:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info("Copying static files for plugin '%s'")
|
||||||
|
|
||||||
|
# Get the source path for the plugin
|
||||||
|
source_path = plugin.path().joinpath('static')
|
||||||
|
|
||||||
|
if not source_path.is_dir():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create prefix for the destination path
|
||||||
|
destination_prefix = f'plugins/{slug}/'
|
||||||
|
|
||||||
|
# Clear the destination path
|
||||||
|
clear_static_dir(destination_prefix)
|
||||||
|
|
||||||
|
items = list(source_path.glob('*'))
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
copied = 0
|
||||||
|
|
||||||
|
while idx < len(items):
|
||||||
|
item = items[idx]
|
||||||
|
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
if item.is_dir():
|
||||||
|
items.extend(item.glob('*'))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if item.is_file():
|
||||||
|
relative_path = item.relative_to(source_path)
|
||||||
|
|
||||||
|
destination_path = f'{destination_prefix}{relative_path}'
|
||||||
|
|
||||||
|
with item.open('rb') as src:
|
||||||
|
staticfiles_storage.save(destination_path, src)
|
||||||
|
|
||||||
|
logger.debug(f'- copied {item} to {destination_path}')
|
||||||
|
copied += 1
|
||||||
|
|
||||||
|
logger.info(f"Copied %s static files for plugin '%s'.", copied, slug)
|
18
tasks.py
18
tasks.py
@ -227,6 +227,9 @@ def plugins(c, uv=False):
|
|||||||
c.run('pip3 install --no-cache-dir --disable-pip-version-check uv')
|
c.run('pip3 install --no-cache-dir --disable-pip-version-check uv')
|
||||||
c.run(f"uv pip install -r '{plugin_file}'")
|
c.run(f"uv pip install -r '{plugin_file}'")
|
||||||
|
|
||||||
|
# Collect plugin static files
|
||||||
|
manage(c, 'collectplugins')
|
||||||
|
|
||||||
|
|
||||||
@task(help={'uv': 'Use UV package manager (experimental)'})
|
@task(help={'uv': 'Use UV package manager (experimental)'})
|
||||||
def install(c, uv=False):
|
def install(c, uv=False):
|
||||||
@ -317,8 +320,8 @@ def remove_mfa(c, mail=''):
|
|||||||
manage(c, f'remove_mfa {mail}')
|
manage(c, f'remove_mfa {mail}')
|
||||||
|
|
||||||
|
|
||||||
@task(help={'frontend': 'Build the frontend'})
|
@task(help={'frontend': 'Build the frontend', 'clear': 'Remove existing static files'})
|
||||||
def static(c, frontend=False):
|
def static(c, frontend=False, clear=True):
|
||||||
"""Copies required static files to the STATIC_ROOT directory, as per Django requirements."""
|
"""Copies required static files to the STATIC_ROOT directory, as per Django requirements."""
|
||||||
manage(c, 'prerender')
|
manage(c, 'prerender')
|
||||||
|
|
||||||
@ -327,7 +330,16 @@ def static(c, frontend=False):
|
|||||||
frontend_build(c)
|
frontend_build(c)
|
||||||
|
|
||||||
print('Collecting static files...')
|
print('Collecting static files...')
|
||||||
manage(c, 'collectstatic --no-input --clear --verbosity 0')
|
|
||||||
|
cmd = 'collectstatic --no-input --verbosity 0'
|
||||||
|
|
||||||
|
if clear:
|
||||||
|
cmd += ' --clear'
|
||||||
|
|
||||||
|
manage(c, cmd)
|
||||||
|
|
||||||
|
# Collect plugin static files
|
||||||
|
manage(c, 'collectplugins')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
|
Loading…
Reference in New Issue
Block a user