diff --git a/invenio_app_rdm/config.py b/invenio_app_rdm/config.py index eafc02c6a..713954394 100644 --- a/invenio_app_rdm/config.py +++ b/invenio_app_rdm/config.py @@ -684,6 +684,12 @@ def files_rest_permission_factory(obj, action): APP_RDM_DEPOSIT_FORM_TEMPLATE = "invenio_app_rdm/records/deposit.html" """Deposit page's form template.""" +RECORDS_RESTRICTION_GRACE_PERIOD = timedelta(days=30) +"""Grace period for changing record access to restricted.""" + +ALLOW_RECORD_RESTRICTION_AFTER_GRACE_PERIOD = False +"""Whether record access restriction is allowed after the grace period or not.""" + APP_RDM_USER_DASHBOARD_ROUTES = { "uploads": "/me/uploads", "communities": "/me/communities", diff --git a/invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/deposit.html b/invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/deposit.html index b97314848..67f1ad68d 100644 --- a/invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/deposit.html +++ b/invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/deposit.html @@ -41,6 +41,15 @@ value='{{ files | tojson }}'> {%- endif %} + {%- if config %} + + + + {%- endif %} + {%- if forms_config %} diff --git a/invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/restricted_tombstone.html b/invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/restricted_tombstone.html new file mode 100644 index 000000000..115a43fc5 --- /dev/null +++ b/invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/restricted_tombstone.html @@ -0,0 +1,38 @@ +{# + Copyright (C) 2024 CERN. + + Invenio App RDM is free software; you can redistribute it and/or modify it + under the terms of the MIT License; see LICENSE file for more details. +#} +{% extends config.THEME_ERROR_TEMPLATE %} + +{%- block message %} +
+

+ +
+ {{_('Permission required')}} +
+

+

+ {{_('The record was previously accessible to the public but is now restricted to users with access.')}} +
+ {{_('The Digital Object Identifier (DOI) associated with this record remains active for reference.')}} +

+ +
+
+ {%- if citation_text %} +

+ {{ _("Citation:") }} {{ citation_text }} +

+ {%- endif %} + {%- if record.pids.doi %} +

+ {{ _("Identifier:") }} {{ record.pids.doi.identifier }} +

+ {%- endif %} +
+
+
+{% endblock message %} diff --git a/invenio_app_rdm/records_ui/views/records.py b/invenio_app_rdm/records_ui/views/records.py index 9fb556325..aacc71fb1 100644 --- a/invenio_app_rdm/records_ui/views/records.py +++ b/invenio_app_rdm/records_ui/views/records.py @@ -22,6 +22,7 @@ from invenio_communities.errors import CommunityDeletedError from invenio_communities.proxies import current_communities from invenio_communities.views.communities import render_community_theme_template +from invenio_i18n.proxies import current_i18n from invenio_previewer.extensions import default as default_previewer from invenio_previewer.proxies import current_previewer from invenio_rdm_records.proxies import current_rdm_records @@ -29,6 +30,11 @@ AccessSettings, ) from invenio_rdm_records.resources.serializers import UIJSONSerializer +from invenio_rdm_records.resources.serializers.csl import ( + CSLJSONSerializer, + get_citation_string, + get_style_location, +) from invenio_stats.proxies import current_stats from invenio_users_resources.proxies import current_user_resources from marshmallow import ValidationError @@ -412,8 +418,36 @@ def record_tombstone_error(error): def record_permission_denied_error(error): - """Handle permission denier error on record views.""" + """Handle permission denied error on record views.""" if not current_user.is_authenticated: # trigger the flask-login unauthorized handler return current_app.login_manager.unauthorized() + + record = getattr(error, "record", None) + + if ( + record + and record.get("access", {}).get("record", None) == "restricted" + and "doi" in record.get("pids", {}) + ): + default_citation_style = current_app.config.get( + "RDM_CITATION_STYLES_DEFAULT", "apa" + ) + + citation = get_citation_string( + CSLJSONSerializer().dump_obj(record), + record.pid.pid_value, + get_style_location(default_citation_style), + locale=current_i18n.language, + ) + + return ( + render_template( + "invenio_app_rdm/records/restricted_tombstone.html", + record=record, + citation_text=citation, + ), + 403, + ) + return render_template(current_app.config["THEME_403_TEMPLATE"]), 403 diff --git a/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/RDMDepositForm.js b/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/RDMDepositForm.js index 937d9c7df..718a199d7 100644 --- a/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/RDMDepositForm.js +++ b/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/RDMDepositForm.js @@ -97,8 +97,15 @@ export class RDMDepositForm extends Component { sidebarRef = createRef(); render() { - const { record, files, permissions, preselectedCommunity, filesLocked } = - this.props; + const { + record, + files, + permissions, + preselectedCommunity, + filesLocked, + recordRestrictionGracePeriod, + allowRecordRestriction, + } = this.props; const customFieldsUI = this.config.custom_fields.ui; return ( {permissions?.can_delete_draft && ( @@ -653,6 +663,8 @@ export class RDMDepositForm extends Component { RDMDepositForm.propTypes = { config: PropTypes.object.isRequired, + recordRestrictionGracePeriod: PropTypes.object.isRequired, + allowRecordRestriction: PropTypes.bool.isRequired, record: PropTypes.object.isRequired, preselectedCommunity: PropTypes.object, files: PropTypes.object, diff --git a/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/index.js b/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/index.js index 2dad88800..730d9a966 100644 --- a/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/index.js +++ b/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/deposit/index.js @@ -22,6 +22,10 @@ ReactDOM.render( config={getInputFromDOM("deposits-config")} permissions={getInputFromDOM("deposits-record-permissions")} filesLocked={getInputFromDOM("deposits-record-locked-files")} + recordRestrictionGracePeriod={getInputFromDOM( + "deposits-record-restriction-grace-period" + )} + allowRecordRestriction={getInputFromDOM("deposits-allow-record-restriction")} /> , document.getElementById("deposit-form") diff --git a/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/landing_page/ShareOptions/ShareModal.js b/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/landing_page/ShareOptions/ShareModal.js index bdc4a2d98..c76d52055 100644 --- a/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/landing_page/ShareOptions/ShareModal.js +++ b/invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/landing_page/ShareOptions/ShareModal.js @@ -158,7 +158,7 @@ export class ShareModal extends Component { ), }, { - menuItem: { icon: "cog", content: i18next.t("Access requests") }, + menuItem: { icon: "cog", content: i18next.t("Settings") }, render: () => (