Skip to content

Commit 7f1a229

Browse files
authored
Merge branch 'inveniosoftware:master' into mirekys/js-test-script
2 parents 277902b + bb74b02 commit 7f1a229

87 files changed

Lines changed: 4163 additions & 202 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGES.rst

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,55 @@
1212
Changes
1313
=======
1414

15+
Version v28.3.1 (released 2026-04-14)
16+
17+
- fix: display DOI validation errors in deposit form
18+
19+
Version v28.3.0 (released 2026-04-13)
20+
21+
- feat: add new RDMSubCommunity request class
22+
- fix: remove duplicate definition of collections service proxy
23+
24+
Version v28.2.0 (released 2026-04-09)
25+
26+
- feat(storage-quota): settings integration
27+
- feat(storage-quota): implement self-served quota increases
28+
- chore: deprecate FILES_REST in favour of RDM_FILES
29+
This was deprecated in RDM records in August 2024
30+
https://github.com/inveniosoftware/invenio-rdm-records/pull/1789/changes
31+
32+
Version v28.1.0 (released 2026-04-09)
33+
- fix: switch to accordion data-label
34+
35+
Version v28.0.0 (released 2026-04-08)
36+
37+
- fix: user access request routing
38+
- chore: upgrade invenio-communities
39+
- feat(community-records-collections): initialize and wire CollectionsService with community_records_service
40+
- fix(file-modification): consider `can_modify_locked_files` permission
41+
- feat(auditlog): Add audit logs for share access
42+
- vocabs: add resource_types for datacite 4.5
43+
- vocabs: update resource_types to datacite 4.7
44+
- resource_types: fix OpenAIRE resource type for computational notebook
45+
- vocabularies: add datacite 4.7 relationType
46+
- vocabularies: add datacite 4.6 date types
47+
- vocabularies: add datacite 4.6 relation types
48+
- feat(vcs): support for new VCS integration
49+
- ui(translations): mark message as translatable
50+
- ui(translations): mark strings as translatable
51+
52+
Version v27.0.0 (released 2026-03-20)
53+
54+
- change(setup): upgrade invenio-checks
55+
56+
Version v26.0.0 (released 2026-03-18)
57+
58+
- change(setup): upgrade invenio-checks and invenio-communities
59+
- feat(contrib): add thesis field display template
60+
- feat(permissions): add RDM_ALLOW_OWNERS_REMOVE_COMMUNITY_FROM_RECORD
61+
- feat(translations): add Arabic language
62+
- fix(accordion): get accordion data-label instead of the label
63+
1564
Version v25.0.0 (released 2026-03-10)
1665

1766
- change(setup): upgrade invenio-checks and invenio-communities

invenio_rdm_records/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212

1313
from .ext import InvenioRDMRecords
1414

15-
__version__ = "25.0.0"
15+
__version__ = "28.3.1"
1616

1717
__all__ = ("__version__", "InvenioRDMRecords")

