Skip to content

Commit 4d14d68

Browse files
authored
Merge pull request DSpace#4368 from atmire/w2p-130253_replace-bitstreams-from-submission_contribute-main
Add bitstream replacement page and submission modal
2 parents 22b328a + 0535fd3 commit 4d14d68

27 files changed

Lines changed: 1652 additions & 26 deletions

src/app/bitstream-page/bitstream-page-routes.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ import { bitstreamPageResolver } from './bitstream-page.resolver';
1414
import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard';
1515
import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component';
1616
import { legacyBitstreamURLRedirectGuard } from './legacy-bitstream-url-redirect.guard';
17+
import { ReplaceBitstreamPageComponent } from './replace-bitstream-page/replace-bitstream-page.component';
18+
import { replaceBitstreamPageGuard } from './replace-bitstream-page/replace-bitstream-page.guard';
1719

1820
const EDIT_BITSTREAM_PATH = ':id/edit';
1921
const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
22+
const REPLACE_BITSTREAM_PATH = ':id/replace';
2023

2124
/**
2225
* Routing module to help navigate Bitstream pages
@@ -52,6 +55,20 @@ export const ROUTES: Route[] = [
5255
},
5356
canActivate: [authenticatedGuard],
5457
},
58+
{
59+
path: REPLACE_BITSTREAM_PATH,
60+
component: ReplaceBitstreamPageComponent,
61+
resolve: {
62+
bitstream: bitstreamPageResolver,
63+
breadcrumb: i18nBreadcrumbResolver,
64+
},
65+
canActivate: [authenticatedGuard, replaceBitstreamPageGuard],
66+
data: {
67+
title: 'bitstream.replace.page.title',
68+
breadcrumbKey: 'bitstream.replace.page',
69+
showBreadcrumbs: true,
70+
},
71+
},
5572
{
5673
path: EDIT_BITSTREAM_AUTHORIZATIONS_PATH,
5774
canActivate: [bitstreamPageAuthorizationsGuard],
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@let relatedBitstreams = relatedBitstreams$ | async;
2+
@if (relatedBitstreams.length > 0) {
3+
<div class="alert alert-info w-100 mb-2" role="alert">
4+
<ul class="list-unstyled mb-0">
5+
@for (section of relatedBitstreams; let i = $index; track section[0]) {
6+
<li>
7+
<span>{{ 'bitstream.related.' + section[0] | translate }}</span>:
8+
@for (bitstreamEntry of section[1]; track bitstreamEntry.uuid; let last = $last) {
9+
@if (bitstreamEntry.error === undefined) {
10+
<a [routerLink]="['/bitstreams/', bitstreamEntry.uuid, 'edit']"
11+
[target]="'_blank'">
12+
{{ bitstreamEntry.text }}</a>
13+
} @else {
14+
<span>{{ bitstreamEntry.text }} (<code>{{ bitstreamEntry.uuid }}</code>, {{ 'bitstream.related.deleted' | translate }})</span>
15+
}
16+
@if (!last) {
17+
<span>,</span>
18+
}
19+
}
20+
</li>
21+
}
22+
</ul>
23+
</div>
24+
}

src/app/bitstream-page/edit-bitstream-page-alerts/edit-bitstream-page-alerts.component.scss

Whitespace-only changes.
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
import { NO_ERRORS_SCHEMA } from '@angular/core';
2+
import {
3+
ComponentFixture,
4+
TestBed,
5+
waitForAsync,
6+
} from '@angular/core/testing';
7+
import { By } from '@angular/platform-browser';
8+
import { RouterTestingModule } from '@angular/router/testing';
9+
import { BitstreamDataService } from '@dspace/core/data/bitstream-data.service';
10+
import { Bitstream } from '@dspace/core/shared/bitstream.model';
11+
import { BitstreamFormat } from '@dspace/core/shared/bitstream-format.model';
12+
import { BitstreamFormatSupportLevel } from '@dspace/core/shared/bitstream-format-support-level';
13+
import { Item } from '@dspace/core/shared/item.model';
14+
import { MetadataValueFilter } from '@dspace/core/shared/metadata.models';
15+
import { createSuccessfulRemoteDataObject$ } from '@dspace/core/utilities/remote-data.utils';
16+
import { TranslateModule } from '@ngx-translate/core';
17+
18+
import { FileSizePipe } from '../../shared/utils/file-size-pipe';
19+
import { VarDirective } from '../../shared/utils/var.directive';
20+
import { EditBitstreamPageAlertsComponent } from './edit-bitstream-page-alerts.component';
21+
22+
let bitstreamService: BitstreamDataService;
23+
let bitstream: Bitstream;
24+
let bitstreamID: string;
25+
let selectedFormat: BitstreamFormat;
26+
let allFormats: BitstreamFormat[];
27+
let bundle;
28+
let comp: EditBitstreamPageAlertsComponent;
29+
let fixture: ComponentFixture<EditBitstreamPageAlertsComponent>;
30+
31+
describe('EditBitstreamPageAlertsComponent', () => {
32+
33+
beforeEach(() => {
34+
bitstreamID = 'current-bitstream-id';
35+
allFormats = [
36+
Object.assign({
37+
id: '1',
38+
shortDescription: 'Unknown',
39+
description: 'Unknown format',
40+
supportLevel: BitstreamFormatSupportLevel.Unknown,
41+
mimetype: 'application/octet-stream',
42+
_links: {
43+
self: { href: 'format-selflink-1' },
44+
},
45+
}),
46+
Object.assign({
47+
id: '2',
48+
shortDescription: 'PNG',
49+
description: 'Portable Network Graphics',
50+
supportLevel: BitstreamFormatSupportLevel.Known,
51+
mimetype: 'image/png',
52+
_links: {
53+
self: { href: 'format-selflink-2' },
54+
},
55+
}),
56+
Object.assign({
57+
id: '3',
58+
shortDescription: 'GIF',
59+
description: 'Graphics Interchange Format',
60+
supportLevel: BitstreamFormatSupportLevel.Known,
61+
mimetype: 'image/gif',
62+
_links: {
63+
self: { href: 'format-selflink-3' },
64+
},
65+
}),
66+
] as BitstreamFormat[];
67+
selectedFormat = allFormats[1];
68+
69+
bundle = {
70+
_links: {
71+
primaryBitstream: {
72+
href: 'bundle-selflink',
73+
},
74+
},
75+
item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
76+
uuid: 'some-uuid',
77+
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
78+
return undefined;
79+
},
80+
})),
81+
};
82+
});
83+
84+
describe('when the bitstream has no replacement reference metadata', () => {
85+
beforeEach(waitForAsync(() => {
86+
bundle = {
87+
_links: {
88+
primaryBitstream: {
89+
href: 'bundle-selflink',
90+
},
91+
},
92+
item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
93+
uuid: 'some-uuid',
94+
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
95+
return undefined;
96+
},
97+
})),
98+
};
99+
bitstream = Object.assign(new Bitstream(), {
100+
uuid: bitstreamID,
101+
id: bitstreamID,
102+
metadata: {
103+
'dc.description': [
104+
{
105+
value: 'Bitstream description',
106+
},
107+
],
108+
'dc.title': [
109+
{
110+
value: 'Bitstream title',
111+
},
112+
],
113+
},
114+
format: createSuccessfulRemoteDataObject$(selectedFormat),
115+
_links: {
116+
self: 'bitstream-selflink',
117+
},
118+
bundle: createSuccessfulRemoteDataObject$(bundle),
119+
});
120+
bitstreamService = jasmine.createSpyObj('bitstreamService', {
121+
findById: createSuccessfulRemoteDataObject$(bitstream),
122+
findByHref: createSuccessfulRemoteDataObject$(bitstream),
123+
update: createSuccessfulRemoteDataObject$(bitstream),
124+
updateFormat: createSuccessfulRemoteDataObject$(bitstream),
125+
commitUpdates: {},
126+
patch: {},
127+
});
128+
129+
TestBed.configureTestingModule({
130+
imports: [TranslateModule.forRoot(), RouterTestingModule, EditBitstreamPageAlertsComponent, FileSizePipe, VarDirective],
131+
providers: [
132+
{ provide: BitstreamDataService, useValue: bitstreamService },
133+
],
134+
schemas: [NO_ERRORS_SCHEMA],
135+
}).compileComponents();
136+
}));
137+
138+
beforeEach(() => {
139+
fixture = TestBed.createComponent(EditBitstreamPageAlertsComponent);
140+
comp = fixture.componentInstance;
141+
comp.bitstream = bitstream;
142+
fixture.detectChanges();
143+
});
144+
145+
it('no alert should be shown', () => {
146+
const alert = fixture.debugElement.query(By.css('.alert-info'));
147+
expect(alert).toBeFalsy();
148+
});
149+
});
150+
151+
describe('when the bitstream has has two replacement reference values', () => {
152+
beforeEach(waitForAsync(() => {
153+
bundle = {
154+
_links: {
155+
primaryBitstream: {
156+
href: 'bundle-selflink',
157+
},
158+
},
159+
item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
160+
uuid: 'some-uuid',
161+
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
162+
return undefined;
163+
},
164+
})),
165+
};
166+
bitstream = Object.assign(new Bitstream(), {
167+
uuid: bitstreamID,
168+
id: bitstreamID,
169+
metadata: {
170+
'dc.description': [
171+
{
172+
value: 'Bitstream description',
173+
},
174+
],
175+
'dc.title': [
176+
{
177+
value: 'Bitstream title',
178+
},
179+
],
180+
'dspace.bitstream.isCopyOf': [
181+
{
182+
value: 'Related Bitstream title 1',
183+
authority: 'related-uuid1',
184+
},
185+
],
186+
'dspace.bitstream.isReplacedBy': [
187+
{
188+
value: 'Related Bitstream title 2',
189+
authority: 'related-uuid2',
190+
},
191+
],
192+
},
193+
format: createSuccessfulRemoteDataObject$(selectedFormat),
194+
_links: {
195+
self: 'bitstream-selflink',
196+
},
197+
bundle: createSuccessfulRemoteDataObject$(bundle),
198+
});
199+
const relatedBitstream1 = Object.assign(new Bitstream(), {
200+
uuid: 'related-uuid1',
201+
id: 'related-uuid1',
202+
metadata: {
203+
'dc.description': [
204+
{
205+
value: 'Bitstream description',
206+
},
207+
],
208+
'dc.title': [
209+
{
210+
value: 'Related Bitstream title 1',
211+
},
212+
],
213+
},
214+
format: createSuccessfulRemoteDataObject$(selectedFormat),
215+
_links: {
216+
self: 'bitstream-selflink',
217+
},
218+
bundle: createSuccessfulRemoteDataObject$(bundle),
219+
});
220+
const relatedBitstream2 = Object.assign(new Bitstream(), {
221+
uuid: 'related-uuid2',
222+
id: 'related-uuid2',
223+
metadata: {
224+
'dc.description': [
225+
{
226+
value: 'Bitstream description',
227+
},
228+
],
229+
'dc.title': [
230+
{
231+
value: 'Related Bitstream title 2',
232+
},
233+
],
234+
},
235+
format: createSuccessfulRemoteDataObject$(selectedFormat),
236+
_links: {
237+
self: 'bitstream-selflink',
238+
},
239+
bundle: createSuccessfulRemoteDataObject$(bundle),
240+
});
241+
bitstreamService = jasmine.createSpyObj('bitstreamService', {
242+
findById: createSuccessfulRemoteDataObject$(bitstream),
243+
findByHref: createSuccessfulRemoteDataObject$(bitstream),
244+
update: createSuccessfulRemoteDataObject$(bitstream),
245+
updateFormat: createSuccessfulRemoteDataObject$(bitstream),
246+
commitUpdates: {},
247+
patch: {},
248+
});
249+
(bitstreamService as any).findById.and.callFake((id: string) => {
250+
if (id === 'related-uuid1') {
251+
return createSuccessfulRemoteDataObject$(relatedBitstream1);
252+
} else if (id === 'related-uuid2') {
253+
return createSuccessfulRemoteDataObject$(relatedBitstream2);
254+
}
255+
});
256+
TestBed.configureTestingModule({
257+
imports: [TranslateModule.forRoot(), RouterTestingModule, EditBitstreamPageAlertsComponent, FileSizePipe, VarDirective],
258+
providers: [
259+
{ provide: BitstreamDataService, useValue: bitstreamService },
260+
],
261+
schemas: [NO_ERRORS_SCHEMA],
262+
}).compileComponents();
263+
}));
264+
265+
beforeEach(() => {
266+
fixture = TestBed.createComponent(EditBitstreamPageAlertsComponent);
267+
comp = fixture.componentInstance;
268+
comp.bitstream = bitstream;
269+
fixture.detectChanges();
270+
});
271+
272+
it('both values should be shown in an alert, only one as link', () => {
273+
fixture.detectChanges();
274+
275+
const links = fixture.debugElement.queryAll(By.css('.alert-info a'));
276+
expect(links).toBeTruthy();
277+
expect(links.length).toBe(2);
278+
expect(links[0].nativeElement.textContent).toContain('Related Bitstream title 1');
279+
expect(links[0].nativeElement.href).toContain('related-uuid1');
280+
expect(links[1].nativeElement.textContent).toContain('Related Bitstream title 2');
281+
expect(links[1].nativeElement.href).toContain('related-uuid2');
282+
});
283+
});
284+
285+
});

0 commit comments

Comments
 (0)