2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 19:46:46 +00:00

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 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(): def get_secret_key():
"""Return the secret key value which will be used by django. """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'^about/', AboutView.as_view(), name='about'),
re_path(r'^stats/', DatabaseStatsView.as_view(), name='stats'), 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 # DB user sessions
path('accounts/sessions/other/delete/', view=CustomSessionDeleteOtherView.as_view(), name='session_delete_other', ), 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', ), 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 new_frontendpatterns = platform_urls
# Load patterns for frontend according to settings urlpatterns = [
frontendpatterns = [] # admin sites
if settings.ENABLE_CLASSIC_FRONTEND: re_path(f'^{settings.INVENTREE_ADMIN_URL}/error_log/', include('error_report.urls')),
frontendpatterns.append(re_path('', include(classic_frontendpatterns))) re_path(f'^{settings.INVENTREE_ADMIN_URL}/', admin.site.urls, name='inventree-admin'),
if settings.ENABLE_PLATFORM_FRONTEND: ]
frontendpatterns.append(re_path('', include(new_frontendpatterns)))
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) # Append custom plugin URLs (if plugin support is enabled)
if settings.PLUGINS_ENABLED: if settings.PLUGINS_ENABLED:
frontendpatterns.append(get_plugin_urls()) urlpatterns.append(get_plugin_urls())
urlpatterns = [
re_path('', include(frontendpatterns)),
re_path('', include(backendpatterns)),
]
# Server running in "DEBUG" mode? # Server running in "DEBUG" mode?
if settings.DEBUG: if settings.DEBUG:
@ -245,5 +245,10 @@ if settings.DEBUG:
path('__debug__/', include(debug_toolbar.urls)), path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns ] + 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 # Send any unknown URLs to the parts page
urlpatterns += [re_path(r'^.*$', RedirectView.as_view(url='/index/', permanent=False), name='index')] urlpatterns += [re_path(r'^.*$', RedirectView.as_view(url='/index/', permanent=False), name='index')]

View File

@ -1,11 +1,14 @@
"""Plugin mixin class for AppMixin.""" """Plugin mixin class for AppMixin."""
import logging import logging
from importlib import reload from importlib import reload
from pathlib import Path
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from InvenTree.config import get_plugin_dir
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
@ -156,12 +159,22 @@ class AppMixin:
- a local file / dir - a local file / dir
- a package - a package
""" """
try: path = plugin.path()
# for local path plugins custom_plugins_dir = get_plugin_dir()
plugin_path = '.'.join(plugin.path().relative_to(settings.BASE_DIR).parts)
except ValueError: # pragma: no cover 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 is shipped as package - extract plugin module name
plugin_path = plugin.__module__.split('.')[0] plugin_path = plugin.__module__.split('.')[0]
return plugin_path return plugin_path
# endregion # endregion

View File

@ -17,14 +17,14 @@ from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.db.utils import IntegrityError, OperationalError, ProgrammingError 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.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from maintenance_mode.core import (get_maintenance_mode, maintenance_mode_on, from maintenance_mode.core import (get_maintenance_mode, maintenance_mode_on,
set_maintenance_mode) set_maintenance_mode)
from InvenTree.config import get_setting from InvenTree.config import get_plugin_dir
from InvenTree.ready import canAppAccessDatabase from InvenTree.ready import canAppAccessDatabase
from .helpers import (IntegrationPluginError, get_entrypoints, get_plugins, from .helpers import (IntegrationPluginError, get_entrypoints, get_plugins,
@ -239,7 +239,7 @@ class PluginsRegistry:
if settings.TESTING: if settings.TESTING:
custom_dirs = os.getenv('INVENTREE_PLUGIN_TEST_DIR', None) custom_dirs = os.getenv('INVENTREE_PLUGIN_TEST_DIR', None)
else: # pragma: no cover 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) # Load from user specified directories (unless in testing mode)
dirs.append('plugins') dirs.append('plugins')
@ -577,19 +577,28 @@ class PluginsRegistry:
self.plugins_full: Dict[str, InvenTreePlugin] = {} self.plugins_full: Dict[str, InvenTreePlugin] = {}
def _update_urls(self): def _update_urls(self):
from InvenTree.urls import frontendpatterns as urlpattern """Due to the order in which plugins are loaded, the patterns in urls.py may be out of date.
from InvenTree.urls import urlpatterns as global_pattern
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 from plugin.urls import get_plugin_urls
for index, url in enumerate(urlpattern): for index, url in enumerate(urlpatterns):
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()
# Replace frontendpatterns app_name = getattr(url, 'app_name', None)
global_pattern[0] = re_path('', include(urlpattern))
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() clear_url_caches()
# endregion # endregion