Skip to content

Commit

Permalink
pids: no doi for restricted records
Browse files Browse the repository at this point in the history
* handle doi creation and deletion in pid store
* register doi when embargo is lifted
* adjust UI
  • Loading branch information
anikachurilova committed May 14, 2024
1 parent 6d30996 commit 768b222
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export class AccessRightFieldCmp extends Component {
labelIcon,
showMetadataAccess,
community,
record,
recordRestrictionGracePeriod,
allowRecordRestriction,
} = this.props;

const isGhostCommunity = community?.is_ghost === true;
Expand All @@ -52,6 +55,9 @@ export class AccessRightFieldCmp extends Component {
<MetadataAccess
recordAccess={formik.field.value.record}
communityAccess={communityAccess}
record={record}
recordRestrictionGracePeriod={recordRestrictionGracePeriod}
allowRecordRestriction={allowRecordRestriction}
/>
<Divider hidden />
</>
Expand Down Expand Up @@ -97,6 +103,9 @@ AccessRightFieldCmp.propTypes = {
labelIcon: PropTypes.string.isRequired,
showMetadataAccess: PropTypes.bool,
community: PropTypes.object,
record: PropTypes.object.isRequired,
recordRestrictionGracePeriod: PropTypes.object.isRequired,
allowRecordRestriction: PropTypes.bool.isRequired,
};

AccessRightFieldCmp.defaultProps = {
Expand Down Expand Up @@ -130,6 +139,9 @@ AccessRightField.propTypes = {
label: PropTypes.string.isRequired,
labelIcon: PropTypes.string,
isMetadataOnly: PropTypes.bool,
record: PropTypes.object.isRequired,
recordRestrictionGracePeriod: PropTypes.object.isRequired,
allowRecordRestriction: PropTypes.bool.isRequired,
};

AccessRightField.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,41 @@ import { i18next } from "@translations/invenio_rdm_records/i18next";
import Overridable from "react-overridable";

export const MetadataAccess = (props) => {
const { recordAccess, communityAccess } = props;
const {
recordAccess,
communityAccess,
record,
recordRestrictionGracePeriod,
allowRecordRestriction,
} = props;
const publicMetadata = recordAccess === "public";
const publicCommunity = communityAccess === "public";

const checkGracePeriod = () => {
const created = record?.created;
if (!allowRecordRestriction && created) {
const createdDate = new Date(created);

// calculate end of grace period
createdDate.setDate(createdDate.getDate() + recordRestrictionGracePeriod);

return createdDate.getTime() <= new Date().getTime();
}

return false;
};

const isButtonDisabled = () => {
return checkGracePeriod() && record?.access?.record !== "restricted";
};

return (
<Overridable id="ReactInvenioDeposit.MetadataAccess.layout" {...props}>
<>
{i18next.t("Full record")}
<ProtectionButtons
isRestrictionDisabled={isButtonDisabled()}
record={record}
active={publicMetadata && publicCommunity}
disabled={!publicCommunity}
fieldPath="access.record"
Expand All @@ -33,4 +59,7 @@ export const MetadataAccess = (props) => {
MetadataAccess.propTypes = {
recordAccess: PropTypes.string.isRequired,
communityAccess: PropTypes.string.isRequired,
record: PropTypes.object.isRequired,
recordRestrictionGracePeriod: PropTypes.object.isRequired,
allowRecordRestriction: PropTypes.bool.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// under the terms of the MIT License; see LICENSE file for more details.

import React, { Component } from "react";
import { Button } from "semantic-ui-react";
import { Button, Popup, Icon } from "semantic-ui-react";
import { FastField } from "formik";
import { i18next } from "@translations/invenio_rdm_records/i18next";
import PropTypes from "prop-types";
Expand Down Expand Up @@ -39,36 +39,49 @@ class ProtectionButtonsComponent extends Component {
};

render() {
const { active, disabled } = this.props;
const { active, disabled, isRestrictionDisabled } = this.props;

const publicColor = active ? "positive" : "";
const restrictedColor = !active ? "negative" : "";

return (
<Button.Group widths="2">
<Button
className={publicColor}
data-testid="protection-buttons-component-public"
disabled={disabled}
onClick={this.handlePublicButtonClick}
active={active}
>
{i18next.t("Public")}
</Button>
<Button
className={restrictedColor}
data-testid="protection-buttons-component-restricted"
active={!active}
onClick={this.handleRestrictionButtonClick}
>
{i18next.t("Restricted")}
</Button>
</Button.Group>
<>
{isRestrictionDisabled && (
<Popup
trigger={<Icon className="right-floated" name="help circle" />}
content={i18next.t(
"Grace period is expired. Record visibility can not be changed to restricted anymore. Please contact support if you still need to make these changes."
)}
/>
)}
<Button.Group widths="2">
<Button
className={publicColor}
data-testid="protection-buttons-component-public"
disabled={disabled}
onClick={this.handlePublicButtonClick}
active={active}
>
{i18next.t("Public")}
</Button>

<Button
disabled={isRestrictionDisabled}
className={restrictedColor}
data-testid="protection-buttons-component-restricted"
active={!active}
onClick={this.handleRestrictionButtonClick}
>
{i18next.t("Restricted")}
</Button>
</Button.Group>
</>
);
}
}

ProtectionButtonsComponent.propTypes = {
isRestrictionDisabled: PropTypes.bool,
fieldPath: PropTypes.string.isRequired,
formik: PropTypes.object.isRequired,
active: PropTypes.bool,
Expand All @@ -78,6 +91,7 @@ ProtectionButtonsComponent.propTypes = {
ProtectionButtonsComponent.defaultProps = {
active: true,
disabled: false,
isRestrictionDisabled: false,
};

export class ProtectionButtons extends Component {
Expand All @@ -96,5 +110,6 @@ export class ProtectionButtons extends Component {
}

ProtectionButtons.propTypes = {
isRestrictionDisabled: PropTypes.bool.isRequired,
fieldPath: PropTypes.string.isRequired,
};
22 changes: 22 additions & 0 deletions invenio_rdm_records/services/components/pids.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,28 @@ def publish(self, identity, draft=None, record=None):
if record_id != draft_id:
changed_pids[scheme] = record_pids[scheme]

if "doi" not in draft_schemes:
draft_schemes.add("doi")
if "doi" not in draft_pids:
draft_pids["doi"] = {}

for scheme in draft_schemes:
draft_doi = draft.pids.get("doi")
if scheme == "doi" and draft["access"]["record"] == "restricted":
if draft_doi.get("provider") == "external":
continue
if (
draft_doi == {}
or self.service.pids.pid_manager.read(
"doi",
draft_doi.get("identifier"),
draft_doi.get("provider"),
).status
== "N"
):
self.delete_draft(identity, draft)
del draft_pids["doi"]

self.service.pids.pid_manager.discard_all(changed_pids)

# Determine schemes which are required, but not yet created.
Expand Down
4 changes: 2 additions & 2 deletions invenio_rdm_records/services/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ def is_record_and_has_doi(record, ctx):

def is_record_or_draft_and_has_parent_doi(record, ctx):
"""Determine if draft or record has parent doi."""
return (
is_record(record, ctx) or is_draft(record, ctx) and has_doi(record.parent, ctx)
return (is_record(record, ctx) or is_draft(record, ctx)) and has_doi(
record.parent, ctx
)


Expand Down
6 changes: 3 additions & 3 deletions invenio_rdm_records/services/pids/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def create_all(self, draft, pids=None, schemes=None):
result[scheme] = self.create(
draft,
scheme,
pid_attrs["identifier"],
pid_attrs.get("identifier", None),
pid_attrs.get("provider"),
)

Expand Down Expand Up @@ -279,8 +279,8 @@ def discard_all(self, pids, soft_delete=False):
try:
self.discard(
scheme,
pid_attrs["identifier"],
pid_attrs["provider"],
pid_attrs.get("identifier", None),
pid_attrs.get("provider"),
soft_delete=soft_delete,
)
except PIDDoesNotExistError:
Expand Down
36 changes: 34 additions & 2 deletions invenio_rdm_records/services/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@
from invenio_accounts.models import User
from invenio_db import db
from invenio_drafts_resources.services.records import RecordService
from invenio_drafts_resources.services.records.uow import ParentRecordCommitOp
from invenio_records_resources.services import LinksTemplate, ServiceSchemaWrapper
from invenio_records_resources.services.uow import (
RecordCommitOp,
RecordIndexDeleteOp,
RecordIndexOp,
TaskOp,
unit_of_work,
)
from invenio_requests.services.results import EntityResolverExpandableField
from invenio_search.engine import dsl
from sqlalchemy.exc import NoResultFound

from invenio_rdm_records.records.models import RDMRecordQuota, RDMUserQuota
from invenio_rdm_records.services.pids.tasks import register_or_update_pid

from ..records.systemfields.deletion_status import RecordDeletionStatusEnum
from .errors import (
Expand Down Expand Up @@ -132,8 +135,37 @@ def lift_embargo(self, identity, _id, uow=None):
"lift_embargo", identity, draft=draft, record=record, uow=uow
)

# Commit and reindex record
uow.register(RecordCommitOp(record, indexer=self.indexer))
if "doi" not in record.pids:
pids = self._pids.pid_manager.create_all(
record,
schemes={"doi"},
)

parent_pids = self._pids.parent_pid_manager.create_all(
record.parent,
schemes={"doi"},
)

# Reserve all created PIDs and store them on the record
self._pids.pid_manager.reserve_all(record, pids)
record.pids["doi"] = pids["doi"]
uow.register(RecordCommitOp(record, indexer=self.indexer))
uow.register(
TaskOp(register_or_update_pid, record["id"], "doi", parent=False)
)

self._pids.parent_pid_manager.reserve_all(record.parent, parent_pids)
record.parent.pids["doi"] = parent_pids["doi"]
uow.register(
ParentRecordCommitOp(
record.parent,
)
)
uow.register(
TaskOp(register_or_update_pid, record["id"], "doi", parent=True)
)
else:
uow.register(RecordCommitOp(record, indexer=self.indexer))

def scan_expired_embargos(self, identity):
"""Scan for records with an expired embargo."""
Expand Down

0 comments on commit 768b222

Please sign in to comment.