mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[UI] Add CUI compatibility URLs (#9541)
* Add CUI compatibility URLs * Add config option to enable URL compatibility * Add unit tests * Simplify tests
This commit is contained in:
		| @@ -484,4 +484,13 @@ def get_frontend_settings(debug=True): | ||||
|         # If no servers are specified, show server selector | ||||
|         frontend_settings['show_server_selector'] = True | ||||
|  | ||||
|     # Support compatibility with "legacy" URLs? | ||||
|     try: | ||||
|         frontend_settings['url_compatibility'] = bool( | ||||
|             frontend_settings.get('url_compatibility', True) | ||||
|         ) | ||||
|     except Exception: | ||||
|         # If the value is not a boolean, set it to True | ||||
|         frontend_settings['url_compatibility'] = True | ||||
|  | ||||
|     return frontend_settings | ||||
|   | ||||
| @@ -1704,3 +1704,22 @@ class SchemaPostprocessingTest(TestCase): | ||||
|         self.assertNotIn('customer_detail', schemas_out.get('SalesOrder')['required']) | ||||
|         # required key removed when empty | ||||
|         self.assertNotIn('required', schemas_out.get('SalesOrderShipment')) | ||||
|  | ||||
|  | ||||
| class URLCompatibilityTest(InvenTreeTestCase): | ||||
|     """Unit test for legacy URL compatibility.""" | ||||
|  | ||||
|     URL_MAPPINGS = [ | ||||
|         ('/index/', '/web'), | ||||
|         ('/part/1/', '/web/part/1/'), | ||||
|         ('/company/customers/', '/web/sales/index/customers'), | ||||
|         ('/build/3/', '/web/manufacturing/build-order/3'), | ||||
|         ('/stock/item/1/', '/web/stock/item/1/'), | ||||
|     ] | ||||
|  | ||||
|     def test_legacy_urls(self): | ||||
|         """Test legacy URLs.""" | ||||
|         for old_url, new_url in self.URL_MAPPINGS: | ||||
|             response = self.client.get(old_url) | ||||
|             self.assertEqual(response.status_code, 302) | ||||
|             self.assertEqual(response['Location'], new_url) | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import report.api | ||||
| import stock.api | ||||
| import users.api | ||||
| from plugin.urls import get_plugin_urls | ||||
| from web.urls import cui_compatibility_urls | ||||
| from web.urls import urlpatterns as platform_urls | ||||
|  | ||||
| from .api import ( | ||||
| @@ -172,6 +173,10 @@ urlpatterns.append( | ||||
|     ) | ||||
| ) | ||||
|  | ||||
| # Compatibility layer for old (CUI) URLs | ||||
| if settings.FRONTEND_SETTINGS.get('url_compatibility'): | ||||
|     urlpatterns += cui_compatibility_urls(settings.FRONTEND_URL_BASE) | ||||
|  | ||||
| # Send any unknown URLs to the index page | ||||
| urlpatterns += [ | ||||
|     re_path( | ||||
|   | ||||
| @@ -3,10 +3,131 @@ | ||||
| from django.conf import settings | ||||
| from django.urls import include, path, re_path | ||||
| from django.views.decorators.csrf import ensure_csrf_cookie | ||||
| from django.views.generic import TemplateView | ||||
| from django.views.generic import RedirectView, TemplateView | ||||
|  | ||||
| spa_view = ensure_csrf_cookie(TemplateView.as_view(template_name='web/index.html')) | ||||
|  | ||||
|  | ||||
| def cui_compatibility_urls(base: str) -> list: | ||||
|     """Generate a list of URL patterns for compatibility with old (CUI) URLs. | ||||
|  | ||||
|     These URLs are provided for backwards compatibility with older versions of InvenTree, | ||||
|     before we moved to a SPA (Single Page Application) architecture in the 1.0.0 release. | ||||
|  | ||||
|     At some future point these may be removed? | ||||
|  | ||||
|     Args: | ||||
|         base (str): The base URL to use for generating the patterns. | ||||
|  | ||||
|     Returns: | ||||
|         list: A list of URL patterns. | ||||
|     """ | ||||
|     return [ | ||||
|         # Old 'index' view - reroute to the dashboard | ||||
|         path('index/', RedirectView.as_view(url=f'/{base}')), | ||||
|         path('settings/', RedirectView.as_view(url=f'/{base}/settings')), | ||||
|         # Company patterns | ||||
|         path( | ||||
|             'company/', | ||||
|             include([ | ||||
|                 path( | ||||
|                     'customers/', | ||||
|                     RedirectView.as_view(url=f'/{base}/sales/index/customers'), | ||||
|                 ), | ||||
|                 path( | ||||
|                     'manufacturers/', | ||||
|                     RedirectView.as_view(url=f'/{base}/purchasing/index/manufacturers'), | ||||
|                 ), | ||||
|                 path( | ||||
|                     'suppliers/', | ||||
|                     RedirectView.as_view(url=f'/{base}/purchasing/index/suppliers'), | ||||
|                 ), | ||||
|                 re_path( | ||||
|                     r'(?P<pk>\d+)/', RedirectView.as_view(url=f'/{base}/company/%(pk)s') | ||||
|                 ), | ||||
|             ]), | ||||
|         ), | ||||
|         # "Part" app views | ||||
|         re_path( | ||||
|             r'^part/(?P<path>.*)$', RedirectView.as_view(url=f'/{base}/part/%(path)s') | ||||
|         ), | ||||
|         # "Stock" app views | ||||
|         re_path( | ||||
|             r'^stock/(?P<path>.*)$', RedirectView.as_view(url=f'/{base}/stock/%(path)s') | ||||
|         ), | ||||
|         # "Build" app views (requires some custom handling) | ||||
|         path( | ||||
|             'build/', | ||||
|             include([ | ||||
|                 re_path( | ||||
|                     r'^(?P<pk>\d+)/', | ||||
|                     RedirectView.as_view( | ||||
|                         url=f'/{base}/manufacturing/build-order/%(pk)s' | ||||
|                     ), | ||||
|                 ), | ||||
|                 re_path('.*', RedirectView.as_view(url=f'/{base}/manufacturing/')), | ||||
|             ]), | ||||
|         ), | ||||
|         # "Order" app views | ||||
|         path( | ||||
|             'order/', | ||||
|             include([ | ||||
|                 path( | ||||
|                     'purchase-order/', | ||||
|                     include([ | ||||
|                         re_path( | ||||
|                             r'^(?P<pk>\d+)/', | ||||
|                             RedirectView.as_view( | ||||
|                                 url=f'/{base}/purchasing/purchase-order/%(pk)s' | ||||
|                             ), | ||||
|                         ), | ||||
|                         re_path( | ||||
|                             '.*', | ||||
|                             RedirectView.as_view( | ||||
|                                 url=f'/{base}/purchasing/index/purchaseorders/' | ||||
|                             ), | ||||
|                         ), | ||||
|                     ]), | ||||
|                 ), | ||||
|                 path( | ||||
|                     'sales-order/', | ||||
|                     include([ | ||||
|                         re_path( | ||||
|                             r'^(?P<pk>\d+)/', | ||||
|                             RedirectView.as_view( | ||||
|                                 url=f'/{base}/sales/sales-order/%(pk)s' | ||||
|                             ), | ||||
|                         ), | ||||
|                         re_path( | ||||
|                             '.*', | ||||
|                             RedirectView.as_view( | ||||
|                                 url=f'/{base}/sales/index/salesorders/' | ||||
|                             ), | ||||
|                         ), | ||||
|                     ]), | ||||
|                 ), | ||||
|                 path( | ||||
|                     'return-order/', | ||||
|                     include([ | ||||
|                         re_path( | ||||
|                             r'^(?P<pk>\d+)/', | ||||
|                             RedirectView.as_view( | ||||
|                                 url=f'/{base}/sales/return-order/%(pk)s' | ||||
|                             ), | ||||
|                         ), | ||||
|                         re_path( | ||||
|                             '.*', | ||||
|                             RedirectView.as_view( | ||||
|                                 url=f'/{base}/sales/index/returnorders/' | ||||
|                             ), | ||||
|                         ), | ||||
|                     ]), | ||||
|                 ), | ||||
|             ]), | ||||
|         ), | ||||
|     ] | ||||
|  | ||||
|  | ||||
| urlpatterns = [ | ||||
|     path( | ||||
|         f'{settings.FRONTEND_URL_BASE}/', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user