Skip to content

Commit 34ce94a

Browse files
Merge branch 'dspace-cris-2023_02_x' into task/dspace-cris-2023_02_x/DSC-2227
2 parents 75a1833 + 1f1cfc5 commit 34ce94a

9 files changed

Lines changed: 233 additions & 24 deletions

File tree

src/app/app-routing.module.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
HEALTH_PAGE_PATH,
1818
INFO_MODULE_PATH,
1919
INTERNAL_SERVER_ERROR,
20-
LEGACY_BITSTREAM_MODULE_PATH,
20+
LEGACY_BITSTREAM_MODULE_PATH, PAGE_NOT_FOUND_PATH,
2121
PROFILE_MODULE_PATH,
2222
REGISTER_PATH,
2323
REQUEST_COPY_MODULE_PATH,
@@ -54,8 +54,8 @@ import {
5454
@NgModule({
5555
imports: [
5656
RouterModule.forRoot([
57-
{ path: INTERNAL_SERVER_ERROR, component: ThemedPageInternalServerErrorComponent },
58-
{ path: ERROR_PAGE , component: ThemedPageErrorComponent },
57+
{ path: INTERNAL_SERVER_ERROR, component: ThemedPageInternalServerErrorComponent, data: { title: INTERNAL_SERVER_ERROR } },
58+
{ path: ERROR_PAGE , component: ThemedPageErrorComponent, data: { title: ERROR_PAGE} },
5959
{
6060
path: '',
6161
canActivate: [AuthBlockingGuard],
@@ -67,7 +67,10 @@ import {
6767
path: 'reload/:rnd',
6868
component: ThemedPageNotFoundComponent,
6969
pathMatch: 'full',
70-
canActivate: [ReloadGuard]
70+
canActivate: [ReloadGuard],
71+
data: {
72+
title: PAGE_NOT_FOUND_PATH
73+
}
7174
},
7275
{
7376
path: 'home',
@@ -268,7 +271,10 @@ import {
268271
},
269272
{
270273
path: FORBIDDEN_PATH,
271-
component: ThemedForbiddenComponent
274+
component: ThemedForbiddenComponent,
275+
data: {
276+
title: FORBIDDEN_PATH
277+
}
272278
},
273279
{
274280
path: STATISTICS_PAGE_PATH,
@@ -307,7 +313,7 @@ import {
307313
loadChildren: () => import('./invitation/invitation.module')
308314
.then((m) => m.InvitationModule)
309315
},
310-
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent, canActivate: [RedirectService] },
316+
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent, canActivate: [RedirectService], data: { title: PAGE_NOT_FOUND_PATH } },
311317
]
312318
}
313319
], {

src/app/core/config/models/config-submission-access.model.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ export class SubmissionAccessModel extends ConfigObject {
3737
@autoserialize
3838
singleAccessCondition: boolean;
3939

40+
/**
41+
* Whether the field is mandatory
42+
*/
43+
@autoserialize
44+
required: boolean;
45+
4046
/**
4147
* The links to all related resources returned by the rest api.
4248
*/

src/app/core/metadata/metadata.service.spec.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ describe('MetadataService', () => {
8383
meta = jasmine.createSpyObj('meta', {
8484
updateTag: {},
8585
addTag: {},
86-
removeTag: {}
86+
removeTag: {},
87+
removeTagElement: {},
88+
getTags: ['1', '2'],
8789
});
8890
title = jasmine.createSpyObj({
8991
setTitle: {}
@@ -156,7 +158,8 @@ describe('MetadataService', () => {
156158
name: 'citation_title',
157159
content: 'Test PowerPoint Document',
158160
});
159-
expect(meta.updateTag).toHaveBeenCalledWith({ name: 'citation_author', content: 'Doe, Jane' });
161+
expect(meta.addTag).toHaveBeenCalledWith({ name: 'citation_author', content: 'Doe, Jane' });
162+
expect(meta.addTag).toHaveBeenCalledWith({ name: 'citation_author', content: 'Doe, John' });
160163
expect(meta.updateTag).toHaveBeenCalledWith({
161164
name: 'citation_publication_date',
162165
content: '1650-06-26',
@@ -169,6 +172,13 @@ describe('MetadataService', () => {
169172
});
170173
}));
171174

175+
it('items page should remove multiple tags', fakeAsync(() => {
176+
metadataService.clearMetaTags();
177+
expect(meta.getTags).toHaveBeenCalledWith('name="title"');
178+
expect(meta.getTags).toHaveBeenCalledWith('name="description"');
179+
expect(meta.removeTagElement).toHaveBeenCalledTimes(4);
180+
}));
181+
172182
it('items page should set meta tags as published Thesis', fakeAsync(() => {
173183
(metadataService as any).processRouteChange({
174184
data: {

src/app/core/metadata/metadata.service.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,7 @@ export class MetadataService {
268268
* Add <meta name="citation_author" ... > to the <head>
269269
*/
270270
private setCitationAuthorTags(): void {
271-
// limit author to first 20 entries to avoid issue with item page rendering
272-
const values: string[] = this.getMetaTagValues(['dc.author', 'dc.contributor.author', 'dc.creator'])
273-
.slice(0, this.appConfig.item.metatagLimit);
271+
const values: string[] = this.getMetaTagValues(['dc.author', 'dc.contributor.author', 'dc.creator']);
274272
this.addMetaTags('citation_author', values);
275273
}
276274

@@ -720,18 +718,19 @@ export class MetadataService {
720718
return this.currentObject.value.allMetadataValues(keys);
721719
}
722720

723-
protected addMetaTag(name: string, content: string, isProperty = false): void {
721+
protected addMetaTag(name: string, content: string, isProperty = false, isMultiple = false): void {
724722
if (content) {
725723
const tag = isProperty ? { name, property: name, content } as MetaDefinition
726724
: { name, content } as MetaDefinition;
727-
this.meta.updateTag(tag);
725+
isMultiple ? this.meta.addTag(tag) : this.meta.updateTag(tag);
728726
this.storeTag(name);
729727
}
730728
}
731729

732730
private addMetaTags(name: string, content: string[]): void {
733-
for (const value of content) {
734-
this.addMetaTag(name, value);
731+
// limit meta tags with the same name to avoid issues with page rendering
732+
for (const value of content.slice(0, this.appConfig.item.metatagLimit)) {
733+
this.addMetaTag(name, value, false, true);
735734
}
736735
}
737736

@@ -746,7 +745,9 @@ export class MetadataService {
746745
take(1)
747746
).subscribe((tagsInUse: string[]) => {
748747
for (const name of tagsInUse) {
749-
this.meta.updateTag({name, content: ''});
748+
this.meta.getTags(`name="${name}"`).forEach((tag) => {
749+
this.meta.removeTagElement(tag);
750+
});
750751
}
751752
this.store.dispatch(new ClearMetaTagAction());
752753
});

src/app/shared/mocks/item.mock.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ export const ItemMock: Item = Object.assign(new Item(), {
182182
{
183183
language: 'en_US',
184184
value: 'Doe, Jane'
185+
},
186+
{
187+
language: 'en_US',
188+
value: 'Doe, John'
185189
}
186190
],
187191
'dc.date.accessioned': [

src/app/shared/mocks/submission.mock.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3868,6 +3868,95 @@ export const mockAccessesFormData = {
38683868
]
38693869
};
38703870

3871+
export const mockAccessesServiceData = {
3872+
discoverable: true,
3873+
accessConditions: [
3874+
{
3875+
accessConditionGroup: {
3876+
name: [
3877+
{
3878+
value: 'openaccess',
3879+
language: null,
3880+
authority: null,
3881+
display: 'openaccess',
3882+
confidence: -1,
3883+
place: 0,
3884+
otherInformation: null
3885+
}
3886+
],
3887+
},
3888+
},
3889+
{
3890+
accessConditionGroup: {
3891+
name: [
3892+
{
3893+
value: 'lease',
3894+
language: null,
3895+
authority: null,
3896+
display: 'lease',
3897+
confidence: -1,
3898+
place: 0,
3899+
otherInformation: null
3900+
}
3901+
],
3902+
endDate: [
3903+
{
3904+
value: {
3905+
year: 2019,
3906+
month: 1,
3907+
day: 16
3908+
},
3909+
language: null,
3910+
authority: null,
3911+
display: {
3912+
year: 2019,
3913+
month: 1,
3914+
day: 16
3915+
},
3916+
confidence: -1,
3917+
place: 0,
3918+
otherInformation: null
3919+
}
3920+
],
3921+
}
3922+
},
3923+
{
3924+
accessConditionGroup: {
3925+
name: [
3926+
{
3927+
value: 'embargo',
3928+
language: null,
3929+
authority: null,
3930+
display: 'lease',
3931+
confidence: -1,
3932+
place: 0,
3933+
otherInformation: null
3934+
}
3935+
],
3936+
startDate: [
3937+
{
3938+
value: {
3939+
year: 2019,
3940+
month: 1,
3941+
day: 16
3942+
},
3943+
language: null,
3944+
authority: null,
3945+
display: {
3946+
year: 2019,
3947+
month: 1,
3948+
day: 16
3949+
},
3950+
confidence: -1,
3951+
place: 0,
3952+
otherInformation: null
3953+
}
3954+
],
3955+
}
3956+
}
3957+
]
3958+
};
3959+
38713960
// mockDeduplicationMatches id for Workflow decision
38723961
export const mockDeduplicationWorkflowId = '78ca1d06-cce7-4ee9-abda-46440d9b0bb7';
38733962
// mockDeduplicationMatches id for Submitter decision

src/app/submission/sections/accesses/section-accesses.component.spec.ts

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141
} from '@ng-dynamic-forms/core';
4242
import { AppState } from '../../../app.reducer';
4343
import { getMockFormService } from '../../../shared/mocks/form-service.mock';
44-
import { mockAccessesFormData } from '../../../shared/mocks/submission.mock';
44+
import { mockAccessesFormData, mockAccessesServiceData } from '../../../shared/mocks/submission.mock';
4545
import { accessConditionChangeEvent, checkboxChangeEvent } from '../../../shared/testing/form-event.stub';
4646
import { XSRFService } from '../../../core/xsrf/xsrf.service';
4747
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
@@ -120,6 +120,7 @@ describe('SubmissionSectionAccessesComponent', () => {
120120
formService.validateAllFormFields.and.callFake(() => null);
121121
formService.isValid.and.returnValue(observableOf(true));
122122
formService.getFormData.and.returnValue(observableOf(mockAccessesFormData));
123+
sectionsServiceStub.getSectionErrors.and.returnValue(observableOf(null));
123124
submissionAccessesConfigService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(accessConditionSectionConfigRes) as any);
124125
fixture.detectChanges();
125126
}));
@@ -183,6 +184,7 @@ describe('SubmissionSectionAccessesComponent', () => {
183184
formService.validateAllFormFields.and.callFake(() => null);
184185
formService.isValid.and.returnValue(observableOf(true));
185186
formService.getFormData.and.returnValue(observableOf(mockAccessesFormData));
187+
sectionsServiceStub.getSectionErrors.and.returnValue(observableOf(null));
186188
submissionAccessesConfigService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(accessConditionSectionSingleAccessConfigRes) as any);
187189
fixture.detectChanges();
188190
}));
@@ -264,6 +266,7 @@ describe('SubmissionSectionAccessesComponent', () => {
264266
formService.validateAllFormFields.and.callFake(() => null);
265267
formService.isValid.and.returnValue(observableOf(true));
266268
formService.getFormData.and.returnValue(observableOf(mockAccessesFormData));
269+
sectionsServiceStub.getSectionErrors.and.returnValue(observableOf(null));
267270
fixture.detectChanges();
268271
}));
269272

@@ -277,4 +280,82 @@ describe('SubmissionSectionAccessesComponent', () => {
277280
});
278281

279282
});
283+
284+
describe('when section is required', () => {
285+
286+
const service = getSectionAccessesService();
287+
288+
beforeEach(async () => {
289+
await TestBed.configureTestingModule({
290+
imports: [
291+
BrowserModule,
292+
TranslateModule.forRoot()
293+
],
294+
declarations: [SubmissionSectionAccessesComponent, FormComponent],
295+
providers: [
296+
{ provide: SectionsService, useValue: sectionsServiceStub },
297+
{ provide: SubmissionAccessesConfigDataService, useValue: submissionAccessesConfigService },
298+
{ provide: SectionAccessesService, useValue: service },
299+
{ provide: SectionFormOperationsService, useValue: sectionFormOperationsService },
300+
{ provide: JsonPatchOperationsBuilder, useValue: operationsBuilder },
301+
{ provide: TranslateService, useValue: getMockTranslateService() },
302+
{ provide: FormService, useValue: getMockFormService() },
303+
{ provide: Store, useValue: storeStub },
304+
{ provide: XSRFService, useValue: {} },
305+
{ provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub },
306+
{ provide: 'sectionDataProvider', useValue: sectionData },
307+
{ provide: 'submissionIdProvider', useValue: '1508' },
308+
FormBuilderService
309+
]
310+
})
311+
.compileComponents();
312+
});
313+
314+
beforeEach(inject([Store], (store: Store<AppState>) => {
315+
fixture = TestBed.createComponent(SubmissionSectionAccessesComponent);
316+
component = fixture.componentInstance;
317+
formService = TestBed.inject(FormService);
318+
formService.validateAllFormFields.and.callFake(() => null);
319+
formService.isValid.and.returnValue(observableOf(true));
320+
formService.getFormData.and.returnValue(observableOf(mockAccessesFormData));
321+
sectionsServiceStub.getSectionErrors.and.returnValue(observableOf(null));
322+
submissionAccessesConfigService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(accessConditionSectionConfigRes) as any);
323+
fixture.detectChanges();
324+
}));
325+
326+
it('should have section status invalid on init', (done) => {
327+
component.required$.next(true);
328+
service.getAccessesData.and.returnValue(observableOf({}));
329+
fixture.detectChanges();
330+
331+
component.getSectionStatus().subscribe(status => {
332+
expect(status).toEqual(false);
333+
done();
334+
});
335+
});
336+
337+
it('should have section status valid if there are data and no error', (done) => {
338+
service.getAccessesData.and.returnValue(observableOf(mockAccessesServiceData));
339+
component.required$.next(true);
340+
fixture.detectChanges();
341+
342+
component.getSectionStatus().subscribe(status => {
343+
expect(status).toEqual(true);
344+
done();
345+
});
346+
});
347+
348+
it('should have section status invalid if there are data and errors', (done) => {
349+
service.getAccessesData.and.returnValue(observableOf(mockAccessesServiceData));
350+
component.required$.next(true);
351+
sectionsServiceStub.getSectionErrors.and.returnValue(observableOf({error: 'testError'}));
352+
353+
fixture.detectChanges();
354+
355+
component.getSectionStatus().subscribe(status => {
356+
expect(status).toEqual(false);
357+
done();
358+
});
359+
});
360+
});
280361
});

0 commit comments

Comments
 (0)