From ec2e3cd52af320e08447a0ab971f722830c5845a Mon Sep 17 00:00:00 2001 From: Mohammed Taha Khan Date: Mon, 13 Jul 2026 13:36:32 +0200 Subject: [PATCH] feat: genericise EP approval UI labels via config --- .../record_details/CommitteeApproval.js | 37 +++++++++++++++---- .../record_details/NewVersionButton.js | 33 +++++++++++------ .../overridableRegistry/mapping.js | 18 ++++++++- invenio.cfg | 11 ++++-- site/cds_rdm/requests/committee_approval.py | 29 +++++++++++++++ .../requests/committee_approval_state.py | 20 +++++++++- site/cds_rdm/requests/views.py | 10 +++++ .../committee-approval-request.submit.jinja | 16 ++++---- 8 files changed, 142 insertions(+), 32 deletions(-) diff --git a/assets/js/components/record_details/CommitteeApproval.js b/assets/js/components/record_details/CommitteeApproval.js index 27fea035..c58ea78c 100644 --- a/assets/js/components/record_details/CommitteeApproval.js +++ b/assets/js/components/record_details/CommitteeApproval.js @@ -65,6 +65,7 @@ export class CommitteeApprovalManageSection extends Component { // Public EP-approved record — show a compact provenance note. if (committeeApproval.is_public_approved_record) { const { + approval_label: approvalLabel, approved_report_number: pubRn, draft_record_id: draftRecordId, can_view_reviewed_version: canViewReviewedVersion, @@ -73,7 +74,11 @@ export class CommitteeApprovalManageSection extends Component { return ( -
{i18next.t("Approval request workflow")}
+
+ {i18next.t("{{approvalLabel}} request workflow", { + approvalLabel, + })} +

@@ -104,6 +109,8 @@ export class CommitteeApprovalManageSection extends Component { const { can_submit: canSubmit, can_create_public: canCreatePublicFlag, + approval_label: approvalLabel, + committee_name: committeeName, open_request: openRequest, approved_report_number: approvedReportNumber, receiver_group: receiverGroup, @@ -151,7 +158,11 @@ export class CommitteeApprovalManageSection extends Component { return ( -

{i18next.t("Approval request workflow")}
+
+ {i18next.t("{{approvalLabel}} request workflow", { + approvalLabel, + })} +
@@ -228,7 +244,11 @@ export class CommitteeApprovalManageSection extends Component {
- {i18next.t("EP Board review")} + + {i18next.t("{{committeeName}} review", { + committeeName, + })} + {step2Completed ? ( requestLink ? ( @@ -245,7 +265,10 @@ export class CommitteeApprovalManageSection extends Component { ) ) : ( i18next.t( - "The EP secretariat will review the submission." + "The {{committeeName}} will review the submission.", + { + committeeName, + } ) )} @@ -295,9 +318,7 @@ export class CommitteeApprovalManageSection extends Component { i18next.t("Public record created.") ) ) : ( - i18next.t( - "Publish the EP-approved record publicly on CDS." - ) + i18next.t("Publish the approved record publicly on CDS.") )}
diff --git a/assets/js/components/record_details/NewVersionButton.js b/assets/js/components/record_details/NewVersionButton.js index e9b230fd..f3b0fcdd 100644 --- a/assets/js/components/record_details/NewVersionButton.js +++ b/assets/js/components/record_details/NewVersionButton.js @@ -10,17 +10,21 @@ import { http } from "react-invenio-forms"; import { Button, Icon, Modal, Popup } from "semantic-ui-react"; import { i18next } from "@translations/invenio_rdm_records/i18next"; +const readCommitteeApprovalState = () => { + const recordManagementDiv = document.getElementById("recordManagement"); + return recordManagementDiv + ? JSON.parse(recordManagementDiv.dataset.committeeApproval || "null") + : null; +}; + export const NewVersionButton = ({ onError, record, disabled, ...uiProps }) => { const [loading, setLoading] = useState(false); const [showModal, setShowModal] = useState(false); - const committeeApprovalStatus = useMemo(() => { - const recordManagementDiv = document.getElementById("recordManagement"); - return recordManagementDiv - ? JSON.parse(recordManagementDiv.dataset.committeeApproval || "null") - ?.open_request?.status - : null; - }, []); + const committeeApproval = useMemo(readCommitteeApprovalState, []); + const committeeApprovalStatus = committeeApproval?.open_request?.status; + const approvalLabel = + committeeApproval?.approval_label || i18next.t("Committee approval"); const handleClick = useCallback(async () => { if ( @@ -80,17 +84,24 @@ export const NewVersionButton = ({ onError, record, disabled, ...uiProps }) => { {committeeApprovalStatus === "submitted" && - i18next.t("EP approval request pending")} + i18next.t("{{approvalLabel}} request pending", { + approvalLabel, + })} {committeeApprovalStatus === "accepted" && - i18next.t("EP approval already complete")} + i18next.t("{{approvalLabel}} already complete", { + approvalLabel, + })} {committeeApprovalStatus === "submitted" && ( <>

{i18next.t( - "An EP approval request is already pending for an existing version of this record. " + - "A new version will not be taken into account for the approval request." + "An {{approvalLabel}} request is already pending for an existing version of this record. " + + "A new version will not be taken into account for the {{approvalLabel}} request.", + { + approvalLabel, + } )}

diff --git a/assets/js/invenio_app_rdm/overridableRegistry/mapping.js b/assets/js/invenio_app_rdm/overridableRegistry/mapping.js index c653b9c5..4b94d632 100644 --- a/assets/js/invenio_app_rdm/overridableRegistry/mapping.js +++ b/assets/js/invenio_app_rdm/overridableRegistry/mapping.js @@ -4,7 +4,10 @@ // CDS RDM is free software; you can redistribute it and/or modify it // under the terms of the GPL-2.0 License; see LICENSE file for more details. -import React from "react"; +import React, { useContext } from "react"; +import { DatasetContext } from "@js/invenio_requests/data"; +import { i18next as requestsI18next } from "@translations/invenio_requests/i18next"; +import { Label } from "semantic-ui-react"; import { BasicCERNInformation } from "../../components/deposit/BasicInformation"; import { CDSCarouselItem } from "../../components/communities_carousel/overrides/CarouselItem"; import { CDSRecordsList } from "../../components/frontpage/overrides/RecordsList"; @@ -29,6 +32,17 @@ const RecordManagementContainer = (props) => ( ); +const CommunitySubmissionRequestTypeLabel = () => { + const dataset = useContext(DatasetContext); + const approvalLabel = dataset?.request?.approval_label; + + return ( + + ); +}; + export const overriddenComponents = { "InvenioAppRdm.RecordsList.layout": CDSRecordsList, "InvenioAppRdm.RecordsResultsListItem.layout": CDSRecordsResultsListItem, @@ -48,6 +62,8 @@ export const overriddenComponents = { "InvenioRdmRecords.RecordLandingPage.RecordManagement.NewVersionButton": NewVersionButton, "InvenioRequests.LockRequest": LockRequestComponent, + "RequestTypeLabel.layout.community-submission": + CommunitySubmissionRequestTypeLabel, "InvenioAppRdm.RecordVersionsList.Item.container": RecordVersionItemContent, "InvenioRequests.TimelineEventBody": TimelineEventBodyComponent, }; diff --git a/invenio.cfg b/invenio.cfg index 9d218e3f..39ac494b 100644 --- a/invenio.cfg +++ b/invenio.cfg @@ -36,6 +36,7 @@ from cds_rdm.notifications.committee_approval import ( CommitteeApprovalDeclineNotificationBuilder, CommitteeApprovalSubmitNotificationBuilder, ) +from cds_rdm.requests.committee_approval import CommitteeApprovalCommunitySubmission from cds_rdm.api import CDSRDMRecord, CDSRDMDraft from invenio_cern_sync.sso import cern_keycloak, cern_remote_app_name from invenio_cern_sync.users.profile import CERNUserProfileSchema @@ -461,7 +462,8 @@ CDS_COMMITTEE_APPROVAL_COMMUNITIES = { # UUIDs are used (not slugs) because slugs can be renamed. # # "4b121eb9-3559-487d-9f73-8af1027668e9": { - # "label": "EP approval", # shown in UI buttons/headings + # "label": "EP approval", # shown in request buttons/headings + # "committee_name": "EP Board", # shown in review-step labels/text # "referee_group": "cds-ph-ep-publication", # CERN e-group slug # "report_number": { # "prefix": "CERN-EP", # literal prefix, e.g. "CERN-EP" @@ -473,7 +475,10 @@ CDS_COMMITTEE_APPROVAL_COMMUNITIES = { """Communities enrolled in the Publication Approval Workflow. Keyed by community UUID. Each entry must have: - - ``label``: human-readable name shown in the UI (e.g. "EP approval") + - ``label``: human-readable workflow label shown in the UI + (e.g. "EP approval") + - ``committee_name`` (optional): reviewer/board name shown in review-step + labels and text (e.g. "EP Board") - ``referee_group``: CERN e-group whose members act as referees - ``report_number``: dict with: - ``prefix`` (str): fixed prefix, e.g. ``"CERN-EP"`` @@ -487,7 +492,7 @@ enforced across all enrolled communities. CHECKS_ENABLED = True """Enable metadata checks.""" -RDM_COMMUNITY_SUBMISSION_REQUEST_CLS = checks_requests.CommunitySubmission +RDM_COMMUNITY_SUBMISSION_REQUEST_CLS = CommitteeApprovalCommunitySubmission RDM_COMMUNITY_INCLUSION_REQUEST_CLS = checks_requests.CommunityInclusion # require a community when publishing diff --git a/site/cds_rdm/requests/committee_approval.py b/site/cds_rdm/requests/committee_approval.py index 1a966a86..7e8a2c90 100644 --- a/site/cds_rdm/requests/committee_approval.py +++ b/site/cds_rdm/requests/committee_approval.py @@ -21,6 +21,7 @@ from invenio_i18n import lazy_gettext as _ from invenio_notifications.services.uow import NotificationOp from invenio_pidstore.models import PersistentIdentifier, PIDStatus +from invenio_rdm_records.checks import requests as checks_requests from invenio_rdm_records.proxies import current_rdm_records_service from invenio_rdm_records.records.api import RDMRecord from invenio_rdm_records.requests.base import BaseRequest as RDMBaseRequest @@ -77,6 +78,16 @@ def _resolve_community_config(request): ) +def _community_submission_approval_label(request): + """Return the configured label for a community submission request.""" + community_id = (request.get("receiver") or {}).get("community") + return ( + current_app.config.get("CDS_COMMITTEE_APPROVAL_COMMUNITIES", {}) + .get(str(community_id), {}) + .get("label") + ) + + class CommitteeApprovalSubmitAction(actions.CreateAndSubmitAction): """Submit action — validate community enrollment and notify referees.""" @@ -343,6 +354,8 @@ class CommitteeApprovalRequest(RDMBaseRequest): # Payload fields collected from the submission form. payload_schema: Final[dict] = { + # Captured at submission for request-page and email display. + "approval_label": fields.Str(load_default=None), # Populated on accept by the system. "approved_report_number": fields.Str(load_default=None), # Form fields. @@ -354,3 +367,19 @@ class CommitteeApprovalRequest(RDMBaseRequest): "controversy": fields.Bool(load_default=False), "additional_communication": fields.Str(load_default=None), } + + +class CommitteeApprovalCommunitySubmission(checks_requests.CommunitySubmission): + """Community submission that exposes its configured UI label.""" + + @classmethod + def _create_marshmallow_schema(cls): + """Add the configured label when serializing existing requests.""" + schema = super()._create_marshmallow_schema() + return schema.from_dict( + { + "approval_label": fields.Function( + serialize=_community_submission_approval_label + ) + } + ) diff --git a/site/cds_rdm/requests/committee_approval_state.py b/site/cds_rdm/requests/committee_approval_state.py index e675f397..73b4ddcb 100644 --- a/site/cds_rdm/requests/committee_approval_state.py +++ b/site/cds_rdm/requests/committee_approval_state.py @@ -15,6 +15,16 @@ from invenio_requests.proxies import current_requests_service +def _approval_label(community_config=None): + """Return the UI label for this workflow.""" + return (community_config or {}).get("label") + + +def _committee_name(community_config=None): + """Return the configured reviewer/board name for this workflow.""" + return (community_config or {}).get("committee_name") + + def _get_enrolled_community(record_ui): """Return (community_id, community_config) for enrolled communities, else (None, None).""" ep_communities = current_app.config.get("CDS_COMMITTEE_APPROVAL_COMMUNITIES", {}) @@ -139,6 +149,8 @@ def get_committee_approval_state(record_ui, record=None): Returns a dict with: - can_submit: bool - can_create_public: bool + - approval_label: str + - committee_name: str or None - community_enrolled: bool - is_public_approved_record: bool - open_request: dict or None — {id, status, links} @@ -150,6 +162,7 @@ def get_committee_approval_state(record_ui, record=None): """ # Read committee_approval from the parent. ea = _get_parent_committee_approval(record) + community_id, community_config = _get_enrolled_community(record_ui) # Early exit: this IS the public committee-approved copy. # The public record's parent has source_internal_version set. @@ -167,6 +180,8 @@ def get_committee_approval_state(record_ui, record=None): return { "can_submit": False, "can_create_public": False, + "approval_label": _approval_label(community_config), + "committee_name": _committee_name(community_config), "community_enrolled": False, "is_public_approved_record": True, "open_request": None, @@ -180,11 +195,12 @@ def get_committee_approval_state(record_ui, record=None): } # Check community enrollment. - community_id, community_config = _get_enrolled_community(record_ui) if community_id is None: return { "can_submit": False, "can_create_public": False, + "approval_label": _approval_label(), + "committee_name": None, "community_enrolled": False, "is_public_approved_record": False, "open_request": None, @@ -217,6 +233,8 @@ def get_committee_approval_state(record_ui, record=None): return { "can_submit": can_submit, "can_create_public": can_create_public, + "approval_label": _approval_label(community_config), + "committee_name": _committee_name(community_config), "community_enrolled": True, "is_public_approved_record": False, "open_request": open_request, diff --git a/site/cds_rdm/requests/views.py b/site/cds_rdm/requests/views.py index 086ee25e..e41ea6e4 100644 --- a/site/cds_rdm/requests/views.py +++ b/site/cds_rdm/requests/views.py @@ -59,6 +59,16 @@ def submit_committee_approval(pid_value): ) title = record.data.get("metadata", {}).get("title", "") + default_community_id = ( + record._record.parent.get("communities", {}) or {} + ).get("default") + approval_label = ( + current_app.config.get("CDS_COMMITTEE_APPROVAL_COMMUNITIES", {}) + .get(default_community_id, {}) + .get("label") + or "Committee approval" + ) + payload["approval_label"] = approval_label req = current_requests_service.create( identity=g.identity, data={ diff --git a/templates/semantic-ui/invenio_notifications/committee-approval-request.submit.jinja b/templates/semantic-ui/invenio_notifications/committee-approval-request.submit.jinja index fbf826f1..31f88208 100644 --- a/templates/semantic-ui/invenio_notifications/committee-approval-request.submit.jinja +++ b/templates/semantic-ui/invenio_notifications/committee-approval-request.submit.jinja @@ -2,7 +2,7 @@ {% set record = committee_request.topic %} {% set request_id = committee_request.id %} {% set record_title = record.metadata.title %} -{% set report_number = committee_request.title | replace('Committee approval for "', '') | replace('"', '') %} +{% set approval_label = committee_request.payload.get("approval_label", "Committee approval") %} {% if recipient.data.profile is defined and recipient.data.profile.full_name %} {% set recipient_full_name = recipient.data.profile.full_name %} {% else %} @@ -23,7 +23,7 @@ %} {%- block subject -%} - [CDS] {{ _("Request for committee approval of '{record_title}'").format(record_title=record_title) }} + [CDS] {{ _("Request for {approval_label} of '{record_title}'").format(approval_label=approval_label, record_title=record_title) }} {%- endblock subject -%} {%- block html_body -%} @@ -34,7 +34,7 @@ - {{ _("A new committee approval request has been submitted for the following document:") }} + {{ _("A new {approval_label} request has been submitted for the following document:").format(approval_label=approval_label) }} @@ -47,7 +47,7 @@ {{ _("Please review the document and register your decision at the link below.") }} - {{ _("Review the approval request") }} + {{ _("Review the {approval_label} request").format(approval_label=approval_label) }} @@ -66,14 +66,14 @@ {%- block plain_body -%} {{ _("Dear {recipient}").format(recipient=recipient_full_name) }}, -{{ _("A new committee approval request has been submitted for the following document:") }} +{{ _("A new {approval_label} request has been submitted for the following document:").format(approval_label=approval_label) }} {{ _("Title:") }} {{ record_title }} {{ _("Record:") }} {{ record_link }} {{ _("Please review the document and register your decision at the link below.") }} -{{ _("Review the approval request:") }} {{ request_link }} +{{ _("Review the {approval_label} request:").format(approval_label=approval_label) }} {{ request_link }} {{ _("Best regards") }}, -- @@ -86,14 +86,14 @@ CERN Document Server {{ config.SITE_UI_URL }} {%- block md_body -%} {{ _("Dear {recipient}").format(recipient=recipient_full_name) }}, -{{ _("A new committee approval request has been submitted for the following document:") }} +{{ _("A new {approval_label} request has been submitted for the following document:").format(approval_label=approval_label) }} **{{ _("Title:") }}** {{ record_title }} **{{ _("Record:") }}** {{ record_link }} {{ _("Please review the document and register your decision at the link below.") }} -{{ _("Review the approval request:") }} {{ request_link }} +{{ _("Review the {approval_label} request:").format(approval_label=approval_label) }} {{ request_link }} {{ _("Best regards") }}, --