diff --git a/src/backend/InvenTree/plugin/base/integration/UrlsMixin.py b/src/backend/InvenTree/plugin/base/integration/UrlsMixin.py index 654a4fb7d2..90d596204c 100644 --- a/src/backend/InvenTree/plugin/base/integration/UrlsMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/UrlsMixin.py @@ -1,7 +1,6 @@ """Plugin mixin class for UrlsMixin.""" from django.conf import settings -from django.urls import include, re_path import structlog @@ -72,12 +71,8 @@ class UrlsMixin: @property def urlpatterns(self): - """Urlpatterns for this plugin.""" - if self.has_urls: - return re_path( - f'^{self.slug}/', include((self.urls, self.slug)), name=self.slug - ) - return None + """URL patterns for this plugin.""" + return self.urls or None @property def has_urls(self): diff --git a/src/backend/InvenTree/plugin/base/integration/test_mixins.py b/src/backend/InvenTree/plugin/base/integration/test_mixins.py index 8fe805e2a8..d390d4de3a 100644 --- a/src/backend/InvenTree/plugin/base/integration/test_mixins.py +++ b/src/backend/InvenTree/plugin/base/integration/test_mixins.py @@ -113,13 +113,10 @@ class UrlsMixinTest(BaseMixinDefinition, TestCase): target_pattern = re_path( f'^{plg_name}/', include((self.mixin.urls, plg_name)), name=plg_name ) - self.assertEqual( - self.mixin.urlpatterns.reverse_dict, target_pattern.reverse_dict - ) # resolve the view - self.assertEqual(self.mixin.urlpatterns.resolve('/testpath').func(), 'ccc') - self.assertEqual(self.mixin.urlpatterns.reverse('test'), 'testpath') + self.assertEqual(target_pattern.resolve('/testpath').func(), 'ccc') + self.assertEqual(target_pattern.reverse('test'), 'testpath') # no url self.assertIsNone(self.mixin_nothing.urls) diff --git a/src/backend/InvenTree/plugin/urls.py b/src/backend/InvenTree/plugin/urls.py index 406e6cee97..30852f4509 100644 --- a/src/backend/InvenTree/plugin/urls.py +++ b/src/backend/InvenTree/plugin/urls.py @@ -2,8 +2,11 @@ from django.conf import settings from django.urls import include, re_path +from django.urls.exceptions import Resolver404 +from django.views.generic.base import RedirectView from common.validators import get_global_setting +from InvenTree.exceptions import log_error from plugin import PluginMixinEnum PLUGIN_BASE = 'plugin' # Constant for links @@ -16,8 +19,35 @@ def get_plugin_urls(): urls = [] if get_global_setting('ENABLE_PLUGINS_URL', False) or settings.PLUGIN_TESTING_SETUP: - for plugin in registry.plugins.values(): - if plugin.mixin_enabled(PluginMixinEnum.URLS): - urls.append(plugin.urlpatterns) + for plugin in registry.with_mixin(PluginMixinEnum.URLS): + try: + if plugin_urls := plugin.urlpatterns: + # Check if the plugin has a custom URL pattern + for url in plugin_urls: + # Attempt to resolve against the URL pattern as a validation check + try: + url.resolve('') + except Resolver404: + pass + + urls.append( + re_path( + f'^{plugin.slug}/', + include((plugin_urls, plugin.slug)), + name=plugin.slug, + ) + ) + except Exception: + log_error('get_plugin_urls', plugin=plugin.slug) + continue + + # Redirect anything else to the root index + urls.append( + re_path( + r'^.*$', + RedirectView.as_view(url=f'/{settings.FRONTEND_URL_BASE}', permanent=False), + name='index', + ) + ) return re_path(f'^{PLUGIN_BASE}/', include((urls, 'plugin')))