From e809807b6fd16f1eb1c28c7a62d9d7683fa81cc1 Mon Sep 17 00:00:00 2001 From: Mikko Nieminen Date: Mon, 30 Sep 2024 10:41:49 +0200 Subject: [PATCH 01/14] fix SODAR_API setting requirement in tests (#1495) --- CHANGELOG.rst | 10 ++++++++++ config/settings/base.py | 6 +++--- docs/source/conf.py | 2 +- docs/source/major_changes.rst | 9 +++++++++ projectroles/tests/test_views_api.py | 8 ++++++-- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9424f223..6e4eb85d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,16 @@ Changelog for the **SODAR Core** Django app package. Loosely follows the `Keep a Changelog `_ guidelines. +Unreleased +========== + +Fixed +----- + +- **Projectroles** + - Deprecated ``SODAR_API_*`` settings required in tests (#1495) + + v1.0.2 (2024-09-09) =================== diff --git a/config/settings/base.py b/config/settings/base.py index fb26cf2f..d7eb37e3 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -559,9 +559,9 @@ def set_logging(level=None): # SODAR API settings # DEPRECATED: To be removed in SODAR Core v1.1 (see #1401) -SODAR_API_DEFAULT_VERSION = '0.1' -SODAR_API_ALLOWED_VERSIONS = [SODAR_API_DEFAULT_VERSION] -SODAR_API_MEDIA_TYPE = 'application/your.application+json' +# SODAR_API_DEFAULT_VERSION = '0.1' +# SODAR_API_ALLOWED_VERSIONS = [SODAR_API_DEFAULT_VERSION] +# SODAR_API_MEDIA_TYPE = 'application/your.application+json' # SODAR API host URL SODAR_API_DEFAULT_HOST = env.url( 'SODAR_API_DEFAULT_HOST', 'http://0.0.0.0:8000' diff --git a/docs/source/conf.py b/docs/source/conf.py index 88fae263..c8445fbe 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -29,7 +29,7 @@ # The short X.Y version version = '1.0' # The full version, including alpha/beta/rc tags -release = '1.0.2' +release = '1.0.3-WIP' # -- General configuration --------------------------------------------------- diff --git a/docs/source/major_changes.rst b/docs/source/major_changes.rst index 8c14a719..5d632771 100644 --- a/docs/source/major_changes.rst +++ b/docs/source/major_changes.rst @@ -10,6 +10,15 @@ older SODAR Core version. For a complete list of changes in current and previous releases, see the :ref:`full changelog`. +v1.0.3 (WIP) +************ + +Release Highlights +================== + +- Fix requiring deprecated SODAR API settings in tests + + v1.0.2 (2024-09-09) ******************* diff --git a/projectroles/tests/test_views_api.py b/projectroles/tests/test_views_api.py index 99ab2114..6de10970 100644 --- a/projectroles/tests/test_views_api.py +++ b/projectroles/tests/test_views_api.py @@ -50,6 +50,10 @@ REMOTE_SITE_SECRET, ) from projectroles.utils import build_secret +from projectroles.views_api import ( + SODAR_API_MEDIA_TYPE, # TODO: Remove in v1.1 (see #1401) + SODAR_API_DEFAULT_VERSION, # TODO: Remove in v1.1 (see #1401) +) app_settings = AppSettingAPI() @@ -129,8 +133,8 @@ class SODARAPIViewTestMixin(SerializedObjectMixin): # Default API header parameters are for external SODAR site APIs # DEPRECATED: To be removed in SODAR Core v1.1 (see #1401) # Instead, provide a media type and version specific to your app - media_type = settings.SODAR_API_MEDIA_TYPE - api_version = settings.SODAR_API_DEFAULT_VERSION + media_type = SODAR_API_MEDIA_TYPE + api_version = SODAR_API_DEFAULT_VERSION # Copied from Knox tests @classmethod From bc219942ec54eec1f660f810a8b1ca8294c3ce95 Mon Sep 17 00:00:00 2001 From: Mikko Nieminen Date: Thu, 10 Oct 2024 10:10:13 +0200 Subject: [PATCH 02/14] add auth type in userprofile (#1500) --- CHANGELOG.rst | 6 ++++++ docs/source/major_changes.rst | 1 + userprofile/templates/userprofile/detail.html | 2 ++ 3 files changed, 9 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6e4eb85d..0e74af84 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,12 @@ Changelog for the **SODAR Core** Django app package. Loosely follows the Unreleased ========== +Added +----- + +- **Userprofile** + - Authentication type in user details (#1500) + Fixed ----- diff --git a/docs/source/major_changes.rst b/docs/source/major_changes.rst index 5d632771..868a7b06 100644 --- a/docs/source/major_changes.rst +++ b/docs/source/major_changes.rst @@ -16,6 +16,7 @@ v1.0.3 (WIP) Release Highlights ================== +- Add auth type in user profile details card - Fix requiring deprecated SODAR API settings in tests diff --git a/userprofile/templates/userprofile/detail.html b/userprofile/templates/userprofile/detail.html index 7a7cd70f..dd8f0020 100644 --- a/userprofile/templates/userprofile/detail.html +++ b/userprofile/templates/userprofile/detail.html @@ -73,6 +73,8 @@

{{ request.user.sodar_uuid }}
Date Joined
{{ request.user.date_joined | date:'Y-m-d H:i' }}
+
Authentication
+
{{ request.user.get_auth_type }}
From 4bce71e511f32c01d854dc103263fe34f8bea6fa Mon Sep 17 00:00:00 2001 From: Mikko Nieminen Date: Thu, 10 Oct 2024 13:41:24 +0200 Subject: [PATCH 03/14] update default oidc login template (#1503) --- CHANGELOG.rst | 6 ++++++ example_site/templates/include/_login_oidc.html | 2 +- projectroles/templates/projectroles/_login_oidc.html | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0e74af84..06eebd92 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,12 @@ Added - **Userprofile** - Authentication type in user details (#1500) +Changed +------- + +- **Projectroles** + - Update default OIDC login button template (#1503) + Fixed ----- diff --git a/example_site/templates/include/_login_oidc.html b/example_site/templates/include/_login_oidc.html index e2bb3b2c..ca8eaec8 100644 --- a/example_site/templates/include/_login_oidc.html +++ b/example_site/templates/include/_login_oidc.html @@ -3,5 +3,5 @@ - OpenID Connect Login + Single Sign-On Login diff --git a/projectroles/templates/projectroles/_login_oidc.html b/projectroles/templates/projectroles/_login_oidc.html index 2c31f8da..c6e7b19e 100644 --- a/projectroles/templates/projectroles/_login_oidc.html +++ b/projectroles/templates/projectroles/_login_oidc.html @@ -9,6 +9,6 @@ - OpenID Connect Login + Single Sign-On Login {% endif %} From f5be1ccb68b8ecaa38e46d84501ca0a58570deda Mon Sep 17 00:00:00 2001 From: Mikko Nieminen Date: Mon, 21 Oct 2024 12:22:42 +0200 Subject: [PATCH 04/14] add timeline user count stats (#1504), add plugin tests (#1506) --- CHANGELOG.rst | 3 + docs/source/major_changes.rst | 1 + timeline/plugins.py | 44 +++-- timeline/tests/test_plugins.py | 316 +++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+), 17 deletions(-) create mode 100644 timeline/tests/test_plugins.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 06eebd92..617c4104 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,9 @@ Unreleased Added ----- +- **Timeline** + - User count in siteinfo stats (#1504) + - Plugin tests (#1506) - **Userprofile** - Authentication type in user details (#1500) diff --git a/docs/source/major_changes.rst b/docs/source/major_changes.rst index 868a7b06..97fdbd6e 100644 --- a/docs/source/major_changes.rst +++ b/docs/source/major_changes.rst @@ -17,6 +17,7 @@ Release Highlights ================== - Add auth type in user profile details card +- Add user count in timeline siteinfo statistics - Fix requiring deprecated SODAR API settings in tests diff --git a/timeline/plugins.py b/timeline/plugins.py index 643e070c..472e5527 100644 --- a/timeline/plugins.py +++ b/timeline/plugins.py @@ -15,6 +15,10 @@ from timeline.urls import urls_ui_project, urls_ui_site, urls_ui_admin +# Local constants +STATS_DESC_USER_COUNT = 'Amount of users who have initiated events' + + class ProjectAppPlugin(ProjectAppPluginPoint): """Plugin for registering app with Projectroles""" @@ -69,29 +73,35 @@ class ProjectAppPlugin(ProjectAppPluginPoint): #: Names of plugin specific Django settings to display in siteinfo info_settings = ['TIMELINE_PAGINATION', 'TIMELINE_SEARCH_LIMIT'] + @classmethod + def _check_permission(cls, user, event): + """Check if user has permission to view event""" + if event.project and event.classified: + return user.has_perm( + 'timeline.view_classified_event', event.project + ) + elif event.project: + return user.has_perm('timeline.view_timeline', event.project) + elif event.classified: + return user.has_perm('timeline.view_classified_site_event') + return user.has_perm('timeline.view_site_timeline') + def get_statistics(self): return { 'event_count': { 'label': 'Events', 'value': TimelineEvent.objects.all().count(), - } + }, + 'user_count': { + 'label': 'Users', + 'description': STATS_DESC_USER_COUNT, + 'value': TimelineEvent.objects.exclude(user__isnull=True) + .values('user') + .distinct() + .count(), + }, } - def check_permission(self, user, event): - """Check if user has permission to view event""" - if event.project is not None: - if event.classified: - return user.has_perm( - 'timeline.view_classified_event', event.project - ) - else: - return user.has_perm('timeline.view_timeline', event.project) - else: - if event.classified: - return user.has_perm('timeline.view_classified_site_event') - else: - return user.has_perm('timeline.view_site_timeline') - def search(self, search_terms, user, search_type=None, keywords=None): """ Return app items based on one or more search terms, user, optional type @@ -107,7 +117,7 @@ def search(self, search_terms, user, search_type=None, keywords=None): items = [] if not search_type or search_type == 'timeline': events = list(TimelineEvent.objects.find(search_terms, keywords)) - items = [e for e in events if self.check_permission(user, e)] + items = [e for e in events if self._check_permission(user, e)] ret = PluginSearchResult( category='all', title='Timeline Events', diff --git a/timeline/tests/test_plugins.py b/timeline/tests/test_plugins.py new file mode 100644 index 00000000..b606c52f --- /dev/null +++ b/timeline/tests/test_plugins.py @@ -0,0 +1,316 @@ +"""Plugin tests for the timeline app""" + +from test_plus.test import TestCase + +# Projectroles dependency +from projectroles.models import SODAR_CONSTANTS +from projectroles.plugins import ( + get_backend_api, + ProjectAppPluginPoint, + BackendPluginPoint, + SiteAppPluginPoint, + PluginSearchResult, +) +from projectroles.tests.test_models import ( + ProjectMixin, + RoleMixin, + RoleAssignmentMixin, +) + +from timeline.api import TimelineAPI +from timeline.plugins import STATS_DESC_USER_COUNT + +# from timeline.tests.test_models import TimelineEventMixin +from timeline.urls import urls_ui_project, urls_ui_site, urls_ui_admin + + +# SODAR constants +PROJECT_ROLE_OWNER = SODAR_CONSTANTS['PROJECT_ROLE_OWNER'] +PROJECT_ROLE_DELEGATE = SODAR_CONSTANTS['PROJECT_ROLE_DELEGATE'] +PROJECT_ROLE_CONTRIBUTOR = SODAR_CONSTANTS['PROJECT_ROLE_CONTRIBUTOR'] +PROJECT_ROLE_GUEST = SODAR_CONSTANTS['PROJECT_ROLE_GUEST'] +PROJECT_TYPE_CATEGORY = SODAR_CONSTANTS['PROJECT_TYPE_CATEGORY'] +PROJECT_TYPE_PROJECT = SODAR_CONSTANTS['PROJECT_TYPE_PROJECT'] + +# Local constants +PROJECT_PLUGIN_NAME = 'timeline' +PROJECT_PLUGIN_TITLE = 'Timeline' +BACKEND_PLUGIN_NAME = 'timeline_backend' +BACKEND_PLUGIN_TITLE = 'Timeline Backend' +SITE_PLUGIN_NAME = 'timeline_site' +SITE_PLUGIN_TITLE = 'Site-Wide Events' +ADMIN_PLUGIN_NAME = 'timeline_site_admin' +ADMIN_PLUGIN_TITLE = 'All Timeline Events' +SEARCH_TERMS = ['test'] +SEARCH_RET_CAT = 'all' +SEARCH_RET_TITLE = 'Timeline Events' +SEARCH_RET_TYPES = ['timeline'] + + +class TimelinePluginTestBase( + ProjectMixin, + RoleMixin, + RoleAssignmentMixin, + TestCase, +): + """Base class for timeline plugin tests""" + + def setUp(self): + # Init users + self.user = self.make_user('superuser') + self.user.is_staff = True + self.user.is_superuser = True + self.user.save() + self.user_owner = self.make_user('user_owner') + # Init roles + self.init_roles() + # Init category, project and roles + self.category = self.make_project( + 'TestCategory', PROJECT_TYPE_CATEGORY, None + ) + self.owner_as = self.make_assignment( + self.category, self.user_owner, self.role_owner + ) + self.project = self.make_project( + 'TestProject', PROJECT_TYPE_PROJECT, self.category + ) + self.owner_as = self.make_assignment( + self.project, self.user_owner, self.role_owner + ) + + +class TestProjectAppPlugin(TimelinePluginTestBase): + """Tests for timeline ProjectAppPlugin""" + + def setUp(self): + super().setUp() + self.plugin = ProjectAppPluginPoint.get_plugin(PROJECT_PLUGIN_NAME) + self.event_kw = { + 'project': self.project, + 'app_name': 'projectroles', + 'user': self.user, + 'event_name': 'test_event', + 'description': 'description', + } + self.timeline = get_backend_api('timeline_backend') + + def test_plugin_retrieval(self): + """Test retrieving ProjectAppPlugin""" + self.assertIsNotNone(self.plugin) + self.assertEqual(self.plugin.get_model().name, PROJECT_PLUGIN_NAME) + self.assertEqual(self.plugin.name, PROJECT_PLUGIN_NAME) + self.assertEqual(self.plugin.get_model().title, PROJECT_PLUGIN_TITLE) + self.assertEqual(self.plugin.urls, urls_ui_project) + + def test_get_statistics(self): + """Test get_statistics() with no events""" + expected = { + 'event_count': { + 'label': 'Events', + 'value': 0, + }, + 'user_count': { + 'label': 'Users', + 'description': STATS_DESC_USER_COUNT, + 'value': 0, + }, + } + self.assertEqual(self.plugin.get_statistics(), expected) + + def test_get_statistics_events(self): + """Test get_statistics() with existing events""" + self.timeline.add_event(**self.event_kw) + self.event_kw['user'] = self.user_owner + self.timeline.add_event(**self.event_kw) + ret = self.plugin.get_statistics() + self.assertEqual(ret['event_count']['value'], 2) + self.assertEqual(ret['user_count']['value'], 2) + + def test_get_statistics_same_user(self): + """Test get_statistics() with existing events by same user""" + self.timeline.add_event(**self.event_kw) + self.timeline.add_event(**self.event_kw) + ret = self.plugin.get_statistics() + self.assertEqual(ret['event_count']['value'], 2) + self.assertEqual(ret['user_count']['value'], 1) + + def test_get_statistics_no_user(self): + """Test get_statistics() with existing events including no user""" + self.timeline.add_event(**self.event_kw) + self.event_kw['user'] = None + self.timeline.add_event(**self.event_kw) + ret = self.plugin.get_statistics() + self.assertEqual(ret['event_count']['value'], 2) + self.assertEqual(ret['user_count']['value'], 1) + + def test_search(self): + """Test search() with no events""" + ret = self.plugin.search(SEARCH_TERMS, self.user) + self.assertEqual(len(ret), 1) + self.assertIsInstance(ret[0], PluginSearchResult) + self.assertEqual(ret[0].category, SEARCH_RET_CAT) + self.assertEqual(ret[0].title, SEARCH_RET_TITLE) + self.assertEqual(ret[0].search_types, SEARCH_RET_TYPES) + self.assertEqual(ret[0].items, []) + + def test_search_events(self): + """Test search() with events""" + event = self.timeline.add_event(**self.event_kw) + self.event_kw['user'] = self.user_owner + event2 = self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, self.user) + self.assertEqual(len(ret), 1) + self.assertIsInstance(ret[0].items, list) + self.assertEqual(len(ret[0].items), 2) + self.assertEqual(ret[0].items[0], event2) + self.assertEqual(ret[0].items[1], event) + + def test_search_invalid_terms(self): + """Test search() with invalid terms""" + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(['yuyaeQu7ma6aeFi2'], self.user) + self.assertEqual(len(ret[0].items), 0) + + def test_search_mixed_terms(self): + """Test search() with valid and invalid terms""" + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS + ['yuyaeQu7ma6aeFi2'], self.user) + self.assertEqual(len(ret[0].items), 1) + + def test_search_no_perms(self): + """Test search() as user with no permissions""" + # Create user with no permissions to self.project + user_no_perms = self.make_user('user_no_perms') + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, user_no_perms) + self.assertEqual(len(ret[0].items), 0) + + def test_search_mixed_perms(self): + """Test search() as user with mixed permissions""" + user_new = self.make_user('user_new') + project_new = self.make_project( + 'TestProject2', PROJECT_TYPE_PROJECT, self.category + ) + self.make_assignment(project_new, self.user_owner, self.role_owner) + self.make_assignment(project_new, user_new, self.role_contributor) + self.timeline.add_event(**self.event_kw) + self.event_kw['project'] = project_new + project_event_new = self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, user_new) + self.assertEqual(len(ret[0].items), 1) + self.assertEqual(ret[0].items[0], project_event_new) + ret = self.plugin.search(SEARCH_TERMS, self.user_owner) + self.assertEqual(len(ret[0].items), 2) + + def test_search_project_classified_owner(self): + """Test search() with classified project event as owner""" + self.event_kw['classified'] = True + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, self.user_owner) + self.assertEqual(len(ret[0].items), 1) + + def test_search_project_classified_contributor(self): + """Test search() with classified project event as contributor""" + user_contrib = self.make_user('user_contrib') + self.make_assignment(self.project, user_contrib, self.role_contributor) + self.event_kw['classified'] = True + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, user_contrib) + self.assertEqual(len(ret[0].items), 0) + + def test_search_site_superuser(self): + """Test search() with site event as superuser""" + self.event_kw['project'] = None + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, self.user) + self.assertEqual(len(ret[0].items), 1) + + def test_search_site_regular_user(self): + """Test search() with site event as regular user""" + self.event_kw['project'] = None + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, self.user_owner) + self.assertEqual(len(ret[0].items), 1) + + def test_search_site_classified_superuser(self): + """Test search() with classified site event as superuser""" + self.event_kw['project'] = None + self.event_kw['classified'] = True + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, self.user) + self.assertEqual(len(ret[0].items), 1) + + def test_search_site_classified_regular_user(self): + """Test search() with classified site event as regular user""" + self.event_kw['project'] = None + self.event_kw['classified'] = True + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search(SEARCH_TERMS, self.user_owner) + self.assertEqual(len(ret[0].items), 0) + + def test_search_type(self): + """Test search() with defined search type""" + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search( + SEARCH_TERMS, self.user, search_type='timeline' + ) + self.assertEqual(len(ret[0].items), 1) + + def test_search_type_invalid(self): + """Test search() with invalid search type""" + self.timeline.add_event(**self.event_kw) + ret = self.plugin.search( + SEARCH_TERMS, self.user, search_type='raTho0Oo' + ) + self.assertEqual(len(ret[0].items), 0) + + +class TestBackendPlugin(TimelinePluginTestBase): + """Tests for timeline BackendPlugin""" + + def setUp(self): + super().setUp() + self.plugin = BackendPluginPoint.get_plugin(BACKEND_PLUGIN_NAME) + + def test_plugin_retrieval(self): + """Test retrieving BackendPlugin""" + self.assertIsNotNone(self.plugin) + self.assertEqual(self.plugin.get_model().name, BACKEND_PLUGIN_NAME) + self.assertEqual(self.plugin.name, BACKEND_PLUGIN_NAME) + self.assertEqual(self.plugin.get_model().title, BACKEND_PLUGIN_TITLE) + + def test_get_api(self): + """Test get_api()""" + self.assertIsInstance(self.plugin.get_api(), TimelineAPI) + + +class TestSiteAppPlugin(TimelinePluginTestBase): + """Tests for timeline SiteAppPlugin""" + + def setUp(self): + super().setUp() + self.plugin = SiteAppPluginPoint.get_plugin(SITE_PLUGIN_NAME) + + def test_plugin_retrieval(self): + """Test retrieving SiteAppPlugin""" + self.assertIsNotNone(self.plugin) + self.assertEqual(self.plugin.get_model().name, SITE_PLUGIN_NAME) + self.assertEqual(self.plugin.name, SITE_PLUGIN_NAME) + self.assertEqual(self.plugin.get_model().title, SITE_PLUGIN_TITLE) + self.assertEqual(self.plugin.urls, urls_ui_site) + + +class TestAdminSiteAppPlugin(TimelinePluginTestBase): + """Tests for timeline AdminSiteAppPlugin""" + + def setUp(self): + super().setUp() + self.plugin = SiteAppPluginPoint.get_plugin(ADMIN_PLUGIN_NAME) + + def test_plugin_retrieval(self): + """Test retrieving AdminSiteAppPlugin""" + self.assertIsNotNone(self.plugin) + self.assertEqual(self.plugin.get_model().name, ADMIN_PLUGIN_NAME) + self.assertEqual(self.plugin.name, ADMIN_PLUGIN_NAME) + self.assertEqual(self.plugin.get_model().title, ADMIN_PLUGIN_TITLE) + self.assertEqual(self.plugin.urls, urls_ui_admin) From 472277ced846c705504e019f8b212ed5934cecdf Mon Sep 17 00:00:00 2001 From: Mikko Nieminen Date: Mon, 4 Nov 2024 12:09:19 +0100 Subject: [PATCH 05/14] fix ProjectInviteCreateView redirect in category (#1510) --- CHANGELOG.rst | 1 + docs/source/major_changes.rst | 1 + projectroles/tests/test_views.py | 24 ++++++++++++++++++++++-- projectroles/urls.py | 6 ++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 617c4104..15086a41 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -28,6 +28,7 @@ Fixed - **Projectroles** - Deprecated ``SODAR_API_*`` settings required in tests (#1495) + - Add workaround to ``ProjectInviteCreateView`` returning 404 with category and query string (#1510) v1.0.2 (2024-09-09) diff --git a/docs/source/major_changes.rst b/docs/source/major_changes.rst index 97fdbd6e..79d57b12 100644 --- a/docs/source/major_changes.rst +++ b/docs/source/major_changes.rst @@ -18,6 +18,7 @@ Release Highlights - Add auth type in user profile details card - Add user count in timeline siteinfo statistics +- Fix invite create view redirect failing in categories - Fix requiring deprecated SODAR API settings in tests diff --git a/projectroles/tests/test_views.py b/projectroles/tests/test_views.py index 5a94f0ea..da5d87de 100644 --- a/projectroles/tests/test_views.py +++ b/projectroles/tests/test_views.py @@ -4118,8 +4118,8 @@ def test_get(self): form.fields['role'].choices, ) - def test_get_from_roleassignment(self): - """Test GET with forwarded values from RoleAssignment Form""" + def test_get_query_string(self): + """Test GET with query string from RoleAssignment form""" data = { 'e': 'test@example.com', 'r': self.role_contributor.pk, @@ -4140,6 +4140,26 @@ def test_get_from_roleassignment(self): ) self.assertEqual(form.fields['email'].initial, 'test@example.com') + def test_get_query_string_category(self): + """Test GET with query string in category""" + category = self.make_project( + 'TestCategory', PROJECT_TYPE_CATEGORY, None + ) + self.make_assignment(category, self.user, self.role_owner) + data = { + 'e': 'test@example.com', + 'r': self.role_contributor.pk, + } + with self.login(self.user): + response = self.client.get( + reverse( + 'projectroles:invite_create', + kwargs={'project': category.sodar_uuid}, + ), + data, + ) + self.assertEqual(response.status_code, 200) + def test_get_not_found(self): """Test GET with invalid project UUID""" with self.login(self.user): diff --git a/projectroles/urls.py b/projectroles/urls.py index 407f109d..18d27dc1 100644 --- a/projectroles/urls.py +++ b/projectroles/urls.py @@ -89,6 +89,12 @@ view=views.ProjectInviteCreateView.as_view(), name='invite_create', ), + # Workaround for issue #1510 + path( + route='invites/create/?e=&r=', + view=views.ProjectInviteCreateView.as_view(), + name='invite_create', + ), path( route='invites/accept/', view=views.ProjectInviteAcceptView.as_view(), From 5865aea7f70ed6894b7c28fc4b28d93fe924e7b7 Mon Sep 17 00:00:00 2001 From: Mikko Nieminen Date: Tue, 12 Nov 2024 14:31:23 +0100 Subject: [PATCH 06/14] add member list finder role info link (#1511) --- CHANGELOG.rst | 2 ++ docs/source/major_changes.rst | 2 ++ projectroles/templates/projectroles/project_roles.html | 4 ++++ projectroles/tests/test_views.py | 7 +++++++ projectroles/views.py | 8 ++++++++ 5 files changed, 23 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 15086a41..ae5c8158 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,8 @@ Unreleased Added ----- +- **Projectroles** + - Info link for finder role in ``ProjectRoleView`` (#1511) - **Timeline** - User count in siteinfo stats (#1504) - Plugin tests (#1506) diff --git a/docs/source/major_changes.rst b/docs/source/major_changes.rst index 79d57b12..b8023a3d 100644 --- a/docs/source/major_changes.rst +++ b/docs/source/major_changes.rst @@ -18,8 +18,10 @@ Release Highlights - Add auth type in user profile details card - Add user count in timeline siteinfo statistics +- Add finder role info link in member list - Fix invite create view redirect failing in categories - Fix requiring deprecated SODAR API settings in tests +- General bug fixes and minor updates v1.0.2 (2024-09-09) diff --git a/projectroles/templates/projectroles/project_roles.html b/projectroles/templates/projectroles/project_roles.html index d91d7557..6b824cdc 100644 --- a/projectroles/templates/projectroles/project_roles.html +++ b/projectroles/templates/projectroles/project_roles.html @@ -32,6 +32,7 @@ {% block projectroles_extend %} {% get_role_perms project request.user as role_perms %} +{% get_info_link finder_info as finder_info_link %}

@@ -87,6 +88,9 @@

{% else %} {{ role_display }} + {% if role_as.role.name == 'project finder' %} + {{ finder_info_link | safe }} + {% endif %} {% endif %} diff --git a/projectroles/tests/test_views.py b/projectroles/tests/test_views.py index da5d87de..24b677ad 100644 --- a/projectroles/tests/test_views.py +++ b/projectroles/tests/test_views.py @@ -71,6 +71,7 @@ FORM_INVALID_MSG, PROJECT_WELCOME_MSG, USER_PROFILE_LDAP_MSG, + ROLE_FINDER_INFO, INVITE_LDAP_LOCAL_VIEW_MSG, INVITE_LOCAL_NOT_ALLOWED_MSG, INVITE_LOGGED_IN_ACCEPT_MSG, @@ -2617,6 +2618,12 @@ def test_get(self): [model_to_dict(m) for m in response.context['roles']], expected ) self.assertNotIn('remote_role_url', response.context) + self.assertEqual( + response.context['finder_info'], + ROLE_FINDER_INFO.format( + categories='categories', projects='projects' + ), + ) def test_get_not_found(self): """Test GET view with invalid project UUID""" diff --git a/projectroles/views.py b/projectroles/views.py index a1ece660..f979949a 100644 --- a/projectroles/views.py +++ b/projectroles/views.py @@ -122,6 +122,10 @@ ) ROLE_CREATE_MSG = 'Membership granted with the role of "{role}".' ROLE_UPDATE_MSG = 'Member role changed to "{role}".' +ROLE_FINDER_INFO = ( + 'User can see nested {categories} and {projects}, but can not access them ' + 'without having a role explicitly assigned.' +) SEARCH_DICT_DEPRECATE_MSG = ( 'Results from search() as a dict have been deprecated and support will be ' 'removed in v1.1. Provide results as a list of PluginSearchResult objects ' @@ -1719,6 +1723,10 @@ def get_context_data(self, *args, **kwargs): ] = project.get_source_site().url + reverse( 'projectroles:roles', kwargs={'project': project.sodar_uuid} ) + context['finder_info'] = ROLE_FINDER_INFO.format( + categories=get_display_name(PROJECT_TYPE_CATEGORY, plural=True), + projects=get_display_name(PROJECT_TYPE_PROJECT, plural=True), + ) return context From 56b4bbb1b158b8bc3445da892db6a4a625701652 Mon Sep 17 00:00:00 2001 From: Mikko Nieminen Date: Tue, 12 Nov 2024 14:43:23 +0100 Subject: [PATCH 07/14] fix ProjectRoleView tour help attachments (#1512) --- CHANGELOG.rst | 1 + .../projectroles/_project_role_dropdown.html | 3 ++- .../projectroles/_project_role_dropdown_owner.html | 3 ++- .../templates/projectroles/_project_role_ops.html | 2 +- .../templates/projectroles/project_roles.html | 12 ++++++------ 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ae5c8158..b5fa5f97 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,6 +31,7 @@ Fixed - **Projectroles** - Deprecated ``SODAR_API_*`` settings required in tests (#1495) - Add workaround to ``ProjectInviteCreateView`` returning 404 with category and query string (#1510) + - Broken tour help attachments in ``ProjectRoleView`` (#1512) v1.0.2 (2024-09-09) diff --git a/projectroles/templates/projectroles/_project_role_dropdown.html b/projectroles/templates/projectroles/_project_role_dropdown.html index 2b7914eb..c2b811e4 100644 --- a/projectroles/templates/projectroles/_project_role_dropdown.html +++ b/projectroles/templates/projectroles/_project_role_dropdown.html @@ -2,7 +2,8 @@ {% load projectroles_common_tags %} {% load projectroles_role_tags %} -