Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions site/cds_rdm/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,25 @@ def query_filter(self, **kwargs):
return []


class EPWorkflowRequestCommunityManager(Generator):
"""Allows managers and owners of the community a committee approval request is for.

Request-level counterpart of ``EPWorkflowCommunityManager``: use it in request
policies. It resolves the request's topic record and applies the same community
rules.
"""

def needs(self, request=None, **kwargs):
"""Manager and owner needs of the topic record's enrolled community."""
if request is None or request.type.type_id != "committee-approval":
return []
return EPWorkflowCommunityManager().needs(record=request.topic.resolve())

def query_filter(self, **kwargs):
"""Not used for search filters."""
return []


COMMITTEE_APPROVAL_GRANT_ORIGIN_PREFIX = "committee-approval:"
COMMITTEE_APPROVAL_GRANT_PERMISSION = "committee-review"
COMMITTEE_APPROVAL_ACCESS_GRANT = AccessGrant(COMMITTEE_APPROVAL_GRANT_PERMISSION)
Expand Down
11 changes: 8 additions & 3 deletions site/cds_rdm/notifications/committee_approval.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
from invenio_users_resources.notifications.filters import UserPreferencesRecipientFilter
from invenio_users_resources.notifications.generators import UserRecipient

from .generators import GroupEmailRecipientGenerator
from .generators import (
EPWorkflowCommunityMembersRecipient,
GroupEmailRecipientGenerator,
)


