Plugin loading fixes (#5572)

* Add config function to return external plugins dir

* Enable AppMixin support relative to external plugins directory

* Fix for urls.py

- URL patterns were causing custom app mixin plugins to fail reverse lookup in admin interface
- Brought admin URLs up one level

* simplify urls.py

* Fix plugin registry code which registers plugin URLs

- As we have updated InvenTree.urls.py we need to adjust this logic too

* Adds redirect for favicon.ico

* Handle empty plugins dir
This commit is contained in:
Oliver 2023-09-20 13:32:34 +10:00 committed by GitHub
parent cf4df3402c
commit 47f341d2b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 33 deletions

View File

@ -281,6 +281,12 @@ def get_plugin_file():
return plugin_file
def get_plugin_dir():
"""Returns the path of the custom plugins directory"""
return get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir')
def get_secret_key():
"""Return the secret key value which will be used by django.

View File

@ -189,10 +189,6 @@ classic_frontendpatterns = [
re_path(r'^about/', AboutView.as_view(), name='about'),
re_path(r'^stats/', DatabaseStatsView.as_view(), name='stats'),
# admin sites
re_path(f'^{settings.INVENTREE_ADMIN_URL}/error_log/', include('error_report.urls')),
re_path(f'^{settings.INVENTREE_ADMIN_URL}/', admin.site.urls, name='inventree-admin'),
# DB user sessions
path('accounts/sessions/other/delete/', view=CustomSessionDeleteOtherView.as_view(), name='session_delete_other', ),
re_path(r'^accounts/sessions/(?P<pk>\w+)/delete/$', view=CustomSessionDeleteView.as_view(), name='session_delete', ),
@ -213,22 +209,26 @@ classic_frontendpatterns = [
new_frontendpatterns = platform_urls
# Load patterns for frontend according to settings
frontendpatterns = []
if settings.ENABLE_CLASSIC_FRONTEND:
frontendpatterns.append(re_path('', include(classic_frontendpatterns)))
if settings.ENABLE_PLATFORM_FRONTEND:
frontendpatterns.append(re_path('', include(new_frontendpatterns)))
urlpatterns = [
# admin sites
re_path(f'^{settings.INVENTREE_ADMIN_URL}/error_log/', include('error_report.urls')),
re_path(f'^{settings.INVENTREE_ADMIN_URL}/', admin.site.urls, name='inventree-admin'),
]
urlpatterns += backendpatterns
frontendpatterns = []
if settings.ENABLE_CLASSIC_FRONTEND:
frontendpatterns += classic_frontendpatterns
if settings.ENABLE_PLATFORM_FRONTEND:
frontendpatterns += new_frontendpatterns
urlpatterns += frontendpatterns
# Append custom plugin URLs (if plugin support is enabled)
if settings.PLUGINS_ENABLED:
frontendpatterns.append(get_plugin_urls())
urlpatterns = [
re_path('', include(frontendpatterns)),
re_path('', include(backendpatterns)),
]
urlpatterns.append(get_plugin_urls())
# Server running in "DEBUG" mode?
if settings.DEBUG:
@ -245,5 +245,10 @@ if settings.DEBUG:
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
# Redirect for favicon.ico
urlpatterns.append(
path('favicon.ico', RedirectView.as_view(url=f'{settings.STATIC_ROOT}/img/favicon/favicon.ico'))
)
# Send any unknown URLs to the parts page
urlpatterns += [re_path(r'^.*$', RedirectView.as_view(url='/index/', permanent=False), name='index')]

View File

@ -1,11 +1,14 @@
"""Plugin mixin class for AppMixin."""
import logging
from importlib import reload
from pathlib import Path
from django.apps import apps
from django.conf import settings
from django.contrib import admin
from InvenTree.config import get_plugin_dir
logger = logging.getLogger('inventree')
@ -156,12 +159,22 @@ class AppMixin:
- a local file / dir
- a package
"""
try:
# for local path plugins
plugin_path = '.'.join(plugin.path().relative_to(settings.BASE_DIR).parts)
except ValueError: # pragma: no cover
path = plugin.path()
custom_plugins_dir = get_plugin_dir()
if path.is_relative_to(settings.BASE_DIR):
# Plugins which are located relative to the base code directory
plugin_path = '.'.join(path.relative_to(settings.BASE_DIR).parts)
elif custom_plugins_dir and path.is_relative_to(custom_plugins_dir):
# Plugins which are located relative to the custom plugins directory
plugin_path = '.'.join(path.relative_to(custom_plugins_dir).parts)
# Ensure that the parent directory is added also
plugin_path = Path(custom_plugins_dir).parts[-1] + '.' + plugin_path
else:
# plugin is shipped as package - extract plugin module name
plugin_path = plugin.__module__.split('.')[0]
return plugin_path
# endregion

View File

@ -17,14 +17,14 @@ from django.apps import apps
from django.conf import settings
from django.contrib import admin
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
from django.urls import clear_url_caches, include, re_path
from django.urls import clear_url_caches, re_path
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from maintenance_mode.core import (get_maintenance_mode, maintenance_mode_on,
set_maintenance_mode)
from InvenTree.config import get_setting
from InvenTree.config import get_plugin_dir
from InvenTree.ready import canAppAccessDatabase
from .helpers import (IntegrationPluginError, get_entrypoints, get_plugins,
@ -239,7 +239,7 @@ class PluginsRegistry:
if settings.TESTING:
custom_dirs = os.getenv('INVENTREE_PLUGIN_TEST_DIR', None)
else: # pragma: no cover
custom_dirs = get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir')
custom_dirs = get_plugin_dir()
# Load from user specified directories (unless in testing mode)
dirs.append('plugins')
@ -577,19 +577,28 @@ class PluginsRegistry:
self.plugins_full: Dict[str, InvenTreePlugin] = {}
def _update_urls(self):
from InvenTree.urls import frontendpatterns as urlpattern
from InvenTree.urls import urlpatterns as global_pattern
"""Due to the order in which plugins are loaded, the patterns in urls.py may be out of date.
This function updates the patterns in urls.py to ensure that the correct patterns are loaded,
and then refreshes the django url cache.
Note that we also have to refresh the admin site URLS,
as any custom AppMixin plugins require admin integration
"""
from InvenTree.urls import urlpatterns
from plugin.urls import get_plugin_urls
for index, url in enumerate(urlpattern):
if hasattr(url, 'app_name'):
if url.app_name == 'admin':
urlpattern[index] = re_path(r'^admin/', admin.site.urls, name='inventree-admin')
elif url.app_name == 'plugin':
urlpattern[index] = get_plugin_urls()
for index, url in enumerate(urlpatterns):
# Replace frontendpatterns
global_pattern[0] = re_path('', include(urlpattern))
app_name = getattr(url, 'app_name', None)
if app_name == 'admin':
urlpatterns[index] = re_path(r'^admin/', admin.site.urls, name='inventree-admin')
if app_name == 'plugin':
urlpatterns[index] = get_plugin_urls()
# Refresh the URL cache
clear_url_caches()
# endregion