Skip to content

Commit 5e8e9f9

Browse files
authored
Merge pull request #522 from digirati-co-uk/feature/further-fixes
Feb fixes
2 parents 305481b + 7ee55e1 commit 5e8e9f9

29 files changed

+226
-100
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Added email server status to system status page
1414
- Added configuration option to allow new users to be contributors
1515
- Added alpha page block for canvas panel
16+
- Added removal of Manifest JSON saved to disk after import (no longer used)
17+
- Added "source manifest" to admin
18+
- Added the ability to specify a reason for rejecting a contribution
1619

1720
### Fixed
1821
- Fixed inconsistencies in translations

services/madoc-ts/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"cross-fetch": "^3.0.4",
8484
"css-loader": "^4.2.2",
8585
"deepmerge": "^4.2.2",
86+
"del": "^6.0.0",
8687
"easy-peasy": "^3.3.0",
8788
"email-validator": "^2.0.4",
8889
"form-data": "^4.0.0",

services/madoc-ts/src/database/queries/get-manifest-snippets.ts

+31-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { sql, TaggedTemplateLiteralInvocationType } from 'slonik';
2-
import { metadataReducer } from '../../utility/iiif-metadata';
2+
import { getViewingDirection } from '../../utility/get-viewing-direction';
3+
import { createMetadataReducer, MetadataField, metadataReducer } from '../../utility/iiif-metadata';
34
import { SQL_EMPTY, SQL_INT_ARRAY } from '../../utility/postgres-tags';
45

