From b55c69134b4e3bc7db9873d6a661099036d58d5d Mon Sep 17 00:00:00 2001 From: Hrafn Malmquist Date: Wed, 19 Feb 2025 11:13:46 +0000 Subject: [PATCH 1/3] Remove external as DOI provider in config --- site/ic_data_repo/config/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/site/ic_data_repo/config/settings.py b/site/ic_data_repo/config/settings.py index ed3364c..8211b06 100644 --- a/site/ic_data_repo/config/settings.py +++ b/site/ic_data_repo/config/settings.py @@ -11,6 +11,7 @@ from invenio_notifications.backends.email import EmailNotificationBackend from invenio_oauthclient.views.client import auto_redirect_login +from invenio_rdm_records.config import RDM_PERSISTENT_IDENTIFIERS from .custom_fields import * # noqa: F401,F403 from .utils import get_user_form_default @@ -132,6 +133,9 @@ DATACITE_TEST_MODE = True DATACITE_DATACENTER_SYMBOL = "" +# Remove "external" as a DOI provider +RDM_PERSISTENT_IDENTIFIERS["doi"]["providers"].remove("external") + # Authentication - Invenio-Accounts and Invenio-OAuthclient # ========================================================= # See: https://inveniordm.docs.cern.ch/customize/authentication/ From 5e519e631172df80eeec532cf867d4159a49306b Mon Sep 17 00:00:00 2001 From: Hrafn Malmquist Date: Wed, 19 Feb 2025 16:03:15 +0000 Subject: [PATCH 2/3] Mandatory PIDField with button --- .../overridableRegistry/mapping.js | 2 + .../js/ic_data_repo/MandatoryPIDField.js | 351 ++++++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 site/ic_data_repo/assets/semantic-ui/js/ic_data_repo/MandatoryPIDField.js diff --git a/assets/js/invenio_app_rdm/overridableRegistry/mapping.js b/assets/js/invenio_app_rdm/overridableRegistry/mapping.js index e98e6bc..38a5929 100644 --- a/assets/js/invenio_app_rdm/overridableRegistry/mapping.js +++ b/assets/js/invenio_app_rdm/overridableRegistry/mapping.js @@ -7,6 +7,7 @@ import { HiddenField } from "../../ic_data_repo/HiddenField"; import { OptionalRoleCreatibutorsField } from "../../ic_data_repo/OptionalRoleCreatibutors"; import { LimitedLicenseField } from "../../ic_data_repo/LimitedLicenseField"; +import { MandatoryPIDField } from "../../ic_data_repo/MandatoryPIDField"; import { parametrize } from "react-overridable"; import { TextAreaField } from "react-invenio-forms"; @@ -24,6 +25,7 @@ const ContributorsField = parametrize(OptionalRoleCreatibutorsField, { export const overriddenComponents = { "InvenioAppRdm.Deposit.ContributorsField.container": ContributorsField, "InvenioAppRdm.Deposit.CreatorsField.container": CreatorsField, + "InvenioAppRdm.Deposit.PIDField.container": MandatoryPIDField, "InvenioAppRdm.Deposit.ResourceTypeField.container": HiddenField, "InvenioAppRdm.Deposit.PublisherField.container": HiddenField, "InvenioAppRdm.Deposit.PublicationDateField.container": HiddenField, diff --git a/site/ic_data_repo/assets/semantic-ui/js/ic_data_repo/MandatoryPIDField.js b/site/ic_data_repo/assets/semantic-ui/js/ic_data_repo/MandatoryPIDField.js new file mode 100644 index 0000000..815e0ff --- /dev/null +++ b/site/ic_data_repo/assets/semantic-ui/js/ic_data_repo/MandatoryPIDField.js @@ -0,0 +1,351 @@ +// This file is part of Invenio-RDM-Records +// Copyright (C) 2020-2023 CERN. +// Copyright (C) 2020-2022 Northwestern University. +// +// Invenio-RDM-Records is free software; you can redistribute it and/or modify it +// under the terms of the MIT License; see LICENSE file for more details. + +import { i18next } from "@translations/invenio_rdm_records/i18next"; +import { FastField, Field, getIn } from "formik"; +import PropTypes from "prop-types"; +import React, { Component, Fragment } from "react"; +import { FieldLabel } from "react-invenio-forms"; +import { connect } from "react-redux"; +import { Form, Popup } from "semantic-ui-react"; +import { + DepositFormSubmitActions, + DepositFormSubmitContext, +} from "@js/invenio_rdm_records/src/deposit/api/DepositFormSubmitContext"; +import { DISCARD_PID_STARTED, RESERVE_PID_STARTED } from "@js/invenio_rdm_records/src/deposit/state/types"; + +const getFieldErrors = (form, fieldPath) => { + return ( + getIn(form.errors, fieldPath, null) || getIn(form.initialErrors, fieldPath, null) + ); +}; + +/** + * Button component to reserve a PID. + */ +class ReservePIDBtn extends Component { + render() { + const { disabled, handleReservePID, label, loading } = this.props; + return ( + + {({ form: formik }) => ( + handleReservePID(e, formik)} + content={label} + /> + )} + + ); + } +} + +ReservePIDBtn.propTypes = { + disabled: PropTypes.bool, + handleReservePID: PropTypes.func.isRequired, + label: PropTypes.string.isRequired, + loading: PropTypes.bool, +}; + +ReservePIDBtn.defaultProps = { + disabled: false, + loading: false, +}; + +/** + * Button component to unreserve a PID. + */ +class UnreservePIDBtn extends Component { + render() { + const { disabled, handleDiscardPID, label, loading } = this.props; + return ( + + {({ form: formik }) => ( + handleDiscardPID(e, formik)} + size="mini" + /> + )} + + } + /> + ); + } +} + +UnreservePIDBtn.propTypes = { + disabled: PropTypes.bool, + handleDiscardPID: PropTypes.func.isRequired, + label: PropTypes.string.isRequired, + loading: PropTypes.bool, +}; + +UnreservePIDBtn.defaultProps = { + disabled: false, + loading: false, +}; + +/** + * Render identifier field and reserve/unreserve + * button components for managed PID. + */ +class ManagedIdentifierComponent extends Component { + static contextType = DepositFormSubmitContext; + + handleReservePID = (event, formik) => { + const { pidType } = this.props; + const { setSubmitContext } = this.context; + setSubmitContext(DepositFormSubmitActions.RESERVE_PID, { + pidType: pidType, + }); + formik.handleSubmit(event); + }; + + handleDiscardPID = (event, formik) => { + const { pidType } = this.props; + const { setSubmitContext } = this.context; + setSubmitContext(DepositFormSubmitActions.DISCARD_PID, { + pidType: pidType, + }); + formik.handleSubmit(event); + }; + + render() { + const { + actionState, + actionStateExtra, + btnLabelDiscardPID, + btnLabelGetPID, + disabled, + helpText, + identifier, + pidType, + } = this.props; + const hasIdentifier = identifier !== ""; + + const ReserveBtn = ( + + ); + + const UnreserveBtn = ( + + ); + + return ( + <> + + {hasIdentifier && ( + + + + )} + + {identifier ? UnreserveBtn : ReserveBtn} + + {helpText && } + + ); + } +} + +ManagedIdentifierComponent.propTypes = { + btnLabelGetPID: PropTypes.string.isRequired, + disabled: PropTypes.bool, + helpText: PropTypes.string, + identifier: PropTypes.string.isRequired, + btnLabelDiscardPID: PropTypes.string.isRequired, + pidType: PropTypes.string.isRequired, + /* from Redux */ + actionState: PropTypes.string, + actionStateExtra: PropTypes.object, +}; + +ManagedIdentifierComponent.defaultProps = { + disabled: false, + helpText: null, + /* from Redux */ + actionState: "", + actionStateExtra: {}, +}; + +const mapStateToProps = (state) => ({ + actionState: state.deposit.actionState, + actionStateExtra: state.deposit.actionStateExtra, +}); + +const ManagedIdentifierCmp = connect(mapStateToProps, null)(ManagedIdentifierComponent); + +/** + * Render managed or unmanaged PID fields and update + * Formik form on input changed. + * The field value has the following format: + * { 'doi': { identifier: '', provider: '', client: '' } } + */ +class CustomPIDField extends Component { + constructor(props) { + super(props); + const { form } = props; + + // Clear the field if the DOI is external + if ('doi' in form.values.pids && form.values.pids['doi'].provider === 'external') { + form.setFieldValue("pids", {}); + } + } + + render() { + const { + btnLabelDiscardPID, + btnLabelGetPID, + form, + fieldPath, + fieldLabel, + isEditingPublishedRecord, + managedHelpText, + pidIcon, + required, + pidType, + field, + record, + } = this.props; + + const value = field.value || {}; + // If we are editing an already published record. + const currentIdentifier = value.identifier || ""; + const doi = record?.pids?.doi?.identifier || ""; + const hasDoi = doi !== ""; + const fieldError = getFieldErrors(form, fieldPath); + + return ( + <> + + + + + + + + ); + } +} + +CustomPIDField.propTypes = { + field: PropTypes.object, + form: PropTypes.object.isRequired, + btnLabelDiscardPID: PropTypes.string.isRequired, + btnLabelGetPID: PropTypes.string.isRequired, + fieldPath: PropTypes.string.isRequired, + fieldLabel: PropTypes.string.isRequired, + isEditingPublishedRecord: PropTypes.bool.isRequired, + managedHelpText: PropTypes.string, + pidIcon: PropTypes.string.isRequired, + pidType: PropTypes.string.isRequired, + required: PropTypes.bool.isRequired, + record: PropTypes.object.isRequired, +}; + +CustomPIDField.defaultProps = { + managedHelpText: null, + field: undefined, +}; + + +/** + * Render the PIDField using a custom Formik component + */ +export class PIDField extends Component { + render() { + const { fieldPath } = this.props; + return ; + } +} + +PIDField.propTypes = { + btnLabelDiscardPID: PropTypes.string, + btnLabelGetPID: PropTypes.string, + fieldPath: PropTypes.string.isRequired, + fieldLabel: PropTypes.string.isRequired, + isEditingPublishedRecord: PropTypes.bool.isRequired, + managedHelpText: PropTypes.string, + pidIcon: PropTypes.string, + pidType: PropTypes.string.isRequired, + required: PropTypes.bool, + record: PropTypes.object.isRequired, +}; + +PIDField.defaultProps = { + btnLabelDiscardPID: "Discard", + btnLabelGetPID: "Reserve", + managedHelpText: null, + pidIcon: "barcode", + required: false, +}; + + +/** + * Render the PIDField using a custom Formik component + */ +export class MandatoryPIDField extends Component { + render() { + let { config, record } = this.props; + + const pids = config.pids.map((pid) => ( + + + + )); + + return ( + + {pids} + + ); + } +} From 78531a2db65808906e39d446925674cde30e93d7 Mon Sep 17 00:00:00 2001 From: Hrafn Malmquist Date: Fri, 21 Feb 2025 09:01:37 +0000 Subject: [PATCH 3/3] Exempt invenio_rdm_records from mypy --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 5e5f91f..8f4ba0a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ module = [ "invenio_notifications.*", "invenio_oauthclient.*", "invenio_records_resources.*", + "invenio_rdm_records.*", "marshmallow.*", "marshmallow_utils.*", ]