From 6bae19e85b11e17d99214dc4c119164ce0f29f3a Mon Sep 17 00:00:00 2001 From: gromdimon Date: Thu, 25 Apr 2024 13:32:51 +0200 Subject: [PATCH] change the request to names for AppLinks utility class --- .../templatetags/projectroles_tags.py | 13 +- projectroles/tests/test_views_ajax.py | 164 ++++++++++++++++++ projectroles/utils.py | 94 ++++++---- projectroles/views_ajax.py | 14 +- 4 files changed, 241 insertions(+), 44 deletions(-) diff --git a/projectroles/templatetags/projectroles_tags.py b/projectroles/templatetags/projectroles_tags.py index e7ccdfb0..3f5708c4 100644 --- a/projectroles/templatetags/projectroles_tags.py +++ b/projectroles/templatetags/projectroles_tags.py @@ -274,10 +274,19 @@ def get_admin_warning(): @register.simple_tag def get_user_links(request): """Return user dropdown links""" - return app_links.get_user_links(request) + return app_links.get_user_links( + request.user, + app_name=request.resolver_match.app_name, + url_name=request.resolver_match.url_name, + ) @register.simple_tag def get_project_app_links(request, project=None): """Return sidebar links""" - return app_links.get_project_app_links(request, project) + return app_links.get_project_app_links( + request.user, + project, + app_name=request.resolver_match.app_name, + url_name=request.resolver_match.url_name, + ) diff --git a/projectroles/tests/test_views_ajax.py b/projectroles/tests/test_views_ajax.py index d09b5cc0..367bd8d9 100644 --- a/projectroles/tests/test_views_ajax.py +++ b/projectroles/tests/test_views_ajax.py @@ -546,6 +546,72 @@ def test_get(self): } self.assertEqual(response.json(), expected) + def test_get_app_links(self): + """Test sidebar content retrieval with specific app links""" + with self.login(self.user): + response = self.client.get( + reverse( + 'projectroles:ajax_sidebar', + kwargs={'project': self.project.sodar_uuid}, + ) + + '?app_name=filesfolders&url_name=list' + ) + self.assertEqual(response.status_code, 200) + expected = { + 'links': [ + { + 'name': 'project-detail', + 'url': f'/project/{self.project.sodar_uuid}', + 'label': 'Project Overview', + 'icon': 'mdi:cube', + 'active': False, + }, + { + 'name': 'app-plugin-bgjobs', + 'url': f'/bgjobs/list/{self.project.sodar_uuid}', + 'label': 'Background Jobs', + 'icon': 'mdi:server', + 'active': False, + }, + { + 'name': 'app-plugin-example_project_app', + 'url': f'/examples/project/{self.project.sodar_uuid}', + 'label': 'Example Project App', + 'icon': 'mdi:rocket-launch', + 'active': False, + }, + { + 'name': 'app-plugin-filesfolders', + 'url': f'/files/{self.project.sodar_uuid}', + 'label': 'Files', + 'icon': 'mdi:file', + 'active': True, + }, + { + 'name': 'app-plugin-timeline', + 'url': f'/timeline/{self.project.sodar_uuid}', + 'label': 'Timeline', + 'icon': 'mdi:clock-time-eight', + 'active': False, + }, + { + 'name': 'project-roles', + 'url': f'/project/members/{self.project.sodar_uuid}', + 'label': 'Members', + 'icon': 'mdi:account-multiple', + 'active': False, + }, + { + 'name': 'project-update', + 'url': f'/project/project/update/{self.project.sodar_uuid}', + 'label': 'Update Project', + 'icon': 'mdi:lead-pencil', + 'active': False, + }, + ], + } + self.assertEqual(response.json(), expected) + def test_get_no_access(self): """Test sidebar content retrieval with no access""" new_user = self.make_user('new_user') @@ -721,6 +787,104 @@ def test_superuser(self): } self.assertEqual(response.json(), expected) + def test_superuser_app_links(self): + """Test UserDropdownContentAjaxView with superuser""" + with self.login(self.user): + response = self.client.get( + reverse('projectroles:ajax_user_dropdown') + + '?app_name=example_site_app&url_name=example' + ) + self.assertEqual(response.status_code, 200) + expected = { + 'links': [ + { + 'name': 'adminalerts', + 'url': '/alerts/adm/list', + 'label': 'Admin Alerts', + 'icon': 'mdi:alert', + 'active': False, + }, + { + 'name': 'appalerts', + 'url': '/alerts/app/list', + 'label': 'App Alerts', + 'icon': 'mdi:alert-octagram', + 'active': False, + }, + { + 'name': 'bgjobs_site', + 'url': '/bgjobs/list', + 'label': 'Site Background Jobs', + 'icon': 'mdi:server', + 'active': False, + }, + { + 'name': 'example_site_app', + 'url': '/examples/site/example', + 'label': 'Example Site App', + 'icon': 'mdi:rocket-launch-outline', + 'active': True, + }, + { + 'name': 'remotesites', + 'url': '/project/remote/sites', + 'label': 'Remote Site Access', + 'icon': 'mdi:cloud-sync', + 'active': False, + }, + { + 'name': 'siteinfo', + 'url': '/siteinfo/info', + 'label': 'Site Info', + 'icon': 'mdi:bar-chart', + 'active': False, + }, + { + 'name': 'timeline_site', + 'url': '/timeline/site', + 'label': 'Site-Wide Events', + 'icon': 'mdi:clock-time-eight-outline', + 'active': False, + }, + { + 'name': 'timeline_site_admin', + 'url': '/timeline/site/all', + 'label': 'All Timeline Events', + 'icon': 'mdi:web-clock', + 'active': False, + }, + { + 'name': 'tokens', + 'url': '/tokens/', + 'label': 'API Tokens', + 'icon': 'mdi:key-chain-variant', + 'active': False, + }, + { + 'name': 'userprofile', + 'url': '/user/profile', + 'label': 'User Profile', + 'icon': 'mdi:account-details', + 'active': False, + }, + { + 'name': 'admin', + 'url': '/admin/', + 'label': 'Django Admin', + 'icon': 'mdi:cogs', + 'active': False, + }, + { + 'name': 'sign-out', + 'url': '/logout/', + 'label': 'Logout', + 'icon': 'mdi:logout-variant', + 'active': False, + }, + ] + } + self.assertEqual(response.json(), expected) + class TestCurrentUserRetrieveAjaxView(SerializedObjectMixin, TestCase): """Tests for CurrentUserRetrieveAjaxView""" diff --git a/projectroles/utils.py b/projectroles/utils.py index efb06cf1..821aafef 100644 --- a/projectroles/utils.py +++ b/projectroles/utils.py @@ -115,26 +115,28 @@ def get_app_names(): class AppLinkContent: """Class for generating application links for the UI""" - def _is_active_projectroles(self, request, link_names=None): + def _is_active_projectroles( + self, app_name=None, url_name=None, link_names=None + ): """Check if current URL is active under the projectroles app.""" + if not app_name or not url_name: + return False # HACK: Avoid circular import from projectroles.urls import urlpatterns - if request.resolver_match.app_name != 'projectroles': + if app_name != 'projectroles': return False - url_name = request.resolver_match.url_name + url_name = url_name return url_name in [u.name for u in urlpatterns] and ( not link_names or url_name in link_names ) - def _is_active_plugin(self, app_plugin, request): + def _is_active_plugin(self, app_plugin, app_name=None, url_name=None): """ Check if current URL is active for a specific app plugin. """ - if not getattr(request, 'resolver_match'): + if not app_name or not url_name: return False - app_name = request.resolver_match.app_name - url_name = request.resolver_match.url_name if app_plugin.name.startswith(app_name) and url_name in [ u.name for u in getattr(app_plugin, 'urls', []) ]: @@ -174,7 +176,9 @@ def _allow_project_creation(self): return False return True - def get_project_app_links(self, request, project=None): + def get_project_app_links( + self, user, project=None, app_name=None, url_name=None + ): """Return project app links based on the current project and user.""" ret = [] # Add project related links @@ -193,13 +197,17 @@ def get_project_app_links(self, request, project=None): if project.type == PROJECT_TYPE_CATEGORY else 'mdi:cube' ), - 'active': self._is_active_projectroles(request, ['detail']), + 'active': self._is_active_projectroles( + link_names=['detail'], + app_name=app_name, + url_name=url_name, + ), } ) # Add app plugins links app_plugins = get_active_plugins() for plugin in app_plugins: - if self._is_app_visible(plugin, project, request.user): + if self._is_app_visible(plugin, project, user): ret.append( { 'name': "app-plugin-" + plugin.name, @@ -209,13 +217,13 @@ def get_project_app_links(self, request, project=None): ), 'label': ' '.join(plugin.title.split(' ')), 'icon': plugin.icon, - 'active': self._is_active_plugin(plugin, request), + 'active': self._is_active_plugin( + plugin, app_name=app_name, url_name=url_name + ), } ) # Add role editing link - if request.user.has_perm( - 'projectroles.view_project_roles', project - ): + if user.has_perm('projectroles.view_project_roles', project): ret.append( { 'name': 'project-roles', @@ -226,12 +234,14 @@ def get_project_app_links(self, request, project=None): 'label': 'Members', 'icon': 'mdi:account-multiple', 'active': self._is_active_projectroles( - request, ROLE_URLS + link_names=ROLE_URLS, + app_name=app_name, + url_name=url_name, ), } ) # Add project update link - if request.user.has_perm('projectroles.update_project', project): + if user.has_perm('projectroles.update_project', project): ret.append( { 'name': 'project-update', @@ -242,7 +252,9 @@ def get_project_app_links(self, request, project=None): 'label': 'Update Project', 'icon': 'mdi:lead-pencil', 'active': self._is_active_projectroles( - request, ['update'] + link_names=['update'], + app_name=app_name, + url_name=url_name, ), } ) @@ -251,7 +263,7 @@ def get_project_app_links(self, request, project=None): if ( project and project.type == PROJECT_TYPE_CATEGORY - and request.user.has_perm('projectroles.create_project', project) + and user.has_perm('projectroles.create_project', project) and self._allow_project_creation() and not project.is_remote() ): @@ -264,12 +276,16 @@ def get_project_app_links(self, request, project=None): ), 'label': 'Create Project or Category', 'icon': 'mdi:plus-thick', - 'active': self._is_active_projectroles(request, ['create']), + 'active': self._is_active_projectroles( + link_names=['create'], + app_name=app_name, + url_name=url_name, + ), } ) elif ( getattr(settings, 'PROJECTROLES_DISABLE_CATEGORIES', False) - and request.user.is_superuser + and user.is_superuser ): ret.append( { @@ -277,16 +293,16 @@ def get_project_app_links(self, request, project=None): 'url': reverse('projectroles:create'), 'label': 'Create Project', 'icon': 'mdi:plus-thick', - 'active': self._is_active_projectroles(request, ['create']), + 'active': self._is_active_projectroles( + link_names=['create'], + app_name=app_name, + url_name=url_name, + ), } ) elif ( - ( - request.resolver_match.url_name == 'home' - or request.resolver_match.app_name == 'projectroles' - and not project - ) - and request.user.has_perm('projectroles.create_project') + (url_name == 'home' or app_name == 'projectroles' and not project) + and user.has_perm('projectroles.create_project') and self._allow_project_creation() ): ret.append( @@ -295,44 +311,46 @@ def get_project_app_links(self, request, project=None): 'url': reverse('projectroles:create'), 'label': 'Create Category', 'icon': 'mdi:plus-thick', - 'active': self._is_active_projectroles(request, ['create']), + 'active': self._is_active_projectroles( + link_names=['create'], + app_name=app_name, + url_name=url_name, + ), } ) return ret - def get_user_links(self, request): + def get_user_links(self, user, app_name=None, url_name=None): """Return user links for the user dropdown""" ret = [] # Add site-wide apps links site_apps = get_active_plugins('site_app') for app in site_apps: - if ( - not app.app_permission - or hasattr(request, 'user') - and request.user.has_perm(app.app_permission) - ): + if not app.app_permission or user.has_perm(app.app_permission): ret.append( { 'name': app.name, 'url': reverse(app.entry_point_url_id), 'label': app.title, 'icon': app.icon, - 'active': self._is_active_plugin(app, request), + 'active': self._is_active_plugin( + app, app_name=app_name, url_name=url_name + ), } ) # Add admin link - if hasattr(request, 'user') and request.user.is_superuser: + if user.is_superuser: ret.append( { 'name': 'admin', 'url': reverse('admin:index'), 'label': 'Django Admin', 'icon': 'mdi:cogs', - 'active': request.path == reverse('admin:index'), + 'active': False, } ) # Add log out / sign in link - if hasattr(request, 'user') and request.user.is_authenticated: + if user.is_authenticated: ret.append( { 'name': 'sign-out', diff --git a/projectroles/views_ajax.py b/projectroles/views_ajax.py index 908a477a..8e95d911 100644 --- a/projectroles/views_ajax.py +++ b/projectroles/views_ajax.py @@ -438,11 +438,13 @@ class SidebarContentAjaxView(SODARBaseProjectAjaxView): def get(self, request, *args, **kwargs): project = self.get_project() + app_name = request.GET.get('app_name') + url_name = request.GET.get('url_name') # Get the content for the sidebar app_link_content = AppLinkContent() - # # Get the current URL for highlighting the active link - # current_url = request.GET.get('current_url') - sidebar_links = app_link_content.get_project_app_links(request, project) + sidebar_links = app_link_content.get_project_app_links( + request.user, project, app_name=app_name, url_name=url_name + ) return JsonResponse({'links': sidebar_links}) @@ -452,9 +454,13 @@ class UserDropdownContentAjaxView(SODARBaseAjaxView): permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): + app_name = request.GET.get('app_name') + url_name = request.GET.get('url_name') # Get the content for the user dropdown app_link_content = AppLinkContent() - user_dropdown_links = app_link_content.get_user_links(request) + user_dropdown_links = app_link_content.get_user_links( + request.user, app_name=app_name, url_name=url_name + ) return JsonResponse({'links': user_dropdown_links})