class CommitteeApprovalNotificationBuilder(NotificationBuilder):
Expand Down Expand Up @@ -78,7 +81,7 @@ class CommitteeApprovalSubmitNotificationBuilder(CommitteeApprovalNotificationBu


class CommitteeApprovalAcceptNotificationBuilder(CommitteeApprovalNotificationBuilder):
"""Notify the submitter when their request is accepted."""
"""Notify the submitter and the community managers/owners when accepted."""

type: ClassVar[str] = f"{CommitteeApprovalNotificationBuilder.type}.accept"
context: ClassVar[list[ContextGenerator]] = [
Expand All @@ -87,11 +90,12 @@ class CommitteeApprovalAcceptNotificationBuilder(CommitteeApprovalNotificationBu
]
recipients: ClassVar[list[RecipientGenerator]] = [
UserRecipient("request.created_by"),
EPWorkflowCommunityMembersRecipient("request.topic"),
]


class CommitteeApprovalDeclineNotificationBuilder(CommitteeApprovalNotificationBuilder):
"""Notify the submitter when their request is declined."""
"""Notify the submitter and the community managers/owners when declined."""

type: ClassVar[str] = f"{CommitteeApprovalNotificationBuilder.type}.decline"
context: ClassVar[list[ContextGenerator]] = [
Expand All @@ -100,4 +104,5 @@ class CommitteeApprovalDeclineNotificationBuilder(CommitteeApprovalNotificationB
]
recipients: ClassVar[list[RecipientGenerator]] = [
UserRecipient("request.created_by"),
EPWorkflowCommunityMembersRecipient("request.topic"),
]
46 changes: 46 additions & 0 deletions site/cds_rdm/notifications/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@

from __future__ import annotations

from flask import current_app
from invenio_communities.notifications.generators import CommunityMembersRecipient
from invenio_notifications.models import Notification, Recipient
from invenio_notifications.services.generators import RecipientGenerator
from invenio_pidstore.models import PersistentIdentifier
from invenio_rdm_records.records.api import RDMRecord
from invenio_records.dictutils import dict_lookup


Expand Down Expand Up @@ -45,3 +49,45 @@ def __call__(
return recipients
recipients[name] = Recipient(data={"email": f"{name}@cern.ch"})
return recipients


class EPWorkflowCommunityMembersRecipient(RecipientGenerator):
"""Recipient generator for the managers and owners of the record's EP community.

Looks up the (resolved) record at ``key`` in the notification context and, if
its default community is enrolled in the committee approval workflow, adds the
members of that community with one of ``roles`` as recipients.
"""

def __init__(self, key: str, roles: tuple = ("manager", "owner")) -> None:
"""Initialise with the context key pointing to the record."""
self.key = key
self.roles = list(roles)

def _default_community_id(self, record):
"""Return the default community id of the record, if any."""
parent = getattr(record, "parent", None)
if parent is None:
pid = PersistentIdentifier.get("recid", record["id"])
parent = RDMRecord.get_record(pid.object_uuid).parent
return (parent.get("communities") or {}).get("default")

def __call__(
self,
notification: Notification,
recipients: dict[str, Recipient],
) -> dict[str, Recipient]:
"""Add the community managers and owners to ``recipients``."""
record = dict_lookup(notification.context, self.key)
community_id = self._default_community_id(record)
enrolled = current_app.config.get("CDS_COMMITTEE_APPROVAL_COMMUNITIES", {})
if not community_id or community_id not in enrolled:
return recipients

notification.context["_ep_community"] = {"id": community_id}
try:
return CommunityMembersRecipient("_ep_community", roles=self.roles)(
notification, recipients
)
finally:
notification.context.pop("_ep_community", None)
13 changes: 13 additions & 0 deletions site/cds_rdm/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
AuthenticatedRegularUser,
CERNEmailsGroups,
CommitteeRefereeVersionGrant,
EPWorkflowCommunityManager,
EPWorkflowRequestCommunityManager,
HarvesterCurator,
InspireHarvester,
Librarian,
Expand Down Expand Up @@ -120,6 +122,13 @@ class CDSRDMRecordPermissionPolicy(RDMRecordPermissionPolicy):
"can_preview", exclude_generators=(RecordCommunitiesAction,)
)

# Who gets read access to a committee approval request through its topic record:
# managers and owners of the record's enrolled community.
can_view_committee_approval_request = [
EPWorkflowCommunityManager(),
SystemProcess(),
]

can_manage_clc_sync = [Librarian(), Administration(), SystemProcess()]

can_modify_locked_files = [
Expand Down Expand Up @@ -160,6 +169,10 @@ class CDSRequestsPermissionPolicy(RDMRequestsPermissionPolicy):

can_reply_comment = RDMRequestsPermissionPolicy.can_read

can_action_cancel = RDMRequestsPermissionPolicy.can_action_cancel + [
EPWorkflowRequestCommunityManager()
]


class FileModificationHarvesterPolicy(BasePolicy):
"""File modification policy which allows the INSPIRE harvester to modify files of all records."""
Expand Down
6 changes: 6 additions & 0 deletions site/cds_rdm/requests/committee_approval.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ class CommitteeApprovalRequest(RDMBaseRequest):
allowed_receiver_ref_types: Final[list] = ["group"]
allowed_topic_ref_types: Final[list] = ["record"]

# Enabling read permission via ``can_view_committee_approval_request``.
needs_context: Final[dict] = {
"record_permission": "view_committee_approval_request"
}
resolve_topic_needs: Final[bool] = True

# Payload fields collected from the submission form.
payload_schema: Final[dict] = {
# Captured at submission for request-page and email display.
Expand Down
2 changes: 2 additions & 0 deletions site/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
from cds_rdm.permissions import (
CDSCommunitiesPermissionPolicy,
CDSRDMRecordPermissionPolicy,
CDSRequestsPermissionPolicy,
lock_edit_record_published_files,
)
from cds_rdm.schemes import is_cds, is_inspire_author
Expand Down Expand Up @@ -201,6 +202,7 @@ def app_config(app_config, mock_datacite_client, mock_crossref_client):
app_config["ACCOUNTS_USER_PROFILE_SCHEMA"] = CERNUserProfileSchema()
app_config["COMMUNITIES_PERMISSION_POLICY"] = CDSCommunitiesPermissionPolicy
app_config["RDM_PERMISSION_POLICY"] = CDSRDMRecordPermissionPolicy
app_config["REQUESTS_PERMISSION_POLICY"] = CDSRequestsPermissionPolicy
app_config["COMMUNITIES_ALLOW_RESTRICTED"] = True
app_config["CDS_GROUPS_ALLOW_CREATE_COMMUNITIES"] = [
"group-allowed-create-communities"
Expand Down
Loading
Loading