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_permissions_ajax.py b/projectroles/tests/test_permissions_ajax.py index 0877115f..1034c0e4 100644 --- a/projectroles/tests/test_permissions_ajax.py +++ b/projectroles/tests/test_permissions_ajax.py @@ -6,7 +6,6 @@ from projectroles.models import SODAR_CONSTANTS from projectroles.tests.test_permissions import ProjectPermissionTestBase - # SODAR constants PROJECT_ROLE_OWNER = SODAR_CONSTANTS['PROJECT_ROLE_OWNER'] PROJECT_ROLE_DELEGATE = SODAR_CONSTANTS['PROJECT_ROLE_DELEGATE'] @@ -237,6 +236,117 @@ def test_get_category_anon(self): self.assert_response(self.url_cat, self.anonymous, 401, method='POST') +class TestSidebarContentAjaxView(ProjectPermissionTestBase): + """Tests for SidebarContentAjaxView permissions""" + + def setUp(self): + super().setUp() + self.url = reverse( + 'projectroles:ajax_sidebar', + kwargs={'project': self.project.sodar_uuid}, + ) + self.url_cat = reverse( + 'projectroles:ajax_sidebar', + kwargs={'project': self.category.sodar_uuid}, + ) + + def test_get(self): + """Test SidebarContentAjaxView GET""" + good_users = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + ] + bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] + self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, bad_users, 403) + self.project.set_public() + self.assert_response(self.url, self.user_no_roles, 200) + + @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) + def test_get_anon(self): + """Test GET with anonymous access""" + self.project.set_public() + self.assert_response(self.url, self.anonymous, 200) + + def test_get_archive(self): + """Test SidebarContentAjaxView GET with archived project""" + self.project.set_archive() + good_users = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + ] + bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] + self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, bad_users, 403) + + def test_get_category(self): + """Test GET with category""" + good_users = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + ] + bad_users = [self.user_no_roles, self.anonymous] + self.assert_response(self.url_cat, good_users, 200) + self.assert_response(self.url_cat, bad_users, 403) + self.project.set_public() + self.assert_response(self.url_cat, self.user_no_roles, 200) + + @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) + def test_get_category_anon(self): + """Test GET with category and anonymous access""" + self.project.set_public() + self.assert_response(self.url_cat, self.anonymous, 200) + + +class TestUserDropdownContentAjaxView(ProjectPermissionTestBase): + """Tests for UserDropdownContentAjaxView permissions""" + + def setUp(self): + super().setUp() + self.url = reverse('projectroles:ajax_user_dropdown') + + def test_get(self): + """Test UserDropdownContentAjaxView GET""" + good_users = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + self.user_finder_cat, + self.user_no_roles, + ] + bad_users = [self.anonymous] + self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, bad_users, 403) + + class TestUserAjaxViews(ProjectPermissionTestBase): """Tests for user Ajax view permissions""" diff --git a/projectroles/tests/test_views_ajax.py b/projectroles/tests/test_views_ajax.py index 87b97164..6f039186 100644 --- a/projectroles/tests/test_views_ajax.py +++ b/projectroles/tests/test_views_ajax.py @@ -461,6 +461,431 @@ def test_project_star(self): self.assertEqual(star, False) +class TestSidebarContentAjaxView( + ProjectMixin, RoleAssignmentMixin, ViewTestBase +): + """Tests for SidebarContentAjaxView""" + + def setUp(self): + super().setUp() + self.category = self.make_project( + 'TestCategory', PROJECT_TYPE_CATEGORY, None + ) + self.owner_as_cat = self.make_assignment( + self.category, self.user, self.role_owner + ) + self.project = self.make_project( + 'TestProject', PROJECT_TYPE_PROJECT, self.category + ) + self.owner_as = self.make_assignment( + self.project, self.user, self.role_owner + ) + + def test_get(self): + """Test sidebar content retrieval""" + with self.login(self.user): + response = self.client.get( + reverse( + 'projectroles:ajax_sidebar', + kwargs={'project': self.project.sodar_uuid}, + ) + ) + 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': False, + }, + { + '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_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' + ) + 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') + with self.login(new_user): + response = self.client.get( + reverse( + 'projectroles:ajax_sidebar', + kwargs={'project': self.project.sodar_uuid}, + ) + ) + self.assertEqual(response.status_code, 403) + + +class TestUserDropdownContentAjaxView(ViewTestBase): + """Tests for UserDropdownContentAjaxView""" + + def setUp(self): + super().setUp() + self.user = self.make_user('user') + self.user.is_superuser = True + self.user.save() + self.reg_user = self.make_user('reg_user') + + def test_regular_user(self): + """Test UserDropdownContentAjaxView with regular user""" + with self.login(self.reg_user): + response = self.client.get( + reverse('projectroles:ajax_user_dropdown') + ) + self.assertEqual(response.status_code, 200) + expected = { + 'links': [ + { + 'name': 'appalerts', + 'url': '/alerts/app/list', + 'label': 'App Alerts', + 'icon': 'mdi:alert-octagram', + 'active': False, + }, + { + 'name': 'example_site_app', + 'url': '/examples/site/example', + 'label': 'Example Site App', + 'icon': 'mdi:rocket-launch-outline', + 'active': False, + }, + { + 'name': 'timeline_site', + 'url': '/timeline/site', + 'label': 'Site-Wide Events', + 'icon': 'mdi:clock-time-eight-outline', + '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': 'sign-out', + 'url': '/logout/', + 'label': 'Logout', + 'icon': 'mdi:logout-variant', + 'active': False, + }, + ] + } + self.assertEqual(response.json(), expected) + + def test_superuser(self): + """Test UserDropdownContentAjaxView with superuser""" + with self.login(self.user): + response = self.client.get( + reverse('projectroles:ajax_user_dropdown') + ) + 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': False, + }, + { + '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) + + 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' + ) + 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/urls.py b/projectroles/urls.py index ebb29720..aa0adaa7 100644 --- a/projectroles/urls.py +++ b/projectroles/urls.py @@ -179,6 +179,16 @@ view=views_ajax.ProjectStarringAjaxView.as_view(), name='ajax_star', ), + path( + route='ajax/sidebar/', + view=views_ajax.SidebarContentAjaxView.as_view(), + name='ajax_sidebar', + ), + path( + route='ajax/dropdown', + view=views_ajax.UserDropdownContentAjaxView.as_view(), + name='ajax_user_dropdown', + ), path( route='ajax/user/current', view=views_ajax.CurrentUserRetrieveAjaxView.as_view(), diff --git a/projectroles/utils.py b/projectroles/utils.py index efb06cf1..2ff39bba 100644 --- a/projectroles/utils.py +++ b/projectroles/utils.py @@ -115,29 +115,31 @@ 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 and 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 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 and 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', []) - ]: + if app_plugin.name.startswith(app_name) and ( + not url_name + or url_name in [u.name for u in getattr(app_plugin, 'urls', [])] + ): return True # HACK for remote site views, see issue #1336 if ( @@ -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 4c5e03c5..44704fda 100644 --- a/projectroles/views_ajax.py +++ b/projectroles/views_ajax.py @@ -27,7 +27,7 @@ ROLE_RANKING, ) from projectroles.plugins import get_active_plugins -from projectroles.utils import get_display_name +from projectroles.utils import get_display_name, AppLinkContent from projectroles.views import ProjectAccessMixin, User from projectroles.views_api import ( SODARAPIProjectPermission, @@ -431,6 +431,49 @@ def post(self, request, *args, **kwargs): return Response(1 if value else 0, status=200) +class SidebarContentAjaxView(SODARBaseProjectAjaxView): + """ + Ajax view for delivering sidebar content for specific projects. + All returned links are nor active by default. To get correct "active" + attribute for each of the links, you must provide the app_name as GET + parameter. The app_name refers to the current app name + (request.resolver_match.app_name). + """ + + permission_required = 'projectroles.view_project' + + def get(self, request, *args, **kwargs): + project = self.get_project() + app_name = request.GET.get('app_name') + # Get the content for the sidebar + app_link_content = AppLinkContent() + sidebar_links = app_link_content.get_project_app_links( + request.user, project, app_name=app_name + ) + return JsonResponse({'links': sidebar_links}) + + +class UserDropdownContentAjaxView(SODARBaseAjaxView): + """ + Ajax view for delivering user dropdown content for the navbar. + All returned links are nor active by default. To get correct "active" + attribute for each of the links, you must provide the app_name as GET + parameter. The app_name refers to the current app name + (request.resolver_match.app_name). + """ + + permission_classes = [IsAuthenticated] + + def get(self, request, *args, **kwargs): + app_name = request.GET.get('app_name') + # Get the content for the user dropdown + app_link_content = AppLinkContent() + user_dropdown_links = app_link_content.get_user_links( + request.user, app_name=app_name + ) + return JsonResponse({'links': user_dropdown_links}) + + class CurrentUserRetrieveAjaxView( SODARBaseAjaxMixin, CurrentUserRetrieveAPIView ):