Skip to content

Commit

Permalink
Merge pull request #89 from ImperialCollegeLondon/feature/limited_rights
Browse files Browse the repository at this point in the history
Feature/limited rights
  • Loading branch information
cc-a authored Dec 9, 2024
2 parents 0dcece3 + 8c30cef commit 49ca923
Show file tree
Hide file tree
Showing 4 changed files with 476 additions and 2 deletions.
5 changes: 3 additions & 2 deletions assets/js/invenio_app_rdm/overridableRegistry/mapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

import { HiddenField } from "../../ic_data_repo/HiddenField";
import { OptionalRoleCreatibutorsField } from "../../ic_data_repo/OptionalRoleCreatibutors";
import { LimitedLicenseField } from "../../ic_data_repo/LimitedLicenseField";
import { parametrize } from "react-overridable";
import { TextAreaField } from "react-invenio-forms";

const CreatorsField = parametrize(OptionalRoleCreatibutorsField, {
helpText:
"The main individuals or institutions involved in creating the data set.",
helpText: "The main individuals or institutions involved in creating the data set.",
includeRole: false,
});

Expand All @@ -28,5 +28,6 @@ export const overriddenComponents = {
"InvenioAppRdm.Deposit.PublisherField.container": HiddenField,
"InvenioAppRdm.Deposit.PublicationDateField.container": HiddenField,
"InvenioAppRdm.Deposit.DescriptionsField.container": TextAreaField,
"InvenioAppRdm.Deposit.LicenseField.container": LimitedLicenseField,
"InvenioAppRdm.Deposit.AccordionFieldReferences.container": HiddenField,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// This file is part of Invenio-RDM-Records
// Copyright (C) 2020-2023 CERN.
// Copyright (C) 2020-2022 Northwestern University.
// Copyright (C) 2021 Graz University of Technology.
//
// 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 _find from "lodash/find";
import React, { Component } from "react";
import PropTypes from "prop-types";
import { getIn, FieldArray } from "formik";
import { HTML5Backend } from "react-dnd-html5-backend";
import { DndProvider } from "react-dnd";
import { FieldLabel } from "react-invenio-forms";
import { Button, Form, Icon, List } from "semantic-ui-react";

import { LimitedLicenseModal } from "./LimitedLicenseModal";
import { LimitedLicenseFieldItem } from "./LimitedLicenseFieldItem";
import { i18next } from "@translations/invenio_rdm_records/i18next";

/**
* The user-facing license.
*
*/
class VisibleLicense {
/**
* Constructor.
*
* @param {array} uiRights
* @param {object} right
* @param {int} index
*/
constructor(uiRights, right, index) {
this.index = index;
this.type = right.id ? "standard" : "custom";
this.key = right.id || right.title;
this.initial = this.type === "custom" ? right : null;

let uiRight =
_find(
uiRights,
right.id ? (o) => o.id === right.id : (o) => o.title === right.title
) || {};

this.description = uiRight.description_l10n || right.description || "";
this.title = uiRight.title_l10n || right.title || "";
this.link =
(uiRight.props && uiRight.props.url) ||
uiRight.link ||
(right.props && right.props.url) ||
right.link ||
"";
}
}

class LicenseFieldForm extends Component {
render() {
const {
label,
labelIcon,
fieldPath,
uiFieldPath,
form: { values },
move: formikArrayMove,
push: formikArrayPush,
remove: formikArrayRemove,
replace: formikArrayReplace,
required,
searchConfig,
serializeLicenses,
} = this.props;

// Limiting the search results
// see https://github.com/inveniosoftware/react-searchkit
searchConfig.initialQueryState.filters = [['tags', 'data']];
searchConfig.initialQueryState.size = 2;
searchConfig.initialQueryState.queryString = 'id: cc-by-4.0 OR cc0-1.0';

const uiRights = getIn(values, uiFieldPath, []);

return (
<DndProvider backend={HTML5Backend}>
<Form.Field required={required}>
<FieldLabel htmlFor={fieldPath} icon={labelIcon} label={label} />
<List>
{getIn(values, fieldPath, []).map((value, index) => {
const license = new VisibleLicense(uiRights, value, index);
return (
<LimitedLicenseFieldItem
key={license.key}
license={license}
moveLicense={formikArrayMove}
replaceLicense={formikArrayReplace}
removeLicense={formikArrayRemove}
searchConfig={searchConfig}
serializeLicenses={serializeLicenses}
/>
);
})}
</List>
{values.metadata.rights.length === 0 && (<LimitedLicenseModal
searchConfig={searchConfig}
trigger={
<Button type="button" key="standard" icon labelPosition="left">
<Icon name="add" />
{i18next.t("Add license")}
</Button>
}
onLicenseChange={(selectedLicense) => {
formikArrayPush(selectedLicense);
}}
action="add"
serializeLicenses={serializeLicenses}
/>)
}
</Form.Field>
</DndProvider>
);
}
}

LicenseFieldForm.propTypes = {
label: PropTypes.node.isRequired,
labelIcon: PropTypes.node,
fieldPath: PropTypes.string.isRequired,
uiFieldPath: PropTypes.string,
form: PropTypes.object.isRequired,
move: PropTypes.func.isRequired,
push: PropTypes.func.isRequired,
remove: PropTypes.func.isRequired,
replace: PropTypes.func.isRequired,
required: PropTypes.bool.isRequired,
searchConfig: PropTypes.object.isRequired,
serializeLicenses: PropTypes.func,
};

LicenseFieldForm.defaultProps = {
labelIcon: undefined,
uiFieldPath: undefined,
serializeLicenses: undefined,
};

export class LimitedLicenseField extends Component {
render() {
const { fieldPath } = this.props;
return (
<FieldArray
name={fieldPath}
component={(formikProps) => (
<LicenseFieldForm {...formikProps} {...this.props} />
)}
/>
);
}
}

LimitedLicenseField.propTypes = {
fieldPath: PropTypes.string.isRequired,
label: PropTypes.string,
labelIcon: PropTypes.string,
searchConfig: PropTypes.object.isRequired,
required: PropTypes.bool,
serializeLicenses: PropTypes.func,
uiFieldPath: PropTypes.string,
};

LimitedLicenseField.defaultProps = {
label: i18next.t("Licenses"),
uiFieldPath: "ui.rights",
labelIcon: "drivers license",
required: false,
serializeLicenses: undefined,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// This file is part of Invenio-RDM-Records
// Copyright (C) 2020-2023 CERN.
// Copyright (C) 2020-2022 Northwestern University.
// Copyright (C) 2021 Graz University of Technology.
//
// 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 React from "react";
import { useDrag, useDrop } from "react-dnd";
import { Button, List, Ref } from "semantic-ui-react";
import _truncate from "lodash/truncate";
import { LimitedLicenseModal } from "./LimitedLicenseModal";
import { i18next } from "@translations/invenio_rdm_records/i18next";
import PropTypes from "prop-types";

export const LimitedLicenseFieldItem = ({
license,
moveLicense,
replaceLicense,
removeLicense,
searchConfig,
serializeLicenses,
}) => {
const dropRef = React.useRef(null);

const [, drag, preview] = useDrag({
item: { index: license.index, type: "license" },
});
const [{ hidden }, drop] = useDrop({
accept: "license",
hover(item, monitor) {
if (!dropRef.current) {
return;
}
const dragIndex = item.index;
const hoverIndex = license.index;

// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}

if (monitor.isOver({ shallow: true })) {
moveLicense(dragIndex, hoverIndex);
item.index = hoverIndex;
}
},
collect: (monitor) => ({
hidden: monitor.isOver({ shallow: true }),
}),
});

// Initialize the ref explicitely
drop(dropRef);
return (
<Ref innerRef={dropRef} key={license.key}>
<List.Item
key={license.key}
className={hidden ? "deposit-drag-listitem hidden" : "deposit-drag-listitem"}
>
<List.Content floated="right">
<LimitedLicenseModal
searchConfig={searchConfig}
onLicenseChange={(selectedLicense) => {
replaceLicense(license.index, selectedLicense);
}}
initialLicense={license.initial}
action="edit"
trigger={
<Button size="mini" primary type="button">
{i18next.t("Change")}
</Button>
}
serializeLicenses={serializeLicenses}
/>
<Button
size="mini"
type="button"
onClick={() => {
removeLicense(license.index);
}}
>
{i18next.t("Remove")}
</Button>
</List.Content>
<Ref innerRef={drag}>
<List.Icon name="bars" className="drag-anchor" />
</Ref>
<Ref innerRef={preview}>
<List.Content>
<List.Header>{license.title}</List.Header>
{license.description && (
<List.Description>
{_truncate(license.description, { length: 300 })}
</List.Description>
)}
{license.link && (
<span>
<a href={license.link} target="_blank" rel="noopener noreferrer">
{license.description && <span>&nbsp;</span>}
{i18next.t("Read more")}
</a>
</span>
)}
</List.Content>
</Ref>
</List.Item>
</Ref>
);
};

LimitedLicenseFieldItem.propTypes = {
license: PropTypes.object.isRequired,
moveLicense: PropTypes.func.isRequired,
replaceLicense: PropTypes.func.isRequired,
removeLicense: PropTypes.func.isRequired,
searchConfig: PropTypes.object.isRequired,
serializeLicenses: PropTypes.func,
};

LimitedLicenseFieldItem.defaultProps = {
serializeLicenses: undefined,
};
Loading

0 comments on commit 49ca923

Please sign in to comment.