56
type ManifestAggregate = TaggedTemplateLiteralInvocationType<{
@@ -36,15 +37,22 @@ export function getSingleManifest({
3637
canvas_thumbnail: string;
3738
task_id?: string;
3839
task_complete?: boolean;
40+
rights: string | null;
41+
viewing_direction: number;
42+
source: string;
3943
}>`
4044
select ${manifestId}::int as manifest_id,
4145
canvas_links.item_id as canvas_id,
4246
manifest_count.item_total as canvas_count,
4347
canvas_resources.default_thumbnail as canvas_thumbnail,
4448
manifest.task_id as task_id,
4549
manifest.task_complete as task_complete,
46-
manifest.published as published
50+
manifest.published as published,
51+
manifest_resource.rights as rights,
52+
manifest_resource.viewing_direction as viewing_direction,
53+
manifest_resource.source as source
4754
from iiif_derived_resource manifest
55+
left join iiif_resource manifest_resource on manifest.resource_id = manifest_resource.id
4856
left join iiif_derived_resource_items canvas_links on manifest.resource_id = canvas_links.resource_id and canvas_links.site_id = ${siteId} ${canvasExclusion}
4957
left join iiif_resource canvas_resources on canvas_links.item_id = canvas_resources.id
5058
left join iiif_derived_resource_item_counts manifest_count
@@ -71,6 +79,11 @@ export type ManifestSnippetsRow = {
7179
source: string;
7280
resource_id: number;
7381
canvas_count?: number;
82+
83+
// Other properties.
84+
manifest_source: string | null;
85+
manifest_rights: string | null;
86+
manifest_viewing_direction: number;
7487
};
7588

7689
export function getManifestSnippets(
@@ -85,14 +98,17 @@ export function getManifestSnippets(
8598
manifest_aggregate.canvas_thumbnail as canvas_thumbnail,
8699
manifest_aggregate.canvas_id as canvas_id,
87100
manifest_aggregate.canvas_count as canvas_count,
88-
manifest_aggregate.m_published as published,
101+
manifest_aggregate.published as published,
102+
manifest_aggregate.rights as manifest_rights,
103+
manifest_aggregate.viewing_direction as manifest_viewing_direction,
104+
manifest_aggregate.source as manifest_source,
89105
metadata.id as metadata_id,
90106
metadata.key as key,
91107
metadata.value as value,
92108
metadata.language as language,
93109
metadata.source as source,
94110
metadata.resource_id as resource_id
95-
from (${query}) manifest_aggregate(manifest_id, canvas_id, canvas_count, canvas_thumbnail, task_id, task_complete, m_published)
111+
from (${query}) manifest_aggregate(manifest_id, canvas_id, canvas_count, canvas_thumbnail, task_id, task_complete, published, rights, viewing_direction, source)
96112
left join iiif_metadata metadata
97113
on (manifest_aggregate.canvas_id = metadata.resource_id or
98114
manifest_aggregate.manifest_id = metadata.resource_id)
@@ -241,6 +257,16 @@ export function getManifestList({
241257
`;
242258
}
243259

260+
export const manifestReducer = createMetadataReducer((next: MetadataField & ManifestSnippetsRow) => ({
261+
id: next.resource_id,
262+
type: next.resource_type,
263+
created: next.created_at,
264+
thumbnail: next.thumbnail,
265+
published: next.published,
266+
source: next.manifest_source,
267+
viewingDirection: getViewingDirection(next.manifest_viewing_direction),
268+
}));
269+
244270
export function mapManifestSnippets(rows: readonly ManifestSnippetsRow[]) {
245271
return rows.reduce(
246272
(state, row) => {
@@ -282,7 +308,7 @@ export function mapManifestSnippets(rows: readonly ManifestSnippetsRow[]) {
282308
return {
283309
manifest_to_canvas: state.manifest_to_canvas,
284310
canvases: state.canvases,
285-
manifests: metadataReducer(state.manifests, row),
311+
manifests: manifestReducer(state.manifests, row),
286312
metadata_ids: state.metadata_ids,
287313
};
288314
},

services/madoc-ts/src/extensions/capture-models/crowdsourcing-api.ts

+5
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,12 @@ export class CrowdsourcingApi implements BaseExtension {
278278
async reviewRejectSubmission({
279279
revisionRequest,
280280
userTaskId,
281+
message,
281282
statusText,
282283
}: {
283284
revisionRequest: RevisionRequest;
284285
userTaskId: string;
286+
message?: string;
285287
statusText?: string;
286288
}) {
287289
try {
@@ -295,6 +297,9 @@ export class CrowdsourcingApi implements BaseExtension {
295297
await this.api.updateTask<CrowdsourcingTask>(userTaskId, {
296298
status: -1,
297299
status_text: statusText || 'Rejected',
300+
state: {
301+
rejectedMessage: message,
302+
},
298303
});
299304
}
300305

services/madoc-ts/src/frontend/admin/pages/content/manifests/manifest.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export const ManifestView: UniversalComponent<ManifestViewType> = createUniversa
2525
const { resolvedData } = usePaginatedData(ManifestView);
2626
const site = useSite();
2727
const { manifest, pagination } = resolvedData || {};
28-
2928
const title = manifest ? <LocaleString>{manifest.label}</LocaleString> : '...';
3029

3130
return (
@@ -37,6 +36,15 @@ export const ManifestView: UniversalComponent<ManifestViewType> = createUniversa
3736
{t('{{count}} canvases', { count: pagination ? pagination.totalResults : 0 })}
3837
{' | '}
3938
<a href={`/s/${site.slug}/manifests/${id}`}>{t('View on site')}</a>
39+
{manifest && manifest.source ? (
40+
<>
41+
<br />
42+
{t('Source manifest')}{' '}
43+
<a href={manifest.source} target="_blank" rel="noreferrer">
44+
{manifest.source}
45+
</a>
46+
</>
47+
) : null}
4048
</>
4149
}
4250
breadcrumbs={[

services/madoc-ts/src/frontend/site/hooks/use-crowdsourcing-task-details.ts

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export function useCrowdsourcingTaskDetails(task: CrowdsourcingTask & { id: stri
2828
task.state.warningTime &&
2929
date - task.modified_at > task.state.warningTime;
3030
const changesRequested = task.status !== 3 && task.state?.changesRequested ? task.state?.changesRequested : undefined;
31+
const rejectedMessage = task.state?.rejectedMessage ? task.state?.rejectedMessage : undefined;
3132

3233
const target = useMemo(() => {
3334
if (captureModel && captureModel.target && captureModel.target[0]) {
@@ -81,5 +82,6 @@ export function useCrowdsourcingTaskDetails(task: CrowdsourcingTask & { id: stri
8182
target,
8283
captureModel,
8384
subject,
85+
rejectedMessage,
8486
};
8587
}

services/madoc-ts/src/frontend/site/pages/tasks/actions/reject-submission.tsx

+31-16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TextField } from '@capture-models/editor/lib/input-types/TextField/TextField';
12
import React, { useCallback, useState } from 'react';
23
import { useApi } from '../../../../shared/hooks/use-api';
34
import { Revisions } from '@capture-models/editor';
@@ -13,28 +14,33 @@ import { DeleteForeverIcon } from '../../../../shared/icons/DeleteForeverIcon';
1314
export const RejectSubmission: React.FC<{ onReject: () => void; userTaskId: string }> = ({ onReject, userTaskId }) => {
1415
const api = useApi();
1516
const [isLoading, setIsLoading] = useState(false);
17+
const [reasonMessage, setReasonMessage] = useState('');
1618
const { currentRevision } = Revisions.useStoreState(state => {
1719
return {
1820
currentRevision: state.currentRevision,
1921
};
2022
});
2123
const deselectRevision = Revisions.useStoreActions(a => a.deselectRevision);
2224

23-
const rejectApiCall = useCallback(() => {
24-
if (currentRevision) {
25-
setIsLoading(true);
26-
api
27-
.reviewRejectSubmission({
28-
revisionRequest: currentRevision,
29-
userTaskId,
30-
})
31-
.then(() => {
32-
deselectRevision({ revisionId: currentRevision.revision.id });
33-
setIsLoading(false);
34-
onReject();
35-
});
36-
}
37-
}, [api, currentRevision, deselectRevision, onReject, userTaskId]);
25+
const rejectApiCall = useCallback(
26+
(message: string) => {
27+
if (currentRevision) {
28+
setIsLoading(true);
29+
api
30+
.reviewRejectSubmission({
31+
revisionRequest: currentRevision,
32+
userTaskId,
33+
message,
34+
})
35+
.then(() => {
36+
deselectRevision({ revisionId: currentRevision.revision.id });
37+
setIsLoading(false);
38+
onReject();
39+
});
40+
}
41+
},
42+
[api, currentRevision, deselectRevision, onReject, userTaskId]
43+
);
3844

3945
if (!currentRevision || currentRevision.revision.approved) {
4046
return null;
@@ -54,14 +60,23 @@ export const RejectSubmission: React.FC<{ onReject: () => void; userTaskId: stri
5460
<li>The user will be notified that the revision has been rejected</li>
5561
<li>You will no longer be able to see the content in the revision</li>
5662
</ul>
63+
<label htmlFor="message">Write a message to the contributor</label>
64+
<TextField
65+
id="message"
66+
type="text-field"
67+
value={reasonMessage}
68+
label="Write message to the contributor"
69+
updateValue={setReasonMessage}
70+
multiline={true}
71+
/>
5772
</div>
5873
)}
5974
renderFooter={({ close }: any) => (
6075
<Button
6176
style={{ marginLeft: 'auto' }}
6277
onClick={() => {
6378
close();
64-
rejectApiCall();
79+
rejectApiCall(reasonMessage);
6580
}}
6681
>
6782
Reject changes

services/madoc-ts/src/frontend/site/pages/tasks/crowdsourcing-task.tsx

+12-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { SimpleSaveButton } from '../../../shared/caputre-models/new/components/
1010
import '@capture-models/editor/lib/input-types/TextField';
1111
import '@capture-models/editor/lib/input-types/HTMLField';
1212
import { CrowdsourcingTask } from '../../../../gateway/tasks/crowdsourcing-task';
13+
import { Heading4 } from '../../../shared/typography/Heading4';
1314
import { HrefLink } from '../../../shared/utility/href-link';
1415
import { useCrowdsourcingTaskDetails } from '../../hooks/use-crowdsourcing-task-details';
1516
import { TaskContext } from '../loaders/task-loader';
@@ -33,6 +34,7 @@ const ViewCrowdSourcingTask: React.FC<TaskContext<CrowdsourcingTask>> = ({ task,
3334
revisionId,
3435
wasRejected,
3536
changesRequested,
37+
rejectedMessage,
3638
} = useCrowdsourcingTaskDetails(task, parentTask);
3739

3840
if (!isCanvas) {
@@ -62,9 +64,17 @@ const ViewCrowdSourcingTask: React.FC<TaskContext<CrowdsourcingTask>> = ({ task,
6264
</SuccessMessage>
6365
) : null}
6466
{wasRejected ? (
65-
<ErrorMessage>
67+
<ErrorMessage $banner>
6668
{t('This contribution was rejected. You can make another contribution from the')}{' '}
6769
{backLink ? <Link to={backLink}>{t('Image page')}</Link> : t('Image page')}
70+
{rejectedMessage ? (
71+
<>
72+
<div style={{ marginTop: '.5em' }}>
73+
<strong>{t('Message from reviewer')}</strong>
74+
</div>
75+
<p style={{ marginTop: '0.4em' }}>{rejectedMessage}</p>
76+
</>
77+
) : null}
6878
</ErrorMessage>
6979
) : null}
7080
{isSubmitted ? <WarningMessage>{t('Your submission is in review')}</WarningMessage> : null}
@@ -82,7 +92,7 @@ const ViewCrowdSourcingTask: React.FC<TaskContext<CrowdsourcingTask>> = ({ task,
8292
</RevisionProviderWithFeatures>
8393
) : null}
8494

85-
{!isSubmitted && !isComplete && editLink ? (
95+
{!isSubmitted && !isComplete && editLink && !wasRejected ? (
8696
<div>
8797
<Button $primary as={HrefLink} href={editLink}>
8898
{t('Edit submission')}

services/madoc-ts/src/gateway/api.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -871,12 +871,11 @@ export class ApiClient {
871871
return this.request<{ manifests: number; canvases: number }>(`/api/madoc/iiif/collections/${id}/statistics`);
872872
}
873873

874-
async createManifest(manifest: Partial<Manifest>, source?: string, taskId?: string) {
874+
async createManifest(manifest: Partial<Manifest>, taskId?: string) {
875875
return this.request<{ id: number }, CreateManifest>(`/api/madoc/iiif/manifests`, {
876876
method: 'POST',
877877
body: {
878878
manifest,
879-
local_source: source,
880879
taskId,
881880
},
882881
});
@@ -887,7 +886,6 @@ export class ApiClient {
887886
method: 'POST',
888887
body: {
889888
canvas,
890-
local_source: source,
891889
thumbnail,
892890
},
893891
});
@@ -1232,7 +1230,12 @@ export class ApiClient {
12321230
}
12331231

12341232
// Review API
1235-
async reviewRejectSubmission(options: { revisionRequest: RevisionRequest; userTaskId: string; statusText?: string }) {
1233+
async reviewRejectSubmission(options: {
1234+
revisionRequest: RevisionRequest;
1235+
message?: string;
1236+
userTaskId: string;
1237+
statusText?: string;
1238+
}) {
12361239
return this.crowdsourcing.reviewRejectSubmission(options);
12371240
}
12381241

services/madoc-ts/src/gateway/tasks/crowdsourcing-task.ts

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface CrowdsourcingTask extends BaseTask {
5858
reviewTask?: string;
5959
// Can start adding to this as we need.
6060
changesRequested?: string | null;
61+
rejectedMessage?: string | null;
6162
mergeId?: string;
6263
warningTime?: number;
6364
userManifestTask?: string | null;

services/madoc-ts/src/gateway/tasks/import-canvas.ts

+2-13
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { BaseTask } from './base-task';
22
import * as tasks from './task-helpers';
33
import { iiifGetLabel } from '../../utility/iiif-get-label';
44
import { ApiClient } from '../api';
5-
import { ContentResource } from '@hyperion-framework/types';
65

76
export const type = 'madoc-canvas-import';
87

@@ -64,19 +63,10 @@ export const jobHandler = async (name: string, taskId: string, api: ApiClient) =
6463

6564
const [userId, pathToManifest, manifestId, siteId] = task.parameters;
6665

67-
const idHash = tasks.manifestHash(manifestId);
68-
69-
const { manifest, unmodifiedManifest, canvas, vault } = await tasks.tryGetManifest(
70-
manifestId,
71-
pathToManifest,
72-
task.subject
73-
);
66+
const { manifest, canvas, vault } = await tasks.tryGetManifest(manifestId, pathToManifest, task.subject);
7467

7568
const idList = (manifest.items || []).map(r => r.id);
7669
const canvasOrder = idList.indexOf(canvas.id);
77-
const canvasJson = tasks.getCanvasFromManifest(unmodifiedManifest, canvas.id);
78-
79-
const fileLocation = await tasks.writeCanvasToDisk(idHash, canvasJson, canvasOrder);
8070

8171
const thumbnail = await tasks.getThumbnail(vault, canvas);
8272
const thumbId = thumbnail && thumbnail.id ? thumbnail.id : undefined;
@@ -124,8 +114,7 @@ export const jobHandler = async (name: string, taskId: string, api: ApiClient) =
124114
rights: canvas.rights || undefined,
125115
navDate: canvas.navDate || undefined,
126116
},
127-
thumbId,
128-
fileLocation
117+
thumbId
129118
);
130119
if (item) {
131120
break;

0 commit comments

Comments
 (0)