invenio_rdm_records/assets/semantic-ui/js/invenio_rdm_records/src/deposit/components/CommunitySelectionModal/CommunityListItem.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const CommunityListItem = ({ result, record, isInitialSubmission }) => {
2727
const itemSelected = getChosenCommunity()?.id === result.id;
2828
const userMembership = userCommunitiesMemberships[result["id"]];
2929
const invalidPermissionLevel =
30-
record.access.record === "public" && result.access.visibility === "restricted";
30+
record?.access.record === "public" && result.access.visibility === "restricted";
3131
const canSubmitRecord = result.ui.permissions.can_submit_record;
3232
const hasTheme = get(result, "theme.enabled");
3333
const dedicatedUpload = isInitialSubmission && hasTheme;
@@ -126,10 +126,11 @@ export const CommunityListItem = ({ result, record, isInitialSubmission }) => {
126126

127127
CommunityListItem.propTypes = {
128128
result: PropTypes.object.isRequired,
129-
record: PropTypes.object.isRequired,
129+
record: PropTypes.object,
130130
isInitialSubmission: PropTypes.bool,
131131
};
132132

133133
CommunityListItem.defaultProps = {
134134
isInitialSubmission: true,
135+
record: null,
135136
};

invenio_rdm_records/assets/semantic-ui/js/invenio_rdm_records/src/deposit/components/CommunitySelectionModal/CommunitySelectionModal.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class CommunitySelectionModalComponent extends Component {
2525
this.contextValue = {
2626
setLocalCommunity: this.setCommunity,
2727
getChosenCommunity: this.getChosenCommunity,
28-
userCommunitiesMemberships,
28+
userCommunitiesMemberships: userCommunitiesMemberships ?? {},
2929
displaySelected,
3030
};
3131
}
@@ -117,15 +117,15 @@ CommunitySelectionModalComponent.propTypes = {
117117
chosenCommunity: PropTypes.object,
118118
onCommunityChange: PropTypes.func.isRequired,
119119
trigger: PropTypes.object,
120-
userCommunitiesMemberships: PropTypes.object.isRequired,
120+
userCommunitiesMemberships: PropTypes.object,
121121
extraContentComponents: PropTypes.node,
122122
modalHeader: PropTypes.string,
123123
onModalChange: PropTypes.func,
124124
displaySelected: PropTypes.bool,
125125
modalOpen: PropTypes.bool,
126126
apiConfigs: PropTypes.object,
127127
handleClose: PropTypes.func.isRequired,
128-
record: PropTypes.object.isRequired,
128+
record: PropTypes.object,
129129
isInitialSubmission: PropTypes.bool,
130130
};
131131

@@ -139,6 +139,7 @@ CommunitySelectionModalComponent.defaultProps = {
139139
trigger: undefined,
140140
apiConfigs: undefined,
141141
isInitialSubmission: true,
142+
record: null,
142143
};
143144

144145
const mapStateToProps = (state) => ({

invenio_rdm_records/assets/semantic-ui/js/invenio_rdm_records/src/deposit/components/CommunitySelectionModal/CommunitySelectionSearch.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ export class CommunitySelectionSearch extends Component {
5757
const searchApi = new InvenioSearchApi(selectedSearchApi);
5858
const overriddenComponents = {
5959
[`${selectedAppId}.ResultsList.item`]: parametrize(CommunityListItem, {
60-
record: record,
61-
isInitialSubmission: isInitialSubmission,
60+
record,
61+
isInitialSubmission,
6262
}),
6363
};
6464

@@ -178,7 +178,7 @@ CommunitySelectionSearch.propTypes = {
178178
searchApi: PropTypes.object.isRequired,
179179
}),
180180
}),
181-
record: PropTypes.object.isRequired,
181+
record: PropTypes.object,
182182
isInitialSubmission: PropTypes.bool,
183183
CommunityListItem: PropTypes.elementType,
184184
pagination: PropTypes.bool,
@@ -192,6 +192,7 @@ CommunitySelectionSearch.defaultProps = {
192192
myCommunitiesEnabled: true,
193193
autofocus: true,
194194
CommunityListItem: CommunityListItem,
195+
record: null,
195196
apiConfigs: {
196197
allCommunities: {
197198
initialQueryState: { size: 5, page: 1, sortBy: "bestmatch" },
@@ -202,7 +203,7 @@ CommunitySelectionSearch.defaultProps = {
202203
},
203204
},
204205
appId: "ReactInvenioDeposit.CommunitySelectionSearch.AllCommunities",
205-
toggleText: "Search in all communities",
206+
toggleText: i18next.t("Search in all communities"),
206207
},
207208
myCommunities: {
208209
initialQueryState: { size: 5, page: 1, sortBy: "bestmatch" },
@@ -213,7 +214,7 @@ CommunitySelectionSearch.defaultProps = {
213214
},
214215
},
215216
appId: "ReactInvenioDeposit.CommunitySelectionSearch.MyCommunities",
216-
toggleText: "Search in my communities",
217+
toggleText: i18next.t("Search in my communities"),
217218
},
218219
},
219220
};
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// This file is part of Invenio-RDM-Records
2+
// Copyright (C) 2026 CERN.
3+
//
4+
// Invenio-RDM-Records is free software; you can redistribute it and/or modify it
5+
// under the terms of the MIT License; see LICENSE file for more details.
6+
7+
import React from "react";
8+
import {
9+
Header,
10+
Grid,
11+
Label,
12+
Progress,
13+
Table,
14+
Container,
15+
Message,
16+
} from "semantic-ui-react";
17+
import PropTypes from "prop-types";
18+
import { i18next } from "@translations/invenio_rdm_records/i18next";
19+
20+
export default function StorageOverview({ storage }) {
21+
const statusColor = {
22+
Draft: "warning",
23+
Published: "positive",
24+
};
25+
26+
return (
27+
<Container aria-label={i18next.t("Storage")} className="storage-overview">
28+
<Header as="h5" className="mb-2">
29+
<strong>{i18next.t("Quota overview")}</strong>
30+
</Header>
31+
<p className="text-muted mb-4">
32+
{i18next.t(
33+
"You can now manage your record storage by assigning additional quota as needed."
34+
)}{" "}
35+
<a href="./">{i18next.t("Find out more.")}</a>{" "}
36+
{/* Need to link to help guide.*/}
37+
</p>
38+
39+
<Grid doubling columns={3} stackable className="rel-mb-1">
40+
<Grid.Column>
41+
<Message
42+
icon="hdd"
43+
size="tiny"
44+
header={<Header as="h4">{storage.default_quota}&nbsp;GB</Header>}
45+
content={i18next.t("Default quota per record")}
46+
className="rel-p-2"
47+
/>
48+
</Grid.Column>
49+
50+
<Grid.Column>
51+
<Message
52+
icon="server"
53+
size="tiny"
54+
positive={storage.additional_available_quota > 5}
55+
warning={storage.additional_available_quota < 5}
56+
header={
57+
<Header as="h4">{storage.additional_available_quota}&nbsp;GB</Header>
58+
}
59+
content={i18next.t("Available of {{total}} GB allowance", {
60+
total: storage.total_allowed_quota,
61+
})}
62+
className="rel-p-2"
63+
/>
64+
</Grid.Column>
65+
66+
<Grid.Column>
67+
<Message
68+
icon="sitemap"
69+
size="tiny"
70+
info
71+
header={<Header as="h4">{storage.additional_granted_quota}&nbsp;GB</Header>}
72+
content={i18next.t("Quota granted across {{count}} records", {
73+
count: storage.records.length,
74+
})}
75+
className="rel-p-2"
76+
/>
77+
</Grid.Column>
78+
</Grid>
79+
80+
<div className="mb-4">
81+
<Grid>
82+
<Grid.Row columns={2}>
83+
<Grid.Column textAlign="left">
84+
<Label className="medium rel-mb-1">0&nbsp;GB</Label>
85+
</Grid.Column>
86+
<Grid.Column textAlign="right">
87+
<Label className="medium rel-mb-1">
88+
{storage.total_allowed_quota}&nbsp;GB
89+
</Label>
90+
</Grid.Column>
91+
</Grid.Row>
92+
</Grid>
93+
<Progress
94+
value={storage.additional_used_quota}
95+
total={storage.total_allowed_quota}
96+
progress="value"
97+
className="primary"
98+
/>
99+
</div>
100+
101+
{/* Storage Allocations Table */}
102+
<Header as="h5" className="mb-2">
103+
<strong>{i18next.t("Allocated storage")}</strong>
104+
</Header>
105+
{storage.records.length > 0 ? (
106+
<Table size="small" padded>
107+
<Table.Header>
108+
<Table.Row>
109+
<Table.HeaderCell width={7}>{i18next.t("Record")}</Table.HeaderCell>
110+
<Table.HeaderCell width={2}>
111+
{i18next.t("Additional quota")}
112+
</Table.HeaderCell>
113+
<Table.HeaderCell width={4}>{i18next.t("Usage")}</Table.HeaderCell>
114+
<Table.HeaderCell width={2}>{i18next.t("Date")}</Table.HeaderCell>
115+
<Table.HeaderCell width={1}>{i18next.t("Status")}</Table.HeaderCell>
116+
</Table.Row>
117+
</Table.Header>
118+
119+
<Table.Body className="storage-overview-table">
120+
{storage.records.map((record, idx) => {
121+
const percent =
122+
record.total > 0 ? Math.round((record.used / record.total) * 100) : 0;
123+
124+
return (
125+
<Table.Row key={idx}>
126+
<Table.Cell>
127+
<a href={record.url}>{record.title}</a>
128+
<p className="text-muted">{record.url}</p>
129+
</Table.Cell>
130+
131+
<Table.Cell>
132+
<strong>+{record.additional_quota}&nbsp;GB</strong>
133+
<div className="ui tiny text-muted">
134+
(
135+
{i18next.t("{{total}} GB total", {
136+
total: record.total,
137+
})}
138+
)
139+
</div>
140+
</Table.Cell>
141+
142+
<Table.Cell>
143+
<Grid verticalAlign="middle" columns="equal">
144+
<Grid.Column width={9}>
145+
<Progress
146+
value={record.used}
147+
total={record.total}
148+
className="m-0 primary"
149+
size="tiny"
150+
/>
151+
</Grid.Column>
152+
<Grid.Column width={7} textAlign="left">
153+
<p className="text-muted">
154+
{record.used} / {record.total}&nbsp;GB
155+
</p>
156+
</Grid.Column>
157+
</Grid>
158+
<p className="text-muted">
159+
{percent}% {i18next.t("used")}
160+
</p>
161+
</Table.Cell>
162+
163+
<Table.Cell>
164+
<p className="text-muted">{record.date}</p>
165+
</Table.Cell>
166+
167+
<Table.Cell>
168+
<Label size="tiny" color={statusColor[record.status] || "grey"}>
169+
{i18next.t(record.status)}
170+
</Label>
171+
</Table.Cell>
172+
</Table.Row>
173+
);
174+
})}
175+
</Table.Body>
176+
</Table>
177+
) : (
178+
<Message icon size="small">
179+
<i className="archive icon" />
180+
<Message.Content>
181+
<Message.Header>{i18next.t("No allocated storage")}</Message.Header>
182+
<p>
183+
{i18next.t("No records have been granted any additional quotas yet.")}
184+
</p>
185+
</Message.Content>
186+
</Message>
187+
)}
188+
</Container>
189+
);
190+
}
191+
192+
StorageOverview.propTypes = {
193+
storage: PropTypes.shape({
194+
default_quota: PropTypes.number.isRequired,
195+
total_allowed_quota: PropTypes.number.isRequired,
196+
additional_granted_quota: PropTypes.number.isRequired,
197+
additional_available_quota: PropTypes.number.isRequired,
198+
additional_used_quota: PropTypes.number.isRequired,
199+
records: PropTypes.arrayOf(
200+
PropTypes.shape({
201+
title: PropTypes.string.isRequired,
202+
url: PropTypes.string.isRequired,
203+
additional_quota: PropTypes.number.isRequired,
204+
total: PropTypes.number.isRequired,
205+
used: PropTypes.number.isRequired,
206+
date: PropTypes.string.isRequired,
207+
status: PropTypes.string.isRequired,
208+
})
209+
),
210+
}).isRequired,
211+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// This file is part of Invenio-RDM-Records
2+
// Copyright (C) 2026 CERN.
3+
//
4+
// Invenio-RDM-Records is free software; you can redistribute it and/or modify it
5+
// under the terms of the MIT License; see LICENSE file for more details.
6+
7+
import React from "react";
8+
import ReactDOM from "react-dom";
9+
import StorageOverview from "./StorageOverview";
10+
11+
const root = document.getElementById("storage-overview-root");
12+
13+
if (root) {
14+
const storage = JSON.parse(root.dataset.storage);
15+
16+
ReactDOM.render(<StorageOverview storage={storage} />, root);
17+
}

0 commit comments

Comments
 (0)