From 48ad0fc8ae67f0d0aae5d586efadb52aa3316f04 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Fri, 31 Dec 2021 11:03:47 +0530 Subject: [PATCH 001/195] [DSC-337] boxes flagged as minor appears in tabs even when are the only available. --- .../cris-layout-metadata-box.component.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html index 9464101311f..f7b031ee659 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html @@ -1,10 +1,12 @@
-
+
+
+
From d5e41e559c52113466e69bd031514f49478af04f Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Tue, 4 Jan 2022 12:16:23 +0530 Subject: [PATCH 002/195] [DSC-337] hide the tab when it's all the box have minor. --- .../cris-layout-metadata-box.component.html | 14 ++++----- src/app/cris-layout/cris-layout.component.ts | 29 +++++++++++++++++-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html index f7b031ee659..9464101311f 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/cris-layout-metadata-box.component.html @@ -1,12 +1,10 @@
-
-
-
+
diff --git a/src/app/cris-layout/cris-layout.component.ts b/src/app/cris-layout/cris-layout.component.ts index ac1ba0198f7..e952f634a53 100644 --- a/src/app/cris-layout/cris-layout.component.ts +++ b/src/app/cris-layout/cris-layout.component.ts @@ -76,7 +76,7 @@ export class CrisLayoutComponent implements OnInit { */ getLeadingTabs(): Observable { return this.tabs$.pipe( - map((tabs: CrisLayoutTab[]) => tabs.filter(tab => tab.leading)), + map((tabs: CrisLayoutTab[]) => tabs.filter(tab => this.checkForMinor(tab,tab.leading))), ); } @@ -85,7 +85,7 @@ export class CrisLayoutComponent implements OnInit { */ getLoaderTabs(): Observable { return this.tabs$.pipe( - map((tabs: CrisLayoutTab[]) => tabs.filter(tab => !tab.leading)), + map((tabs: CrisLayoutTab[]) => tabs.filter(tab => this.checkForMinor(tab,!tab.leading))), ); } @@ -98,4 +98,29 @@ export class CrisLayoutComponent implements OnInit { ); } + /** + * + * @param tab Contains a tab data which has rows, cells and boxes + * @param isLeading Contains a boolean + * @returns Boolean based on cells has minor or not + */ + checkForMinor(tab: CrisLayoutTab,isLeading: boolean): boolean { + if (isLeading) { + let isMinor = true; + for (const row of tab.rows) { + rowLoop: + for (const cell of row.cells) { + for (const box of cell.boxes) { + if (!box.minor) { + isMinor = false; + break rowLoop; + } + } + } + } + return !isMinor; + } + return false; + } + } From f03d3fecf293a1ba55c30161f62ddd99067501c6 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Tue, 4 Jan 2022 16:40:22 +0530 Subject: [PATCH 003/195] [DSC-337] code refactor. --- src/app/cris-layout/cris-layout.component.ts | 30 +++++++------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/app/cris-layout/cris-layout.component.ts b/src/app/cris-layout/cris-layout.component.ts index e952f634a53..26d751132d7 100644 --- a/src/app/cris-layout/cris-layout.component.ts +++ b/src/app/cris-layout/cris-layout.component.ts @@ -76,7 +76,7 @@ export class CrisLayoutComponent implements OnInit { */ getLeadingTabs(): Observable { return this.tabs$.pipe( - map((tabs: CrisLayoutTab[]) => tabs.filter(tab => this.checkForMinor(tab,tab.leading))), + map((tabs: CrisLayoutTab[]) => tabs.filter(tab => tab.leading).filter(tab => this.checkForMinor(tab))), ); } @@ -85,7 +85,7 @@ export class CrisLayoutComponent implements OnInit { */ getLoaderTabs(): Observable { return this.tabs$.pipe( - map((tabs: CrisLayoutTab[]) => tabs.filter(tab => this.checkForMinor(tab,!tab.leading))), + map((tabs: CrisLayoutTab[]) => tabs.filter(tab => !tab.leading ).filter(tab => this.checkForMinor(tab))), ); } @@ -99,28 +99,20 @@ export class CrisLayoutComponent implements OnInit { } /** - * * @param tab Contains a tab data which has rows, cells and boxes - * @param isLeading Contains a boolean * @returns Boolean based on cells has minor or not */ - checkForMinor(tab: CrisLayoutTab,isLeading: boolean): boolean { - if (isLeading) { - let isMinor = true; - for (const row of tab.rows) { - rowLoop: - for (const cell of row.cells) { - for (const box of cell.boxes) { - if (!box.minor) { - isMinor = false; - break rowLoop; - } - } + checkForMinor(tab: CrisLayoutTab): boolean { + for (const row of tab.rows) { + for (const cell of row.cells) { + for (const box of cell.boxes) { + if (!box.minor) { + return true; } } - return !isMinor; - } - return false; + } } + return false; +} } From fd834463ea4edb4ed41f56cc8345439a2c69ddc5 Mon Sep 17 00:00:00 2001 From: mushrankhan-kencor <83634419+mushrankhan-kencor@users.noreply.github.com> Date: Tue, 1 Feb 2022 14:02:04 +0530 Subject: [PATCH 004/195] [DSC-337] test case added for minor element. --- .../cris-layout/cris-layout.component.spec.ts | 13 +- src/app/shared/testing/layout-tab.mocks.ts | 140 ++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/src/app/cris-layout/cris-layout.component.spec.ts b/src/app/cris-layout/cris-layout.component.spec.ts index d7ec238dab6..af5cfa1aeda 100644 --- a/src/app/cris-layout/cris-layout.component.spec.ts +++ b/src/app/cris-layout/cris-layout.component.spec.ts @@ -39,7 +39,7 @@ const tabDataServiceMock: any = jasmine.createSpyObj('TabDataService', { // to FIX // tslint:disable-next-line:prefer-const -describe('CrisLayoutComponent', () => { +fdescribe('CrisLayoutComponent', () => { let component: CrisLayoutComponent; let fixture: ComponentFixture; @@ -153,6 +153,17 @@ describe('CrisLayoutComponent', () => { }); + it('it should not return a tab if box cointain a minor as true', () => { + tabDataServiceMock.findByItem.and.returnValue(observableOf(bothTabs)); + component.tabs$ = observableOf(bothTabs); + component.leadingTabs$ = observableOf(leadingTabs); + component.loaderTabs$ = observableOf(loaderTabs); + component.getLeadingTabs(); + fixture.detectChanges(); + const loaderTabsData = fixture.debugElement.queryAll(By.css('ds-cris-layout-loader')); + expect(loaderTabsData.length).toBe(1); + }); + }); }); diff --git a/src/app/shared/testing/layout-tab.mocks.ts b/src/app/shared/testing/layout-tab.mocks.ts index 9e618cda241..2fb1a852ec0 100644 --- a/src/app/shared/testing/layout-tab.mocks.ts +++ b/src/app/shared/testing/layout-tab.mocks.ts @@ -347,6 +347,146 @@ export const loaderTabs: CrisLayoutTab[] = [Object.assign(new CrisLayoutTab(), { ] } ] +}), +Object.assign(new CrisLayoutTab(), { + 'id': 3, + 'shortname': 'info', + 'header': 'Profile', + 'entityType': 'Person', + 'priority': 1, + 'security': 0, + 'type': 'tab', + 'leading': false, + 'rows': [ + { + 'style': 'col-md-12', + 'cells': [ + { + 'style': 'col-md-6', + 'boxes': [ + { + 'shortname': 'primary', + 'header': 'Primary Information', + 'entityType': 'Person', + 'collapsed': false, + 'minor': false, + 'style': 'col-md-6', + 'clear': true, + 'container': true, + 'maxColumn': 2, + 'security': 0, + 'boxType': 'METADATA', + 'type': 'box', + 'metadataSecurityFields': [], + 'configuration': { + 'id': 1, + 'rows': [ + { + 'fields': [ + { + 'metadata': 'dc.title', + 'label': 'Name', + 'fieldType': 'metadata' + }, + { + 'metadata': 'person.email', + 'label': 'Email', + 'fieldType': 'metadata', + 'valuesInline': 'true' + } + ] + } + ] + } + }, + { + 'shortname': 'other', + 'header': 'Other Informations', + 'entityType': 'Person', + 'collapsed': false, + 'minor': true, + 'style': 'col-md-6', + 'clear': true, + 'maxColumn': 2, + 'security': 0, + 'boxType': 'METADATA', + 'type': 'box', + 'metadataSecurityFields': [ + 'cris.policy.eperson' + ], + 'configuration': { + 'id': 2, + 'rows': [ + { + 'fields': [ + { + 'metadata': 'person.birthDate', + 'label': 'Birth date', + 'fieldType': 'metadata', + 'labelAsHeading': 'true' + } + ] + } + ] + } + } + ] + }, + { + 'style': 'col-md-6', + 'boxes': [ + { + 'shortname': 'researchoutputs', + 'header': 'Research outputs', + 'entityType': 'Person', + 'collapsed': false, + 'minor': false, + 'style': 'col-md-6', + 'clear': true, + 'maxColumn': 2, + 'security': 0, + 'boxType': 'RELATION', + 'type': 'box', + 'metadataSecurityFields': [], + 'configuration': { + 'id': 3, + 'discovery-configuration': 'RELATION.Person.researchoutputs' + } + } + ] + } + ] + }, + { + 'style': 'col-md-12', + 'cells': [ + { + 'style': 'col-md-12', + 'boxes': [ + { + 'shortname': 'metrics', + 'header': 'Metrics', + 'entityType': 'Person', + 'collapsed': false, + 'minor': false, + 'style': null, + 'clear': true, + 'maxColumn': 2, + 'security': 0, + 'boxType': 'METRICS', + 'type': 'box', + 'metadataSecurityFields': [], + 'configuration': { + 'id': 4, + 'numColumns': 2, + 'metrics': ['views', 'downloads'] + } + } + ] + } + ] + } + ] }) ]; From 3319613fb2d36caa86c077b659040d935842751d Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Mon, 7 Feb 2022 13:37:37 +0530 Subject: [PATCH 005/195] [DSC-337] row added in loaderTabs. --- src/app/shared/testing/layout-tab.mocks.ts | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/app/shared/testing/layout-tab.mocks.ts b/src/app/shared/testing/layout-tab.mocks.ts index 2fb1a852ec0..657142946b5 100644 --- a/src/app/shared/testing/layout-tab.mocks.ts +++ b/src/app/shared/testing/layout-tab.mocks.ts @@ -485,6 +485,54 @@ Object.assign(new CrisLayoutTab(), { ] } ] + }, + { + 'style': 'col-md-12', + 'cells': [ + { + 'style': 'col-md-12', + 'boxes': [ + { + 'shortname': 'metrics', + 'header': 'Metrics', + 'entityType': 'Person', + 'collapsed': false, + 'minor': false, + 'style': null, + 'clear': true, + 'maxColumn': 2, + 'security': 0, + 'boxType': 'METRICS', + 'type': 'box', + 'metadataSecurityFields': [], + 'configuration': { + 'id': 4, + 'numColumns': 2, + 'metrics': ['views', 'downloads'] + } + }, + { + 'shortname': 'metrics', + 'header': 'Metrics', + 'entityType': 'Person', + 'collapsed': false, + 'minor': true, + 'style': null, + 'clear': true, + 'maxColumn': 2, + 'security': 0, + 'boxType': 'METRICS', + 'type': 'box', + 'metadataSecurityFields': [], + 'configuration': { + 'id': 4, + 'numColumns': 2, + 'metrics': ['views', 'downloads'] + } + } + ] + } + ] } ] }) From 6d6253b35498bd3bc8bd0f83ceeecc1193796302 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Mon, 7 Feb 2022 13:43:12 +0530 Subject: [PATCH 006/195] [DSC-337] remove fdescribe. --- src/app/cris-layout/cris-layout.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/cris-layout/cris-layout.component.spec.ts b/src/app/cris-layout/cris-layout.component.spec.ts index af5cfa1aeda..804aa9248fd 100644 --- a/src/app/cris-layout/cris-layout.component.spec.ts +++ b/src/app/cris-layout/cris-layout.component.spec.ts @@ -39,7 +39,7 @@ const tabDataServiceMock: any = jasmine.createSpyObj('TabDataService', { // to FIX // tslint:disable-next-line:prefer-const -fdescribe('CrisLayoutComponent', () => { +describe('CrisLayoutComponent', () => { let component: CrisLayoutComponent; let fixture: ComponentFixture; From ce0b993cfee3e403c1bd5dbe7c5a547918dc4b12 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Tue, 22 Mar 2022 18:00:21 +0530 Subject: [PATCH 007/195] [DSC-337] check for minor element is move to tab data service. --- src/app/core/layout/tab-data.service.ts | 36 +++++++++++++++++-- src/app/cris-layout/cris-layout.component.ts | 21 ++--------- .../item-page/cris-item-page-tab.resolver.ts | 1 + 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/app/core/layout/tab-data.service.ts b/src/app/core/layout/tab-data.service.ts index 2d19df11056..fa5e57c200a 100644 --- a/src/app/core/layout/tab-data.service.ts +++ b/src/app/core/layout/tab-data.service.ts @@ -19,6 +19,7 @@ import { PaginatedList } from '../data/paginated-list.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FindListOptions } from '../data/request.models'; import { RequestParam } from '../cache/models/request-param.model'; +import { map } from 'rxjs/operators'; /* tslint:disable:max-classes-per-file */ @@ -75,10 +76,41 @@ export class TabDataService { * @param useCachedVersionIfAvailable * @param linkToFollow */ - findByItem(itemUuid: string, useCachedVersionIfAvailable, linkToFollow?: FollowLinkConfig): Observable>> { + findByItem(itemUuid: string, useCachedVersionIfAvailable, excludeMinors?: boolean ,linkToFollow?: FollowLinkConfig): Observable>> { const options = new FindListOptions(); options.searchParams = [new RequestParam('uuid', itemUuid)]; - return this.dataService.searchBy(this.searchFindByItem, options, useCachedVersionIfAvailable); + + return this.dataService.searchBy(this.searchFindByItem, options, useCachedVersionIfAvailable).pipe(map((data) => { + if (!!data.payload && !!data.payload.page && excludeMinors) { + data.payload.page = this.filterTab(data.payload.page); + } + return data; + })); + } + + /** + * @param tabs + * @returns Tabs which contains non minor element + */ + filterTab(tabs: CrisLayoutTab[]): CrisLayoutTab[] { + return tabs.filter(tab => this.checkForMinor(tab)); + } + + /** + * @param tab Contains a tab data which has rows, cells and boxes + * @returns Boolean based on cells has minor or not + */ + checkForMinor(tab: CrisLayoutTab): boolean { + for (const row of tab.rows) { + for (const cell of row.cells) { + for (const box of cell.boxes) { + if (!box.minor) { + return true; + } + } + } + } + return false; } /** diff --git a/src/app/cris-layout/cris-layout.component.ts b/src/app/cris-layout/cris-layout.component.ts index 1e88fe681a9..8b999950d9d 100644 --- a/src/app/cris-layout/cris-layout.component.ts +++ b/src/app/cris-layout/cris-layout.component.ts @@ -84,7 +84,7 @@ export class CrisLayoutComponent implements OnInit { */ getLeadingTabs(): Observable { return this.tabs$.pipe( - map((tabs: CrisLayoutTab[]) => tabs.filter(tab => tab.leading).filter(tab => this.checkForMinor(tab))), + map((tabs: CrisLayoutTab[]) => tabs.filter(tab => tab.leading)), ); } @@ -93,7 +93,7 @@ export class CrisLayoutComponent implements OnInit { */ getLoaderTabs(): Observable { return this.tabs$.pipe( - map((tabs: CrisLayoutTab[]) => tabs.filter(tab => !tab.leading ).filter(tab => this.checkForMinor(tab))), + map((tabs: CrisLayoutTab[]) => tabs.filter(tab => !tab.leading)), ); } @@ -106,21 +106,4 @@ export class CrisLayoutComponent implements OnInit { ); } - /** - * @param tab Contains a tab data which has rows, cells and boxes - * @returns Boolean based on cells has minor or not - */ - checkForMinor(tab: CrisLayoutTab): boolean { - for (const row of tab.rows) { - for (const cell of row.cells) { - for (const box of cell.boxes) { - if (!box.minor) { - return true; - } - } - } - } - return false; -} - } diff --git a/src/app/item-page/cris-item-page-tab.resolver.ts b/src/app/item-page/cris-item-page-tab.resolver.ts index fc7aaa5f9e0..f1de1265bd6 100644 --- a/src/app/item-page/cris-item-page-tab.resolver.ts +++ b/src/app/item-page/cris-item-page-tab.resolver.ts @@ -44,6 +44,7 @@ export class CrisItemPageTabResolver implements Resolve this.tabService.findByItem( item.uuid, // Item UUID + true, true ).pipe( getFirstCompletedRemoteData(), From f6b38d4b233ee2287f847d0f9cf63d74e66ec2ea Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Tue, 22 Mar 2022 18:11:47 +0530 Subject: [PATCH 008/195] [DSC-337] remove test case for checking minor element from component. --- src/app/cris-layout/cris-layout.component.spec.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/app/cris-layout/cris-layout.component.spec.ts b/src/app/cris-layout/cris-layout.component.spec.ts index 3962f17c7c9..a428fb95e18 100644 --- a/src/app/cris-layout/cris-layout.component.spec.ts +++ b/src/app/cris-layout/cris-layout.component.spec.ts @@ -156,17 +156,6 @@ describe('CrisLayoutComponent', () => { }); - it('it should not return a tab if box cointain a minor as true', () => { - tabDataServiceMock.findByItem.and.returnValue(observableOf(bothTabs)); - component.tabs$ = observableOf(bothTabs); - component.leadingTabs$ = observableOf(leadingTabs); - component.loaderTabs$ = observableOf(loaderTabs); - component.getLeadingTabs(); - fixture.detectChanges(); - const loaderTabsData = fixture.debugElement.queryAll(By.css('ds-cris-layout-loader')); - expect(loaderTabsData.length).toBe(1); - }); - }); }); From e0d6d4067aee1a734be0766bb0ef71e2a8530a50 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Wed, 23 Mar 2022 12:06:50 +0530 Subject: [PATCH 009/195] [DSC-337] add test cases for filterTab. --- src/app/core/layout/tab-data.service.spec.ts | 9 +++++++++ src/app/core/layout/tab-data.service.ts | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/app/core/layout/tab-data.service.spec.ts b/src/app/core/layout/tab-data.service.spec.ts index 30995e1f53e..4e755f0c0ab 100644 --- a/src/app/core/layout/tab-data.service.spec.ts +++ b/src/app/core/layout/tab-data.service.spec.ts @@ -16,6 +16,7 @@ import { of } from 'rxjs'; import { FindListOptions } from '../data/request.models'; import { RequestParam } from '../cache/models/request-param.model'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { bothTabs } from '../../shared/testing/layout-tab.mocks'; describe('TabDataService', () => { let scheduler: TestScheduler; @@ -193,4 +194,12 @@ describe('TabDataService', () => { }); }); + + + fdescribe('filterTab', () => { + it('should return non minor element', () => { + const tabs: CrisLayoutTab[] = service.filterTab(bothTabs); + expect(tabs.length).toBe(2); + }); + }); }); diff --git a/src/app/core/layout/tab-data.service.ts b/src/app/core/layout/tab-data.service.ts index fa5e57c200a..3091160156e 100644 --- a/src/app/core/layout/tab-data.service.ts +++ b/src/app/core/layout/tab-data.service.ts @@ -104,13 +104,13 @@ export class TabDataService { for (const row of tab.rows) { for (const cell of row.cells) { for (const box of cell.boxes) { - if (!box.minor) { - return true; + if (box.minor) { + return false; } } } } - return false; + return true; } /** From 94f07f316dd49fcb93664ba757ea76ed0c04559b Mon Sep 17 00:00:00 2001 From: nikunj59 Date: Thu, 2 Jun 2022 13:16:27 +0530 Subject: [PATCH 010/195] DSC-38 changes for map component should be enhanced in order to give the possibility of exporting the map as png and jpg/jpeg format. --- .../statistics-map.component.html | 14 ++++++- .../statistics-map.component.spec.ts | 29 ++++++++++++++- .../statistics-map.component.ts | 37 ++++++++++++++++++- ...bmission-import-external.component.spec.ts | 2 +- 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html index 83cb9fc6f2c..62934b8924c 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html @@ -1 +1,13 @@ - +
+ + +
+
+ +
diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts index 903b61374fe..e417f76631c 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts @@ -5,6 +5,9 @@ import { UsageReport } from '../../../core/statistics/models/usage-report.model' import { USAGE_REPORT } from '../../../core/statistics/models/usage-report.resource-type'; import { GoogleChartInterface } from 'ng2-google-charts'; +import { ExportService, ExportImageType } from '../../../core/export-service/export.service'; +import { TranslateModule } from '@ngx-translate/core'; +import { By } from '@angular/platform-browser'; describe('StatisticsMapComponent', () => { let component: StatisticsMapComponent; @@ -49,9 +52,17 @@ describe('StatisticsMapComponent', () => { options: { 'title': 'TopCountries' }, }; + const exportServiceMock: any = { + exportAsImage: jasmine.createSpy('exportAsImage'), + exportAsFile: jasmine.createSpy('exportAsFile') + }; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ StatisticsMapComponent ] + imports: [TranslateModule.forRoot()], + declarations: [ StatisticsMapComponent ], + providers: [ + { provide: ExportService, useValue: exportServiceMock } + ], }) .compileComponents(); }); @@ -80,4 +91,20 @@ describe('StatisticsMapComponent', () => { expect(component.geoChart).toEqual(geoChartExpected); }); + it('should download map as png and jpg', () => { + component.report = report; + fixture.detectChanges(); + component.ngOnInit(); + fixture.detectChanges(); + const downloadPngMapBtn = fixture.debugElement.query(By.css('[data-test="download-png-map-btn"]')); + downloadPngMapBtn.triggerEventHandler('click', null); + fixture.detectChanges(); + const node = fixture.debugElement.query(By.css('[data-test="google-chart-ref"]')).nativeElement; + expect(exportServiceMock.exportAsImage).toHaveBeenCalledWith(node, ExportImageType.png, report.reportType, component.isLoading); + + const downloadJpgMapBtn = fixture.debugElement.query(By.css('[data-test="download-jpg-map-btn"]')); + downloadJpgMapBtn.triggerEventHandler('click', null); + fixture.detectChanges(); + expect(exportServiceMock.exportAsImage).toHaveBeenCalledWith(node, ExportImageType.jpeg, report.reportType, component.isSecondLoading); + }); }); diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts index 4bd4bf8a7b5..6f0eead5f1b 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts @@ -1,6 +1,8 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { UsageReport } from '../../../core/statistics/models/usage-report.model'; import { GoogleChartInterface } from 'ng2-google-charts'; +import { ExportImageType, ExportService } from '../../../core/export-service/export.service'; +import { BehaviorSubject } from 'rxjs'; @Component({ @@ -30,6 +32,23 @@ export class StatisticsMapComponent implements OnInit { * Chart Columns needed to be shown in the tooltip */ chartColumns = []; + /** + * Loading utilized for export functions to disable buttons + */ + isLoading: BehaviorSubject = new BehaviorSubject(false); + /** + * Loading utilized for export functions to disable buttons + */ + isSecondLoading: BehaviorSubject = new BehaviorSubject(false); + /** + * Chart ElementRef + */ + @ViewChild('googleChartRef') googleChartRef: ElementRef; + + constructor( + private exportService: ExportService + ) { + } ngOnInit(): void { if ( !!this.report && !!this.report.points && this.report.points.length > 0 ) { @@ -64,6 +83,22 @@ export class StatisticsMapComponent implements OnInit { } + /** + * Download map as image in png version. + */ + downloadPng() { + this.isLoading.next(false); + const node = this.googleChartRef.nativeElement; + this.exportService.exportAsImage(node, ExportImageType.png, this.report.reportType, this.isLoading); + } + /** + * Download map as image in jpeg version. + */ + downloadJpeg() { + this.isSecondLoading.next(false); + const node = this.googleChartRef.nativeElement; + this.exportService.exportAsImage(node, ExportImageType.jpeg, this.report.reportType, this.isSecondLoading); + } } diff --git a/src/app/submission/import-external/submission-import-external.component.spec.ts b/src/app/submission/import-external/submission-import-external.component.spec.ts index dc53b2e45f3..dfcb85ee347 100644 --- a/src/app/submission/import-external/submission-import-external.component.spec.ts +++ b/src/app/submission/import-external/submission-import-external.component.spec.ts @@ -165,7 +165,7 @@ describe('SubmissionImportExternalComponent test suite', () => { ngbModal.open.and.returnValue({componentInstance: { externalSourceEntry: null}}); comp.import(entry); - expect(compAsAny.modalService.open).toHaveBeenCalledWith(SubmissionImportExternalPreviewComponent, { size: 'lg' }); + expect(compAsAny.modalService.open).toHaveBeenCalledWith(SubmissionImportExternalPreviewComponent, { size: 'lg', scrollable: true }); expect(comp.modalRef.componentInstance.externalSourceEntry).toEqual(entry); }); From e96a9794894e5859980b71516b6399eb908e898b Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 12 Sep 2022 20:29:49 +0200 Subject: [PATCH 011/195] [CST-6317] New COLLECTIONS rendering --- .../cris-layout-collection-box.component.html | 8 +++ .../cris-layout-collection-box.component.scss | 0 ...is-layout-collection-box.component.spec.ts | 62 +++++++++++++++++++ .../cris-layout-collection-box.component.ts | 51 +++++++++++++++ src/app/cris-layout/cris-layout.module.ts | 2 + src/app/cris-layout/enums/layout-box.enum.ts | 1 + src/assets/i18n/en.json5 | 2 + 7 files changed, 126 insertions(+) create mode 100644 src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html create mode 100644 src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.scss create mode 100644 src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts create mode 100644 src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html new file mode 100644 index 00000000000..dafa3cecfd3 --- /dev/null +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html @@ -0,0 +1,8 @@ +
+
+
{{ 'cris-layout.rendering.collections.owning-collection.label' | translate }}
+ +
+
diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.scss b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts new file mode 100644 index 00000000000..d6fde55ac6c --- /dev/null +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts @@ -0,0 +1,62 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CrisLayoutCollectionBoxComponent } from './cris-layout-collection-box.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { CrisLayoutBox } from '../../../../../core/layout/models/box.model'; +import { Item } from '../../../../../core/shared/item.model'; +import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; +import { Collection } from '../../../../../core/shared/collection.model'; + +describe('CrisLayoutCollectionBoxComponent', () => { + let component: CrisLayoutCollectionBoxComponent; + let fixture: ComponentFixture; + + const testBox = Object.assign(new CrisLayoutBox(), { + 'id': 1, + 'shortname': 'collections', + 'header': 'Collections', + 'entityType': 'Publication', + 'collapsed': false, + 'minor': false, + 'style': null, + 'security': 0, + 'boxType': 'COLLECTIONS', + 'maxColumns': null, + 'configuration': null, + 'metadataSecurityFields': [], + 'container': false + }); + + const owningCollection = Object.assign(new Collection(), {uuid: 'test-collection-uuid'}); + + const owningCollection$ = createSuccessfulRemoteDataObject$(owningCollection); + + const testItem = Object.assign(new Item(), { + type: 'item', + owningCollection: owningCollection$, + uuid: 'test-item-uuid', + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ], + declarations: [CrisLayoutCollectionBoxComponent], + providers: [ + { provide: 'boxProvider', useValue: testBox }, + { provide: 'itemProvider', useValue: testItem }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CrisLayoutCollectionBoxComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts new file mode 100644 index 00000000000..0ae49bc2851 --- /dev/null +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts @@ -0,0 +1,51 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { CrisLayoutBoxModelComponent } from '../../../../models/cris-layout-box-component.model'; +import { CrisLayoutBox } from '../../../../../core/layout/models/box.model'; +import { Item } from '../../../../../core/shared/item.model'; +import { TranslateService } from '@ngx-translate/core'; +import { RenderCrisLayoutBoxFor } from '../../../../decorators/cris-layout-box.decorator'; +import { LayoutBox } from '../../../../enums/layout-box.enum'; +import { Observable } from 'rxjs'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; +import { map, shareReplay } from 'rxjs/operators'; + +@Component({ + selector: 'ds-cris-layout-collection-box', + templateUrl: './cris-layout-collection-box.component.html', + styleUrls: ['./cris-layout-collection-box.component.scss'] +}) +@RenderCrisLayoutBoxFor(LayoutBox.COLLECTIONS) +export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponent implements OnInit { + + owningCollectionName$: Observable; + + owningCollectionId$: Observable; + + constructor( + protected translateService: TranslateService, + @Inject('boxProvider') public boxProvider: CrisLayoutBox, + @Inject('itemProvider') public itemProvider: Item + ) { + super(translateService, boxProvider, itemProvider); + } + + ngOnInit(): void { + super.ngOnInit(); + + const collection$ = this.item.owningCollection.pipe( + getFirstSucceededRemoteDataPayload(), + shareReplay(), + ); + + this.owningCollectionName$ = collection$.pipe( + map((coll) => coll.firstMetadataValue('dc.title')), + ); + + this.owningCollectionId$ = collection$.pipe( + map((coll) => coll.uuid), + ); + + } + + +} diff --git a/src/app/cris-layout/cris-layout.module.ts b/src/app/cris-layout/cris-layout.module.ts index a5070c6efb8..91a9c274f7c 100644 --- a/src/app/cris-layout/cris-layout.module.ts +++ b/src/app/cris-layout/cris-layout.module.ts @@ -48,12 +48,14 @@ import { ComcolModule } from '../shared/comcol/comcol.module'; import { SearchModule } from '../shared/search/search.module'; import { AdvancedAttachmentComponent } from './cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component'; import { FileDownloadButtonComponent } from '../shared/file-download-button/file-download-button.component'; +import { CrisLayoutCollectionBoxComponent } from './cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator CrisLayoutVerticalComponent, CrisLayoutHorizontalComponent, CrisLayoutMetadataBoxComponent, + CrisLayoutCollectionBoxComponent, TextComponent, HeadingComponent, CrisLayoutRelationBoxComponent, diff --git a/src/app/cris-layout/enums/layout-box.enum.ts b/src/app/cris-layout/enums/layout-box.enum.ts index df5673b98cc..143c14ab535 100644 --- a/src/app/cris-layout/enums/layout-box.enum.ts +++ b/src/app/cris-layout/enums/layout-box.enum.ts @@ -12,4 +12,5 @@ export enum LayoutBox { ORCID_AUTHORIZATIONS = 'ORCID_AUTHORIZATIONS', ORCID_SYNC_QUEUE = 'ORCID_SYNC_QUEUE', IIIFVIEWER = 'IIIFVIEWER', + COLLECTIONS = 'COLLECTIONS', } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 79ae633c815..ca43ee5bfd3 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1622,6 +1622,8 @@ "cris-layout.attachment.viewMore": "View More", + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.noop.label": "NOOP", From 8d2636455fb59e1c62842383e6c42f9e0da3df17 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Wed, 14 Sep 2022 18:22:04 +0530 Subject: [PATCH 012/195] [DSC-740] New COLLECTIONS rendering type to show item's collection with CRIS layout --- .../cris-layout-collection-box.component.html | 33 ++++-- ...is-layout-collection-box.component.spec.ts | 89 ++++++++++++++ .../cris-layout-collection-box.component.ts | 112 +++++++++++++++--- src/assets/i18n/en.json5 | 6 + src/config/default-app-config.ts | 4 + src/config/layout-config.interfaces.ts | 6 + src/environments/environment.test.ts | 4 + 7 files changed, 233 insertions(+), 21 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html index dafa3cecfd3..9ec7d7007b7 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html @@ -1,8 +1,27 @@ -
-
-
{{ 'cris-layout.rendering.collections.owning-collection.label' | translate }}
-
- {{ owningCollectionName$ | async }} +
+
+
{{ 'cris-layout.rendering.collections.owning-collection.label' | translate }}
+
-
-
+
+
{{ 'cris-layout.rendering.collections.mapped-collection.label' | translate }}
+ +
+
\ No newline at end of file diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts index d6fde55ac6c..bf0c80e19d0 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.spec.ts @@ -6,11 +6,21 @@ import { CrisLayoutBox } from '../../../../../core/layout/models/box.model'; import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; import { Collection } from '../../../../../core/shared/collection.model'; +import { CollectionDataService } from '../../../../../core/data/collection-data.service'; +import { buildPaginatedList, PaginatedList } from '../../../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../../../core/shared/page-info.model'; +import { By } from '@angular/platform-browser'; +import { of as observableOf } from 'rxjs'; describe('CrisLayoutCollectionBoxComponent', () => { let component: CrisLayoutCollectionBoxComponent; let fixture: ComponentFixture; + const createMockCollection = (id: string) => Object.assign(new Collection(), { + id: id, + name: `collection-${id}`, + }); + const testBox = Object.assign(new CrisLayoutBox(), { 'id': 1, 'shortname': 'collections', @@ -26,6 +36,9 @@ describe('CrisLayoutCollectionBoxComponent', () => { 'metadataSecurityFields': [], 'container': false }); + let collectionDataService; + let mockCollection1: Collection; + let mockPage1: PaginatedList; const owningCollection = Object.assign(new Collection(), {uuid: 'test-collection-uuid'}); @@ -37,7 +50,14 @@ describe('CrisLayoutCollectionBoxComponent', () => { uuid: 'test-item-uuid', }); + mockCollection1 = createMockCollection('c1'); + beforeEach(async () => { + collectionDataService = jasmine.createSpyObj([ + 'findOwningCollectionFor', + 'findMappedCollectionsFor', + ]); + await TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), @@ -46,6 +66,7 @@ describe('CrisLayoutCollectionBoxComponent', () => { providers: [ { provide: 'boxProvider', useValue: testBox }, { provide: 'itemProvider', useValue: testItem }, + { provide: CollectionDataService, useValue: collectionDataService }, ], }).compileComponents(); }); @@ -53,10 +74,78 @@ describe('CrisLayoutCollectionBoxComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(CrisLayoutCollectionBoxComponent); component = fixture.componentInstance; + mockPage1 = buildPaginatedList(Object.assign(new PageInfo(), { + currentPage: 1, + elementsPerPage: 2, + totalPages: 0, + totalElements: 0, + }), []); + collectionDataService.findOwningCollectionFor.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection1)); + collectionDataService.findMappedCollectionsFor.and.returnValue(createSuccessfulRemoteDataObject$(mockPage1)); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should render container', () => { + expect(fixture.debugElement.query(By.css('.container'))).not.toBeNull(); + }); + + describe('without collections', () => { + beforeEach(() => { + component.owningCollection$ = observableOf(null); + fixture.detectChanges(); + }); + + it('should not render container', () => { + expect(fixture.debugElement.query(By.css('.container'))).toBeNull(); + }); + }); + + describe('without owning collections', () => { + beforeEach(() => { + component.owningCollection$ = observableOf(null); + fixture.detectChanges(); + }); + + it('should not render owning collection row', () => { + expect(fixture.debugElement.query(By.css('div[data-test="owningCollection"]'))).toBeNull(); + }); + }); + + describe('with owning collections', () => { + beforeEach(() => { + component.owningCollection$ = observableOf(mockCollection1); + fixture.detectChanges(); + }); + + it('should render owning collection row', () => { + expect(fixture.debugElement.query(By.css('div[data-test="owningCollection"]'))).not.toBeNull(); + }); + }); + + describe('without mapped collections', () => { + beforeEach(() => { + component.mappedCollections$ = observableOf([]); + fixture.detectChanges(); + }); + + it('should not render mapped collections row', () => { + expect(fixture.debugElement.query(By.css('div[data-test="mappedCollections"]'))).toBeNull(); + }); + }); + + describe('with mapped collections', () => { + beforeEach(() => { + component.mappedCollections$ = observableOf([mockCollection1]); + fixture.detectChanges(); + }); + + it('should render mapped collection row', () => { + expect(fixture.debugElement.query(By.css('div[data-test="mappedCollections"]'))).not.toBeNull(); + }); + }); + }); diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts index 0ae49bc2851..9389d9659e5 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts @@ -5,9 +5,14 @@ import { Item } from '../../../../../core/shared/item.model'; import { TranslateService } from '@ngx-translate/core'; import { RenderCrisLayoutBoxFor } from '../../../../decorators/cris-layout-box.decorator'; import { LayoutBox } from '../../../../enums/layout-box.enum'; -import { Observable } from 'rxjs'; -import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; -import { map, shareReplay } from 'rxjs/operators'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { getFirstSucceededRemoteDataPayload, getAllCompletedRemoteData, getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../../../../../core/shared/operators'; +import { startWith, tap, withLatestFrom, switchMap, scan } from 'rxjs/operators'; +import { Collection } from '../../../../../core/shared/collection.model'; +import { CollectionDataService } from '../../../../../core/data/collection-data.service'; +import { FindListOptions } from '../../../../../core/data/request.models'; +import { PaginatedList } from '../../../../../core/data/paginated-list.model'; +import { environment } from '../../../../../../environments/environment'; @Component({ selector: 'ds-cris-layout-collection-box', @@ -17,14 +22,50 @@ import { map, shareReplay } from 'rxjs/operators'; @RenderCrisLayoutBoxFor(LayoutBox.COLLECTIONS) export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponent implements OnInit { - owningCollectionName$: Observable; + separator = '
'; - owningCollectionId$: Observable; + /** + * Amount of mapped collections that should be fetched at once. + */ + pageSize = 5; + + /** + * Last page of the mapped collections that has been fetched. + */ + lastPage$: BehaviorSubject = new BehaviorSubject(0); + + /** + * Push an event to this observable to fetch the next page of mapped collections. + * Because this observable is a behavior subject, the first page will be requested + * immediately after subscription. + */ + loadMore$: BehaviorSubject = new BehaviorSubject(undefined); + + /** + * Whether or not a page of mapped collections is currently being loaded. + */ + isLoading$: BehaviorSubject = new BehaviorSubject(true); + + /** + * Whether or not more pages of mapped collections are available. + */ + hasMore$: BehaviorSubject = new BehaviorSubject(true); + + /** + * This includes the owning collection + */ + owningCollection$: Observable; + + /** + * This includes the mapped collection + */ + mappedCollections$: Observable; constructor( protected translateService: TranslateService, @Inject('boxProvider') public boxProvider: CrisLayoutBox, - @Inject('itemProvider') public itemProvider: Item + @Inject('itemProvider') public itemProvider: Item, + private cds: CollectionDataService ) { super(translateService, boxProvider, itemProvider); } @@ -32,19 +73,62 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen ngOnInit(): void { super.ngOnInit(); - const collection$ = this.item.owningCollection.pipe( + this.owningCollection$ = this.cds.findOwningCollectionFor(this.item).pipe( getFirstSucceededRemoteDataPayload(), - shareReplay(), + startWith(null as Collection), ); - this.owningCollectionName$ = collection$.pipe( - map((coll) => coll.firstMetadataValue('dc.title')), - ); + this.mappedCollections$ = this.loadMore$.pipe( + // update isLoading$ + tap(() => this.isLoading$.next(true)), - this.owningCollectionId$ = collection$.pipe( - map((coll) => coll.uuid), - ); + // request next batch of mapped collections + withLatestFrom(this.lastPage$), + switchMap(([_, lastPage]: [void, number]) => { + return this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { + elementsPerPage: this.pageSize, + currentPage: lastPage + 1, + })); + }), + + getAllCompletedRemoteData>(), + + // update isLoading$ + tap(() => this.isLoading$.next(false)), + + getAllSucceededRemoteDataPayload(), + + // update hasMore$ + tap((response: PaginatedList) => this.hasMore$.next(response.currentPage < response.totalPages)), + + // update lastPage$ + tap((response: PaginatedList) => this.lastPage$.next(response.currentPage)), + + getPaginatedListPayload(), + + // add current batch to list of collections + scan((prev: Collection[], current: Collection[]) => [...prev, ...current], []), + + startWith([]), + ) as Observable; + } + + handleLoadMore() { + this.loadMore$.next(); + } + + /** + * Returns a string representing the style of field label if exists + */ + get labelStyle(): string { + return environment.crisLayout.collectionsBox.defaultCollectionsLabelColStyle; + } + /** + * Returns a string representing the style of field value if exists + */ + get valueStyle(): string { + return environment.crisLayout.collectionsBox.defaultCollectionsValueColStyle; } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index ca43ee5bfd3..e5503cc0dbc 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1624,6 +1624,12 @@ "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collection", + + "cris-layout.rendering.collections.loading": "Loading...", + + "cris-layout.rendering.collections.load-more": "Load more", + "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.noop.label": "NOOP", diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 6594cebecd3..fc0d21461c3 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -415,6 +415,10 @@ export class DefaultAppConfig implements AppConfig { metadataBox: { defaultMetadataLabelColStyle: 'col-3', defaultMetadataValueColStyle: 'col-9' + }, + collectionsBox: { + defaultCollectionsLabelColStyle: 'col-3 font-weight-bold', + defaultCollectionsValueColStyle: 'col-9' } }; diff --git a/src/config/layout-config.interfaces.ts b/src/config/layout-config.interfaces.ts index 0b15a06aa92..cdc2f43015b 100644 --- a/src/config/layout-config.interfaces.ts +++ b/src/config/layout-config.interfaces.ts @@ -15,6 +15,11 @@ export interface CrisLayoutMetadataBoxConfig extends Config { defaultMetadataValueColStyle: string; } +export interface CrisLayoutCollectionsBoxConfig extends Config { + defaultCollectionsLabelColStyle: string; + defaultCollectionsValueColStyle: string; +} + export interface CrisLayoutTypeConfig { orientation: string; } @@ -34,6 +39,7 @@ export interface CrisLayoutConfig extends Config { crisRef: CrisRefConfig[]; itemPage: CrisItemPageConfig; metadataBox: CrisLayoutMetadataBoxConfig; + collectionsBox: CrisLayoutCollectionsBoxConfig; } export interface LayoutConfig extends Config { diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index a7873fda79d..169f16fbfc9 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -288,6 +288,10 @@ export const environment: BuildConfig = { metadataBox: { defaultMetadataLabelColStyle: 'col-3', defaultMetadataValueColStyle: 'col-9' + }, + collectionsBox: { + defaultCollectionsLabelColStyle: 'col-3 font-weight-bold', + defaultCollectionsValueColStyle: 'col-9' } }, layout: { From 75c3336595a3ea2b864e0dc033762ffd48f8d0ab Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Thu, 15 Sep 2022 19:57:30 +0530 Subject: [PATCH 013/195] [DSC-740] design and functionality fixes --- .../cris-layout-collection-box.component.html | 18 ++--- .../cris-layout-collection-box.component.ts | 68 +++++++++---------- src/assets/i18n/en.json5 | 2 +- src/config/default-app-config.ts | 3 +- src/config/layout-config.interfaces.ts | 2 + src/environments/environment.test.ts | 3 +- 6 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html index 9ec7d7007b7..38f71cfda7c 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html @@ -5,22 +5,24 @@ {{ (owningCollection$ | async).firstMetadataValue('dc.title') }}
-
+
{{ 'cris-layout.rendering.collections.mapped-collection.label' | translate }}
- diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts index 9389d9659e5..cf9eddfb112 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts @@ -5,9 +5,9 @@ import { Item } from '../../../../../core/shared/item.model'; import { TranslateService } from '@ngx-translate/core'; import { RenderCrisLayoutBoxFor } from '../../../../decorators/cris-layout-box.decorator'; import { LayoutBox } from '../../../../enums/layout-box.enum'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, Observable, of } from 'rxjs'; import { getFirstSucceededRemoteDataPayload, getAllCompletedRemoteData, getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../../../../../core/shared/operators'; -import { startWith, tap, withLatestFrom, switchMap, scan } from 'rxjs/operators'; +import { shareReplay, tap, switchMap, scan, map } from 'rxjs/operators'; import { Collection } from '../../../../../core/shared/collection.model'; import { CollectionDataService } from '../../../../../core/data/collection-data.service'; import { FindListOptions } from '../../../../../core/data/request.models'; @@ -22,7 +22,7 @@ import { environment } from '../../../../../../environments/environment'; @RenderCrisLayoutBoxFor(LayoutBox.COLLECTIONS) export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponent implements OnInit { - separator = '
'; + isInline = environment.crisLayout.collectionsBox.isInline; /** * Amount of mapped collections that should be fetched at once. @@ -32,14 +32,7 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen /** * Last page of the mapped collections that has been fetched. */ - lastPage$: BehaviorSubject = new BehaviorSubject(0); - - /** - * Push an event to this observable to fetch the next page of mapped collections. - * Because this observable is a behavior subject, the first page will be requested - * immediately after subscription. - */ - loadMore$: BehaviorSubject = new BehaviorSubject(undefined); + lastPage = 0; /** * Whether or not a page of mapped collections is currently being loaded. @@ -73,24 +66,21 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen ngOnInit(): void { super.ngOnInit(); - this.owningCollection$ = this.cds.findOwningCollectionFor(this.item).pipe( + this.owningCollection$ = this.item.owningCollection.pipe( getFirstSucceededRemoteDataPayload(), - startWith(null as Collection), + shareReplay(), ); - this.mappedCollections$ = this.loadMore$.pipe( - // update isLoading$ - tap(() => this.isLoading$.next(true)), - - // request next batch of mapped collections - withLatestFrom(this.lastPage$), - switchMap(([_, lastPage]: [void, number]) => { - return this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { - elementsPerPage: this.pageSize, - currentPage: lastPage + 1, - })); - }), + this.handleLoadMore(); + } + handleLoadMore() { + this.isLoading$.next(true); + const oldMappedCollections$ = this.mappedCollections$; + this.mappedCollections$ = this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { + elementsPerPage: this.pageSize, + currentPage: this.lastPage + 1, + })).pipe( getAllCompletedRemoteData>(), // update isLoading$ @@ -99,22 +89,26 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen getAllSucceededRemoteDataPayload(), // update hasMore$ - tap((response: PaginatedList) => this.hasMore$.next(response.currentPage < response.totalPages)), + tap((response: PaginatedList) => this.hasMore$.next(this.lastPage < response.totalPages)), - // update lastPage$ - tap((response: PaginatedList) => this.lastPage$.next(response.currentPage)), + // update lastPage + tap((response: PaginatedList) => this.lastPage = response.currentPage), getPaginatedListPayload(), // add current batch to list of collections scan((prev: Collection[], current: Collection[]) => [...prev, ...current], []), - startWith([]), - ) as Observable; - } - - handleLoadMore() { - this.loadMore$.next(); + switchMap((collections: Collection[]) => { + if (oldMappedCollections$) { + return oldMappedCollections$.pipe( + map((mappedCollection) => [...mappedCollection, ...collections]) + ); + } else { + return of(collections); + } + }) + ); } /** @@ -131,5 +125,11 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen return environment.crisLayout.collectionsBox.defaultCollectionsValueColStyle; } + /** + * Returns a string representing the style of row if exists + */ + get rowStyle(): string { + return environment.crisLayout.collectionsBox.defaultCollectionsRowStyle; + } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index e5503cc0dbc..ee207485400 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1624,7 +1624,7 @@ "cris-layout.rendering.collections.owning-collection.label": "Owning collection", - "cris-layout.rendering.collections.mapped-collection.label": "Mapped collection", + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", "cris-layout.rendering.collections.loading": "Loading...", diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index fc0d21461c3..3f782fa1990 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -418,7 +418,8 @@ export class DefaultAppConfig implements AppConfig { }, collectionsBox: { defaultCollectionsLabelColStyle: 'col-3 font-weight-bold', - defaultCollectionsValueColStyle: 'col-9' + defaultCollectionsValueColStyle: 'col-9', + isInline: true } }; diff --git a/src/config/layout-config.interfaces.ts b/src/config/layout-config.interfaces.ts index cdc2f43015b..5c91ca190aa 100644 --- a/src/config/layout-config.interfaces.ts +++ b/src/config/layout-config.interfaces.ts @@ -18,6 +18,8 @@ export interface CrisLayoutMetadataBoxConfig extends Config { export interface CrisLayoutCollectionsBoxConfig extends Config { defaultCollectionsLabelColStyle: string; defaultCollectionsValueColStyle: string; + isInline: boolean; + defaultCollectionsRowStyle?: string; } export interface CrisLayoutTypeConfig { diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 169f16fbfc9..fa194c12989 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -291,7 +291,8 @@ export const environment: BuildConfig = { }, collectionsBox: { defaultCollectionsLabelColStyle: 'col-3 font-weight-bold', - defaultCollectionsValueColStyle: 'col-9' + defaultCollectionsValueColStyle: 'col-9', + isInline: true } }, layout: { From 48a5e6bb7de2e1435d6daa5239a8e50b05b6dc0e Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 16 Sep 2022 15:44:21 +0200 Subject: [PATCH 014/195] [DSC-740] Graphical fixes --- .../cris-layout-collection-box.component.html | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html index 38f71cfda7c..e1126d358d7 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.html @@ -1,29 +1,31 @@ -
-
-
{{ 'cris-layout.rendering.collections.owning-collection.label' | translate }}
- +
+
+
{{ 'cris-layout.rendering.collections.owning-collection.label' | translate }}
+ -
-
{{ 'cris-layout.rendering.collections.mapped-collection.label' | translate }}
- +
+
+
{{ 'cris-layout.rendering.collections.mapped-collection.label' | translate }}
+
+ +
+ {{'cris-layout.rendering.collections.loading' | translate}} +
+ + + {{'cris-layout.rendering.collections.load-more' | translate}} +
-
\ No newline at end of file +
+
From 7cceda1f8173ad6097ab914a9ad123abf1a7c7f3 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 16 Sep 2022 16:18:50 +0200 Subject: [PATCH 015/195] [DSC-740] Refactor WIP --- .../cris-layout-collection-box.component.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts index cf9eddfb112..0fb92ea60c2 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts @@ -13,6 +13,7 @@ import { CollectionDataService } from '../../../../../core/data/collection-data. import { FindListOptions } from '../../../../../core/data/request.models'; import { PaginatedList } from '../../../../../core/data/paginated-list.model'; import { environment } from '../../../../../../environments/environment'; +import { RemoteData } from '../../../../../core/data/remote-data'; @Component({ selector: 'ds-cris-layout-collection-box', @@ -77,10 +78,7 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen handleLoadMore() { this.isLoading$.next(true); const oldMappedCollections$ = this.mappedCollections$; - this.mappedCollections$ = this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { - elementsPerPage: this.pageSize, - currentPage: this.lastPage + 1, - })).pipe( + this.mappedCollections$ = this.mappedCollectionPage(this.lastPage).pipe( getAllCompletedRemoteData>(), // update isLoading$ @@ -111,6 +109,13 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen ); } + mappedCollectionPage(page: number): Observable>> { + return this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { + elementsPerPage: this.pageSize, + currentPage: page, + })); + } + /** * Returns a string representing the style of field label if exists */ From 70ad345701069b0f1deeed752e8de454b3e1ae8d Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 16 Sep 2022 16:34:23 +0200 Subject: [PATCH 016/195] [DSC-740] Refactor WIP --- .../cris-layout-collection-box.component.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts index 0fb92ea60c2..0811360d4fa 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts @@ -78,7 +78,7 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen handleLoadMore() { this.isLoading$.next(true); const oldMappedCollections$ = this.mappedCollections$; - this.mappedCollections$ = this.mappedCollectionPage(this.lastPage).pipe( + this.mappedCollections$ = this.loadMappedCollectionPage(this.lastPage).pipe( getAllCompletedRemoteData>(), // update isLoading$ @@ -109,11 +109,13 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen ); } - mappedCollectionPage(page: number): Observable>> { + loadMappedCollectionPage(page: number): Observable>> { return this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { elementsPerPage: this.pageSize, currentPage: page, - })); + })).pipe( + + ); } /** From 6ec7f63f31200590658ab03a0bf3d5180b4530c7 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Mon, 19 Sep 2022 20:53:26 +0530 Subject: [PATCH 017/195] [DSC-740] load more mapped collection optimization --- .../cris-layout-collection-box.component.ts | 56 ++++++++----------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts index 0811360d4fa..1f5a2445d9f 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/cris-layout-collection-box/cris-layout-collection-box.component.ts @@ -5,15 +5,16 @@ import { Item } from '../../../../../core/shared/item.model'; import { TranslateService } from '@ngx-translate/core'; import { RenderCrisLayoutBoxFor } from '../../../../decorators/cris-layout-box.decorator'; import { LayoutBox } from '../../../../enums/layout-box.enum'; -import { BehaviorSubject, Observable, of } from 'rxjs'; -import { getFirstSucceededRemoteDataPayload, getAllCompletedRemoteData, getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../../../../../core/shared/operators'; -import { shareReplay, tap, switchMap, scan, map } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; +import { getFirstSucceededRemoteDataPayload, getPaginatedListPayload, getFirstCompletedRemoteData } from '../../../../../core/shared/operators'; +import { shareReplay, tap, map } from 'rxjs/operators'; import { Collection } from '../../../../../core/shared/collection.model'; import { CollectionDataService } from '../../../../../core/data/collection-data.service'; import { FindListOptions } from '../../../../../core/data/request.models'; import { PaginatedList } from '../../../../../core/data/paginated-list.model'; import { environment } from '../../../../../../environments/environment'; import { RemoteData } from '../../../../../core/data/remote-data'; +import { hasValue } from 'src/app/shared/empty.util'; @Component({ selector: 'ds-cris-layout-collection-box', @@ -53,7 +54,7 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen /** * This includes the mapped collection */ - mappedCollections$: Observable; + mappedCollections$: Observable = of([]); constructor( protected translateService: TranslateService, @@ -77,44 +78,33 @@ export class CrisLayoutCollectionBoxComponent extends CrisLayoutBoxModelComponen handleLoadMore() { this.isLoading$.next(true); - const oldMappedCollections$ = this.mappedCollections$; - this.mappedCollections$ = this.loadMappedCollectionPage(this.lastPage).pipe( - getAllCompletedRemoteData>(), + const newMappedCollections$ = this.loadMappedCollectionPage(); + this.mappedCollections$ = combineLatest([this.mappedCollections$, newMappedCollections$]).pipe( + map(([mappedCollections, newMappedCollections]: [Collection[], Collection[]]) => { + return [...mappedCollections, ...newMappedCollections].filter(collection => hasValue(collection)); + }), + ); + } + + loadMappedCollectionPage(): Observable { + return this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { + elementsPerPage: this.pageSize, + currentPage: this.lastPage + 1, + })).pipe( + getFirstCompletedRemoteData>(), // update isLoading$ tap(() => this.isLoading$.next(false)), - getAllSucceededRemoteDataPayload(), - - // update hasMore$ - tap((response: PaginatedList) => this.hasMore$.next(this.lastPage < response.totalPages)), + getFirstSucceededRemoteDataPayload(), // update lastPage tap((response: PaginatedList) => this.lastPage = response.currentPage), - getPaginatedListPayload(), - - // add current batch to list of collections - scan((prev: Collection[], current: Collection[]) => [...prev, ...current], []), - - switchMap((collections: Collection[]) => { - if (oldMappedCollections$) { - return oldMappedCollections$.pipe( - map((mappedCollection) => [...mappedCollection, ...collections]) - ); - } else { - return of(collections); - } - }) - ); - } - - loadMappedCollectionPage(page: number): Observable>> { - return this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), { - elementsPerPage: this.pageSize, - currentPage: page, - })).pipe( + // update hasMore$ + tap((response: PaginatedList) => this.hasMore$.next(this.lastPage < response.totalPages)), + getPaginatedListPayload(), ); } From 3deb5f6eb7e00b9d4d66e3096c3304a5c3d26662 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Thu, 10 Nov 2022 20:53:56 +0530 Subject: [PATCH 018/195] [DSC-818] Add bulk export page and link it to side menu --- .../data/processes/script-data.service.ts | 1 + src/app/menu.resolver.ts | 17 ++ .../export-collection-menu.component.ts | 4 +- .../dso-selector-modal-wrapper.component.ts | 1 + .../export-excel-selector.component.ts | 4 +- ...rt-metadata-xls-selector.component.spec.ts | 180 ++++++++++++++++++ .../export-metadata-xls-selector.component.ts | 105 ++++++++++ src/app/shared/shared.module.ts | 3 + src/assets/i18n/en.json5 | 14 +- 9 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.spec.ts create mode 100644 src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.ts diff --git a/src/app/core/data/processes/script-data.service.ts b/src/app/core/data/processes/script-data.service.ts index fb6dc813084..ce4f9196aab 100644 --- a/src/app/core/data/processes/script-data.service.ts +++ b/src/app/core/data/processes/script-data.service.ts @@ -25,6 +25,7 @@ import { CoreState } from '../../core-state.model'; export const METADATA_IMPORT_SCRIPT_NAME = 'metadata-import'; export const METADATA_EXPORT_SCRIPT_NAME = 'metadata-export'; +export const COLLECTION_EXPORT_SCRIPT_NAME = 'collection-export'; export const ITEM_EXPORT_SCRIPT_NAME = 'item-export'; export const BULK_ITEM_EXPORT_SCRIPT_NAME = 'bulk-item-export'; export const BATCH_IMPORT_SCRIPT_NAME = 'import'; diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index 6c8522ba0b0..fa2e8771b77 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -48,6 +48,9 @@ import { Section } from './core/layout/models/section.model'; import { ExportBatchSelectorComponent } from './shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component'; +import { + ExportMetadataXlsSelectorComponent +} from './shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component'; /** * Creates all of the app's menus @@ -511,6 +514,20 @@ export class MenuResolver implements Resolve { } as OnClickMenuItemModel, shouldPersistOnRouteChange: true }); + this.menuService.addSection(MenuID.ADMIN, { + id: 'export_metadata_xls', + parentID: 'export', + active: true, + visible: true, + model: { + type: MenuItemType.ONCLICK, + text: 'menu.section.export_metadata_xls', + function: () => { + this.modalService.open(ExportMetadataXlsSelectorComponent); + } + } as OnClickMenuItemModel, + shouldPersistOnRouteChange: true + }); this.menuService.addSection(MenuID.ADMIN, { id: 'export_batch', parentID: 'export', diff --git a/src/app/shared/context-menu/export-collection/export-collection-menu.component.ts b/src/app/shared/context-menu/export-collection/export-collection-menu.component.ts index e18694417e5..dccc68de169 100644 --- a/src/app/shared/context-menu/export-collection/export-collection-menu.component.ts +++ b/src/app/shared/context-menu/export-collection/export-collection-menu.component.ts @@ -9,7 +9,7 @@ import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model' import { ContextMenuEntryComponent } from '../context-menu-entry.component'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { ProcessParameter } from '../../../process-page/processes/process-parameter.model'; -import { ScriptDataService } from '../../../core/data/processes/script-data.service'; +import { COLLECTION_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../core/data/processes/script-data.service'; import { NotificationsService } from '../../notifications/notifications.service'; import { RequestService } from '../../../core/data/request.service'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; @@ -62,7 +62,7 @@ export class ExportCollectionMenuComponent extends ContextMenuEntryComponent { { name: '-c', value: this.contextMenuObject.id } ]; - this.scriptService.invoke('collection-export', stringParameters, []) + this.scriptService.invoke(COLLECTION_EXPORT_SCRIPT_NAME, stringParameters, []) .pipe(getFirstCompletedRemoteData()) .subscribe((rd: RemoteData) => { if (rd.isSuccess) { diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts index 28b4556e2d2..e6a11c40d55 100644 --- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts @@ -10,6 +10,7 @@ export enum SelectorActionType { CREATE = 'create', EDIT = 'edit', EXPORT_METADATA = 'export-metadata', + EXPORT_METADATA_XLS = 'export-metadata-xls', IMPORT_ITEM = 'import-item', EXPORT_ITEM = 'export-item', IMPORT_BATCH = 'import-batch', diff --git a/src/app/shared/dso-selector/modal-wrappers/export-excel-selector/export-excel-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/export-excel-selector/export-excel-selector.component.ts index 3c7220492ae..d2d58717a2f 100644 --- a/src/app/shared/dso-selector/modal-wrappers/export-excel-selector/export-excel-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/export-excel-selector/export-excel-selector.component.ts @@ -6,7 +6,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; import { TranslateService } from '@ngx-translate/core'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; -import { ScriptDataService } from '../../../../core/data/processes/script-data.service'; +import { COLLECTION_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service'; import { RemoteData } from '../../../../core/data/remote-data'; import { ProcessParameter } from '../../../../process-page/processes/process-parameter.model'; import { Process } from '../../../../process-page/processes/process.model'; @@ -45,7 +45,7 @@ export class ExportExcelSelectorComponent extends DSOSelectorModalWrapperCompone { name: '-c', value: dso.id } ]; - this.scriptService.invoke('collection-export', stringParameters, []) + this.scriptService.invoke(COLLECTION_EXPORT_SCRIPT_NAME, stringParameters, []) .pipe(getFirstCompletedRemoteData()) .subscribe((rd: RemoteData) => { if (rd.isSuccess) { diff --git a/src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.spec.ts new file mode 100644 index 00000000000..d8048fd6e5d --- /dev/null +++ b/src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.spec.ts @@ -0,0 +1,180 @@ +import { of as observableOf } from 'rxjs'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { DebugElement, NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NgbActiveModal, NgbModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; +import { ActivatedRoute, Router } from '@angular/router'; +import { COLLECTION_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service'; +import { Collection } from '../../../../core/shared/collection.model'; +import { Item } from '../../../../core/shared/item.model'; +import { ProcessParameter } from '../../../../process-page/processes/process-parameter.model'; +import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component'; +import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { + createFailedRemoteDataObject$, + createSuccessfulRemoteDataObject, + createSuccessfulRemoteDataObject$ +} from '../../../remote-data.utils'; +import { ExportMetadataXlsSelectorComponent } from './export-metadata-xls-selector.component'; + +// No way to add entryComponents yet to testbed; alternative implemented; source: https://stackoverflow.com/questions/41689468/how-to-shallow-test-a-component-with-an-entrycomponents +@NgModule({ + imports: [NgbModalModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + exports: [], + declarations: [ConfirmationModalComponent], + providers: [] +}) +class ModelTestModule { +} + +describe('ExportMetadataXlsSelectorComponent', () => { + let component: ExportMetadataXlsSelectorComponent; + let fixture: ComponentFixture; + let debugElement: DebugElement; + let modalRef; + + let router; + let notificationService: NotificationsServiceStub; + let scriptService; + + const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018' + }); + + const mockCollection: Collection = Object.assign(new Collection(), { + id: 'test-collection-1-1', + uuid: 'test-collection-1-1', + name: 'test-collection-1', + metadata: { + 'dc.identifier.uri': [ + { + language: null, + value: 'fake/test-collection-1' + } + ] + } + }); + + const itemRD = createSuccessfulRemoteDataObject(mockItem); + const modalStub = jasmine.createSpyObj('modalStub', ['close']); + + beforeEach(waitForAsync(() => { + notificationService = new NotificationsServiceStub(); + router = jasmine.createSpyObj('router', { + navigateByUrl: jasmine.createSpy('navigateByUrl') + }); + scriptService = jasmine.createSpyObj('scriptService', + { + invoke: createSuccessfulRemoteDataObject$({ processId: '45' }) + } + ); + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), ModelTestModule], + declarations: [ExportMetadataXlsSelectorComponent], + providers: [ + { provide: NgbActiveModal, useValue: modalStub }, + { provide: NotificationsService, useValue: notificationService }, + { provide: ScriptDataService, useValue: scriptService }, + { + provide: ActivatedRoute, + useValue: { + root: { + snapshot: { + data: { + dso: itemRD, + }, + }, + } + }, + }, + { + provide: Router, useValue: router + } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExportMetadataXlsSelectorComponent); + component = fixture.componentInstance; + debugElement = fixture.debugElement; + const modalService = TestBed.inject(NgbModal); + modalRef = modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.response = observableOf(true); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('if item is selected', () => { + let scriptRequestSucceeded; + beforeEach((done) => { + component.navigate(mockItem).subscribe((succeeded: boolean) => { + scriptRequestSucceeded = succeeded; + done(); + }); + }); + it('should not invoke collection-export script', () => { + expect(scriptService.invoke).not.toHaveBeenCalled(); + }); + }); + + describe('if collection is selected', () => { + let scriptRequestSucceeded; + beforeEach((done) => { + spyOn((component as any).modalService, 'open').and.returnValue(modalRef); + component.navigate(mockCollection).subscribe((succeeded: boolean) => { + scriptRequestSucceeded = succeeded; + done(); + }); + }); + it('should invoke the collection-export script with option -c uuid', () => { + const parameterValues: ProcessParameter[] = [ + Object.assign(new ProcessParameter(), { name: '-c', value: mockCollection.uuid }), + ]; + expect(scriptService.invoke).toHaveBeenCalledWith(COLLECTION_EXPORT_SCRIPT_NAME, parameterValues, []); + }); + it('success notification is shown', () => { + expect(scriptRequestSucceeded).toBeTrue(); + expect(notificationService.success).toHaveBeenCalled(); + }); + it('redirected to process page', () => { + expect(router.navigateByUrl).toHaveBeenCalledWith('/processes/45'); + }); + }); + + describe('if collection is selected; but script invoke fails', () => { + let scriptRequestSucceeded; + beforeEach((done) => { + spyOn((component as any).modalService, 'open').and.returnValue(modalRef); + jasmine.getEnv().allowRespy(true); + spyOn(scriptService, 'invoke').and.returnValue(createFailedRemoteDataObject$('Error', 500)); + component.navigate(mockCollection).subscribe((succeeded: boolean) => { + scriptRequestSucceeded = succeeded; + done(); + }); + }); + it('error notification is shown', () => { + expect(scriptRequestSucceeded).toBeFalse(); + expect(notificationService.error).toHaveBeenCalled(); + }); + }); + +}); diff --git a/src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.ts new file mode 100644 index 00000000000..9f1a4dfc9ee --- /dev/null +++ b/src/app/shared/dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component.ts @@ -0,0 +1,105 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable, of as observableOf } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { COLLECTION_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service'; +import { Collection } from '../../../../core/shared/collection.model'; +import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ProcessParameter } from '../../../../process-page/processes/process-parameter.model'; +import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component'; +import { isNotEmpty } from '../../../empty.util'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; +import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; +import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; +import { Process } from '../../../../process-page/processes/process.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { getProcessDetailRoute } from '../../../../process-page/process-page-routing.paths'; + +/** + * Component to wrap a list of existing dso's inside a modal + * Used to choose a dso from to export metadata of + */ +@Component({ + selector: 'ds-export-metadata-xls-selector', + templateUrl: '../dso-selector-modal-wrapper.component.html', +}) +export class ExportMetadataXlsSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { + configuration = 'backend'; + objectType = DSpaceObjectType.DSPACEOBJECT; + selectorTypes = [DSpaceObjectType.COLLECTION]; + action = SelectorActionType.EXPORT_METADATA_XLS; + + constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router, + protected notificationsService: NotificationsService, protected translationService: TranslateService, + protected scriptDataService: ScriptDataService, + private modalService: NgbModal) { + super(activeModal, route); + } + + /** + * If the dso is a collection: start export-metadata-xls script & navigate to process if successful + * Otherwise show error message + */ + navigate(dso: DSpaceObject): Observable { + if (dso instanceof Collection) { + const modalRef = this.modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.dso = dso; + modalRef.componentInstance.headerLabel = 'confirmation-modal.export-metadata-xls.header'; + modalRef.componentInstance.infoLabel = 'confirmation-modal.export-metadata-xls.info'; + modalRef.componentInstance.cancelLabel = 'confirmation-modal.export-metadata-xls.cancel'; + modalRef.componentInstance.confirmLabel = 'confirmation-modal.export-metadata-xls.confirm'; + modalRef.componentInstance.confirmIcon = 'fas fa-file-export'; + const resp$ = modalRef.componentInstance.response.pipe(switchMap((confirm: boolean) => { + if (confirm) { + const startScriptSucceeded$ = this.startScriptNotifyAndRedirect(dso); + return startScriptSucceeded$.pipe( + switchMap((r: boolean) => { + return observableOf(r); + }) + ); + } else { + const modalRefExport = this.modalService.open(ExportMetadataXlsSelectorComponent); + modalRefExport.componentInstance.dsoRD = createSuccessfulRemoteDataObject(dso); + } + })); + resp$.subscribe(); + return resp$; + } else { + return observableOf(false); + } + } + + /** + * Start export-metadata-xls script of dso & navigate to process if successful + * Otherwise show error message + * @param dso Dso to export + */ + private startScriptNotifyAndRedirect(dso: DSpaceObject): Observable { + const parameterValues: ProcessParameter[] = [ + Object.assign(new ProcessParameter(), { name: '-c', value: dso.uuid }), + ]; + return this.scriptDataService.invoke(COLLECTION_EXPORT_SCRIPT_NAME, parameterValues, []) + .pipe( + getFirstCompletedRemoteData(), + map((rd: RemoteData) => { + if (rd.hasSucceeded) { + const title = this.translationService.get('process.new.notification.success.title'); + const content = this.translationService.get('process.new.notification.success.content'); + this.notificationsService.success(title, content); + if (isNotEmpty(rd.payload)) { + this.router.navigateByUrl(getProcessDetailRoute(rd.payload.processId)); + } + return true; + } else { + const title = this.translationService.get('process.new.notification.error.title'); + const content = this.translationService.get('process.new.notification.error.content'); + this.notificationsService.error(title, content); + return false; + } + })); + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c43d09aa9f3..992bd3cf10a 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -327,6 +327,7 @@ import { ItemCorrectionComponent } from './object-collection/shared/mydspace-ite import { MetricsModule } from './metric/metrics.module'; import { SearchChartBarHorizontalComponent } from './search/search-charts/search-chart/search-chart-bar-horizontal/search-chart-bar-horizontal.component'; import { ThumbnailService } from './thumbnail/thumbnail.service'; +import { ExportMetadataXlsSelectorComponent } from './dso-selector/modal-wrappers/export-metadata-xls-selector/export-metadata-xls-selector.component'; const MODULES = [ CommonModule, @@ -480,6 +481,7 @@ const COMPONENTS = [ CollectionDropdownComponent, EntityDropdownComponent, ExportMetadataSelectorComponent, + ExportMetadataXlsSelectorComponent, ImportBatchSelectorComponent, ExportBatchSelectorComponent, ConfirmationModalComponent, @@ -579,6 +581,7 @@ const ENTRY_COMPONENTS = [ BitstreamRequestACopyPageComponent, CurationFormComponent, ExportMetadataSelectorComponent, + ExportMetadataXlsSelectorComponent, ImportBatchSelectorComponent, ExportBatchSelectorComponent, ConfirmationModalComponent, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index b38b4c6a56c..aae6dfecb01 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1762,6 +1762,8 @@ "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", + "dso-selector.export-metadata-xls.dspaceobject.head": "Export metadata from", + "dso-selector.import-item.item.head": "Import items", "dso-selector.import-item.sub-level": "Bulk import items in", @@ -1802,6 +1804,14 @@ "confirmation-modal.export-metadata.confirm": "Export", + "confirmation-modal.export-metadata-xls.header": "Export metadata for {{ dsoName }}", + + "confirmation-modal.export-metadata-xls.info": "Are you sure you want to export metadata for {{ dsoName }}", + + "confirmation-modal.export-metadata-xls.cancel": "Cancel", + + "confirmation-modal.export-metadata-xls.confirm": "Export", + "confirmation-modal.export-batch.header": "Export batch (ZIP) for {{ dsoName }}", "confirmation-modal.export-batch.info": "Are you sure you want to export batch (ZIP) for {{ dsoName }}", @@ -3471,7 +3481,9 @@ "menu.section.export_item": "Item", - "menu.section.export_metadata": "Metadata", + "menu.section.export_metadata": "Metadata CSV", + + "menu.section.export_metadata_xls": "Metadata XLS", "menu.section.import_from_excel": "Import from excel", From f4e36aa261a4d0245cd3c304de118633de81baf5 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 11 Nov 2022 17:44:20 +0100 Subject: [PATCH 019/195] [DSC-818] renaming export-metadata to export-metadata-csv --- src/app/menu.resolver.ts | 12 ++++++------ .../dso-selector-modal-wrapper.component.ts | 2 +- .../export-batch-selector.component.ts | 2 +- ...port-metadata-csv-selector.component.spec.ts} | 12 ++++++------ .../export-metadata-csv-selector.component.ts} | 16 ++++++++-------- src/app/shared/shared.module.ts | 8 ++++---- src/assets/i18n/en.json5 | 12 ++++++------ 7 files changed, 32 insertions(+), 32 deletions(-) rename src/app/shared/dso-selector/modal-wrappers/{export-metadata-selector/export-metadata-selector.component.spec.ts => export-metadata-csv-selector/export-metadata-csv-selector.component.spec.ts} (94%) rename src/app/shared/dso-selector/modal-wrappers/{export-metadata-selector/export-metadata-selector.component.ts => export-metadata-csv-selector/export-metadata-csv-selector.component.ts} (93%) diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index fa2e8771b77..8604122f300 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -33,8 +33,8 @@ import { EditItemSelectorComponent } from './shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; import { - ExportMetadataSelectorComponent -} from './shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component'; + ExportMetadataCsvSelectorComponent +} from './shared/dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component'; import { AuthorizationDataService } from './core/data/feature-authorization/authorization-data.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { @@ -431,7 +431,7 @@ export class MenuResolver implements Resolve { } /** - * Create menu sections dependent on whether or not the current user is a site administrator and on whether or not + * Create menu sections depending on whether or not the current user is a site administrator and on whether or not * the export scripts exist and the current user is allowed to execute them */ createExportMenuSections() { @@ -501,15 +501,15 @@ export class MenuResolver implements Resolve { shouldPersistOnRouteChange: true }); this.menuService.addSection(MenuID.ADMIN, { - id: 'export_metadata', + id: 'export_metadata_csv', parentID: 'export', active: true, visible: true, model: { type: MenuItemType.ONCLICK, - text: 'menu.section.export_metadata', + text: 'menu.section.export_metadata_csv', function: () => { - this.modalService.open(ExportMetadataSelectorComponent); + this.modalService.open(ExportMetadataCsvSelectorComponent); } } as OnClickMenuItemModel, shouldPersistOnRouteChange: true diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts index e6a11c40d55..41a448e714b 100644 --- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts @@ -9,7 +9,7 @@ import { hasValue, isNotEmpty } from '../../empty.util'; export enum SelectorActionType { CREATE = 'create', EDIT = 'edit', - EXPORT_METADATA = 'export-metadata', + EXPORT_METADATA_CSV = 'export-metadata-csv', EXPORT_METADATA_XLS = 'export-metadata-xls', IMPORT_ITEM = 'import-item', EXPORT_ITEM = 'export-item', diff --git a/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts index 8a48d8a474d..47c38b6f748 100644 --- a/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts @@ -26,7 +26,7 @@ import { FeatureID } from '../../../../core/data/feature-authorization/feature-i * Used to choose a dso from to export metadata of */ @Component({ - selector: 'ds-export-metadata-selector', + selector: 'ds-export-metadata-csv-selector', templateUrl: '../dso-selector-modal-wrapper.component.html', }) export class ExportBatchSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { diff --git a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component.spec.ts similarity index 94% rename from src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts rename to src/app/shared/dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component.spec.ts index f6b3581df5b..9344b95f65a 100644 --- a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts +++ b/src/app/shared/dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component.spec.ts @@ -19,7 +19,7 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; -import { ExportMetadataSelectorComponent } from './export-metadata-selector.component'; +import { ExportMetadataCsvSelectorComponent } from './export-metadata-csv-selector.component'; // No way to add entryComponents yet to testbed; alternative implemented; source: https://stackoverflow.com/questions/41689468/how-to-shallow-test-a-component-with-an-entrycomponents @NgModule({ @@ -38,9 +38,9 @@ import { ExportMetadataSelectorComponent } from './export-metadata-selector.comp class ModelTestModule { } -describe('ExportMetadataSelectorComponent', () => { - let component: ExportMetadataSelectorComponent; - let fixture: ComponentFixture; +describe('ExportMetadataCsvSelectorComponent', () => { + let component: ExportMetadataCsvSelectorComponent; + let fixture: ComponentFixture; let debugElement: DebugElement; let modalRef; @@ -97,7 +97,7 @@ describe('ExportMetadataSelectorComponent', () => { ); TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), ModelTestModule], - declarations: [ExportMetadataSelectorComponent], + declarations: [ExportMetadataCsvSelectorComponent], providers: [ { provide: NgbActiveModal, useValue: modalStub }, { provide: NotificationsService, useValue: notificationService }, @@ -124,7 +124,7 @@ describe('ExportMetadataSelectorComponent', () => { })); beforeEach(() => { - fixture = TestBed.createComponent(ExportMetadataSelectorComponent); + fixture = TestBed.createComponent(ExportMetadataCsvSelectorComponent); component = fixture.componentInstance; debugElement = fixture.debugElement; const modalService = TestBed.inject(NgbModal); diff --git a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component.ts similarity index 93% rename from src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts rename to src/app/shared/dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component.ts index a04fc0a1cd2..a18474a7519 100644 --- a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component.ts @@ -25,13 +25,13 @@ import { getProcessDetailRoute } from '../../../../process-page/process-page-rou * Used to choose a dso from to export metadata of */ @Component({ - selector: 'ds-export-metadata-selector', + selector: 'ds-export-metadata-csv-selector', templateUrl: '../dso-selector-modal-wrapper.component.html', }) -export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { +export class ExportMetadataCsvSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { objectType = DSpaceObjectType.DSPACEOBJECT; selectorTypes = [DSpaceObjectType.COLLECTION, DSpaceObjectType.COMMUNITY]; - action = SelectorActionType.EXPORT_METADATA; + action = SelectorActionType.EXPORT_METADATA_CSV; constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router, protected notificationsService: NotificationsService, protected translationService: TranslateService, @@ -48,10 +48,10 @@ export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComp if (dso instanceof Collection || dso instanceof Community) { const modalRef = this.modalService.open(ConfirmationModalComponent); modalRef.componentInstance.dso = dso; - modalRef.componentInstance.headerLabel = 'confirmation-modal.export-metadata.header'; - modalRef.componentInstance.infoLabel = 'confirmation-modal.export-metadata.info'; - modalRef.componentInstance.cancelLabel = 'confirmation-modal.export-metadata.cancel'; - modalRef.componentInstance.confirmLabel = 'confirmation-modal.export-metadata.confirm'; + modalRef.componentInstance.headerLabel = 'confirmation-modal.export-metadata-csv.header'; + modalRef.componentInstance.infoLabel = 'confirmation-modal.export-metadata-csv.info'; + modalRef.componentInstance.cancelLabel = 'confirmation-modal.export-metadata-csv.cancel'; + modalRef.componentInstance.confirmLabel = 'confirmation-modal.export-metadata-csv.confirm'; modalRef.componentInstance.confirmIcon = 'fas fa-file-export'; const resp$ = modalRef.componentInstance.response.pipe(switchMap((confirm: boolean) => { if (confirm) { @@ -62,7 +62,7 @@ export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComp }) ); } else { - const modalRefExport = this.modalService.open(ExportMetadataSelectorComponent); + const modalRefExport = this.modalService.open(ExportMetadataCsvSelectorComponent); modalRefExport.componentInstance.dsoRD = createSuccessfulRemoteDataObject(dso); } })); diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 992bd3cf10a..dd463c3f71e 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -23,8 +23,8 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { MomentModule } from 'ngx-moment'; import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component'; import { - ExportMetadataSelectorComponent -} from './dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component'; + ExportMetadataCsvSelectorComponent +} from './dso-selector/modal-wrappers/export-metadata-csv-selector/export-metadata-csv-selector.component'; import { ExportBatchSelectorComponent } from './dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component'; @@ -480,7 +480,7 @@ const COMPONENTS = [ BitstreamRequestACopyPageComponent, CollectionDropdownComponent, EntityDropdownComponent, - ExportMetadataSelectorComponent, + ExportMetadataCsvSelectorComponent, ExportMetadataXlsSelectorComponent, ImportBatchSelectorComponent, ExportBatchSelectorComponent, @@ -580,7 +580,7 @@ const ENTRY_COMPONENTS = [ BitstreamDownloadPageComponent, BitstreamRequestACopyPageComponent, CurationFormComponent, - ExportMetadataSelectorComponent, + ExportMetadataCsvSelectorComponent, ExportMetadataXlsSelectorComponent, ImportBatchSelectorComponent, ExportBatchSelectorComponent, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index aae6dfecb01..0c9a87762d7 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1760,7 +1760,7 @@ "dso-selector.error.title": "An error occurred searching for a {{ type }}", - "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", + "dso-selector.export-metadata-csv.dspaceobject.head": "Export metadata from", "dso-selector.export-metadata-xls.dspaceobject.head": "Export metadata from", @@ -1796,13 +1796,13 @@ "dso-selector.export-item.sub-level": "Export to excel items in", - "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}", + "confirmation-modal.export-metadata-csv.header": "Export metadata for {{ dsoName }}", - "confirmation-modal.export-metadata.info": "Are you sure you want to export metadata for {{ dsoName }}", + "confirmation-modal.export-metadata-csv.info": "Are you sure you want to export metadata for {{ dsoName }}", - "confirmation-modal.export-metadata.cancel": "Cancel", + "confirmation-modal.export-metadata-csv.cancel": "Cancel", - "confirmation-modal.export-metadata.confirm": "Export", + "confirmation-modal.export-metadata-csv.confirm": "Export", "confirmation-modal.export-metadata-xls.header": "Export metadata for {{ dsoName }}", @@ -3481,7 +3481,7 @@ "menu.section.export_item": "Item", - "menu.section.export_metadata": "Metadata CSV", + "menu.section.export_metadata_csv": "Metadata CSV", "menu.section.export_metadata_xls": "Metadata XLS", From 47e65861e1033695c7c34247560ba9bcaccf6d53 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 2 Mar 2023 12:06:41 +0100 Subject: [PATCH 020/195] [DSC-966] Show metric donuts in column --- .../object-list/metric-donuts/metric-donuts.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/object-list/metric-donuts/metric-donuts.component.html b/src/app/shared/object-list/metric-donuts/metric-donuts.component.html index 539c0839236..eb74c7aaedd 100644 --- a/src/app/shared/object-list/metric-donuts/metric-donuts.component.html +++ b/src/app/shared/object-list/metric-donuts/metric-donuts.component.html @@ -1,7 +1,7 @@ -
+
Date: Wed, 5 Apr 2023 15:26:25 +0200 Subject: [PATCH 021/195] [CST-8654] added link to potential duplicate --- .../duplicate-match/duplicate-match.component.html | 11 +++++++++-- .../duplicate-match/duplicate-match.component.ts | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html index 465d79f2357..5659813f852 100644 --- a/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html +++ b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.html @@ -21,7 +21,7 @@
+ + {{"submission.workflow.generic.view" | translate}} + +
diff --git a/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.ts b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.ts index eaa21688a1a..6f29a06f0c8 100644 --- a/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.ts +++ b/src/app/submission/sections/detect-duplicate/duplicate-match/duplicate-match.component.ts @@ -20,6 +20,7 @@ import { SectionsService } from '../../sections.service'; import { DuplicateMatchMetadataDetailConfig } from '../models/duplicate-detail-metadata.model'; import { DetectDuplicateMatch } from '../../../../core/submission/models/workspaceitem-section-deduplication.model'; import { environment } from '../../../../../environments/environment'; +import {getEntityPageRoute, getItemPageRoute} from "../../../../item-page/item-page-routing-paths"; /** * This component shows a single possible duplication within the duplications section. @@ -303,4 +304,9 @@ export class DuplicateMatchComponent implements OnInit { this.modalRef = this.modalService.open(modal); } + getItemPage(item: Item) { + return getItemPageRoute(item); + } + + protected readonly getItemPageRoute = getItemPageRoute; } From 8060e0ec7275a096056b1c6517d43520d16827b5 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 10 May 2023 14:18:32 +0200 Subject: [PATCH 022/195] [DSC-1058] show filter labels on filter select --- src/app/shared/search/search.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index 0e7a0dcd21e..be0d7b56914 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -117,7 +117,7 @@
- +
From 0f0c27d054e163989c3b0d2c6fdaf7a0a5cb1f36 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 30 May 2023 16:47:20 +0200 Subject: [PATCH 023/195] [DSC-38] Export statistics map --- package.json | 4 +- src/app/core/export-service/export.service.ts | 17 ++++++ .../statistics-map.component.html | 44 ++++++++++----- .../statistics-map.component.ts | 45 +++++++--------- src/assets/i18n/en.json5 | 4 +- yarn.lock | 53 ++++++++++--------- 6 files changed, 100 insertions(+), 67 deletions(-) diff --git a/package.json b/package.json index 399441566af..239bd948a85 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "morgan": "^1.10.0", "ng-mocks": "^13.1.1", "ng2-file-upload": "1.4.0", - "ng2-google-charts": "^6.1.0", + "ng2-google-charts": "^7.0.0", "ng2-nouislider": "^1.8.3", "ngx-infinite-scroll": "^10.0.1", "ngx-moment": "^5.0.0", @@ -181,7 +181,7 @@ "eslint": "^8.2.0", "eslint-plugin-deprecation": "^1.3.2", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-jsdoc": "^38.0.6", + "eslint-plugin-jsdoc": "^39.6.4", "eslint-plugin-unused-imports": "^2.0.0", "fork-ts-checker-webpack-plugin": "^6.0.3", "html-loader": "^1.3.2", diff --git a/src/app/core/export-service/export.service.ts b/src/app/core/export-service/export.service.ts index 7c7578f4760..53b53fc2df3 100644 --- a/src/app/core/export-service/export.service.ts +++ b/src/app/core/export-service/export.service.ts @@ -5,6 +5,7 @@ import { toJpeg, toPng } from 'html-to-image'; import { Options } from 'html-to-image/es/options'; import { saveAs } from 'file-saver'; import { BehaviorSubject } from 'rxjs'; +import { hasValue } from 'src/app/shared/empty.util'; export enum ExportImageType { png = 'png', @@ -69,4 +70,20 @@ export class ExportService { } + /** + * Creates an image from the given base64 string. + * @param base64 the base64 string + * @param type image type (png or jpeg) + * @param fileName + * @param isLoading + */ + exportImageWithBase64(base64: string, type: ExportImageType, fileName: string, isLoading: BehaviorSubject): void { + if (hasValue(base64)) { + saveAs(base64, fileName + '.' + type); + } else { + console.error('Base64 string is empty'); + } + + isLoading.next(false); + } } diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html index 62934b8924c..54af8dcd0e0 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.html @@ -1,13 +1,31 @@ -
- - -
-
- -
+ +
+
+
+ +
+ +
+
+
+
+ +
+ +
+
diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts index 6f0eead5f1b..9f803cf029a 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts @@ -1,10 +1,8 @@ -import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { UsageReport } from '../../../core/statistics/models/usage-report.model'; -import { GoogleChartInterface } from 'ng2-google-charts'; +import { GoogleChartComponent, GoogleChartInterface } from 'ng2-google-charts'; import { ExportImageType, ExportService } from '../../../core/export-service/export.service'; import { BehaviorSubject } from 'rxjs'; - - @Component({ selector: 'ds-statistics-map', templateUrl: './statistics-map.component.html', @@ -36,14 +34,20 @@ export class StatisticsMapComponent implements OnInit { * Loading utilized for export functions to disable buttons */ isLoading: BehaviorSubject = new BehaviorSubject(false); - /** - * Loading utilized for export functions to disable buttons - */ - isSecondLoading: BehaviorSubject = new BehaviorSubject(false); + + isLoading$: BehaviorSubject = new BehaviorSubject(false); + /** * Chart ElementRef */ - @ViewChild('googleChartRef') googleChartRef: ElementRef; + @ViewChild('googleChartRef') googleChartRef: GoogleChartComponent; + + exportImageType = ExportImageType; + + exportImageTypes = [ + { type: ExportImageType.png, label: 'PNG' }, + { type: ExportImageType.jpeg, label: 'JPEG/JPG' } + ]; constructor( private exportService: ExportService @@ -80,25 +84,16 @@ export class StatisticsMapComponent implements OnInit { ], options: { 'title': this.report.reportType } }; - } /** - * Download map as image in png version. + * Export the map as an image + * @param type of export */ - downloadPng() { - this.isLoading.next(false); - const node = this.googleChartRef.nativeElement; - this.exportService.exportAsImage(node, ExportImageType.png, this.report.reportType, this.isLoading); + exportMapAsImage(type: ExportImageType) { + this.isLoading$.next(true); + const chart = this.googleChartRef.wrapper.getChart(); + const imageURI: string = chart?.getImageURI(); + this.exportService.exportImageWithBase64(imageURI, type, this.report.reportType, this.isLoading$); } - - /** - * Download map as image in jpeg version. - */ - downloadJpeg() { - this.isSecondLoading.next(false); - const node = this.googleChartRef.nativeElement; - this.exportService.exportAsImage(node, ExportImageType.jpeg, this.report.reportType, this.isSecondLoading); - } - } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index fec5973a533..3408366b70f 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -6181,5 +6181,7 @@ "invitation.ignore-btn": "Ignore", - "authority-confidence.search-label":"Search" + "authority-confidence.search-label":"Search", + + "statistics-page.export-map-as-image": "Export map", } diff --git a/yarn.lock b/yarn.lock index caae587bf34..439ccf953ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1494,14 +1494,14 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@es-joy/jsdoccomment@~0.22.1": - version "0.22.1" - resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.22.1.tgz#3c86d458780231769215a795105bd3b03b2616f2" - integrity sha512-/WMkqLYfwCf0waCAMC8Eddt3iAOdghkDF5vmyKEu8pfO66KRFY1L15yks8mfgURiwOAOJpAQ3blvB3Znj6ZwBw== +"@es-joy/jsdoccomment@~0.36.1": + version "0.36.1" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz#c37db40da36e4b848da5fd427a74bae3b004a30f" + integrity sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg== dependencies: comment-parser "1.3.1" esquery "^1.4.0" - jsdoc-type-pratt-parser "~2.2.5" + jsdoc-type-pratt-parser "~3.1.0" "@eslint/eslintrc@^1.2.1": version "1.2.1" @@ -5965,18 +5965,17 @@ eslint-plugin-import@^2.25.4: resolve "^1.20.0" tsconfig-paths "^3.12.0" -eslint-plugin-jsdoc@^38.0.6: - version "38.0.6" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.0.6.tgz#b26843bdc445202b9f0e3830bda39ec5aacbfa97" - integrity sha512-Wvh5ERLUL8zt2yLZ8LLgi8RuF2UkjDvD+ri1/i7yMpbfreK2S29B9b5JC7iBIoFR7KDaEWCLnUPHTqgwcXX1Sg== +eslint-plugin-jsdoc@^39.6.4: + version "39.9.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.9.1.tgz#e9ce1723411fd7ea0933b3ef0dd02156ae3068e2" + integrity sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw== dependencies: - "@es-joy/jsdoccomment" "~0.22.1" + "@es-joy/jsdoccomment" "~0.36.1" comment-parser "1.3.1" debug "^4.3.4" escape-string-regexp "^4.0.0" esquery "^1.4.0" - regextras "^0.8.0" - semver "^7.3.5" + semver "^7.3.8" spdx-expression-parse "^3.0.1" eslint-plugin-unused-imports@^2.0.0: @@ -8094,10 +8093,10 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdoc-type-pratt-parser@~2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz#c9f93afac7ee4b5ed4432fe3f09f7d36b05ed0ff" - integrity sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw== +jsdoc-type-pratt-parser@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz#a4a56bdc6e82e5865ffd9febc5b1a227ff28e67e" + integrity sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw== jsdom@19.0.0: version "19.0.0" @@ -9365,12 +9364,12 @@ ng2-file-upload@1.4.0: dependencies: tslib "^1.9.0" -ng2-google-charts@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/ng2-google-charts/-/ng2-google-charts-6.2.0.tgz#c0b89e2b7bde0acbdca7489270e2bb8c91ba75bd" - integrity sha512-dbG93G22hDcNNfdR3vL1GyT8ez0N2J5c8UHrI0MO8+AdFWJm6v4iODRbTGrcUGWYxKsC4V5gRrzfVBtOhMnlhA== +ng2-google-charts@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ng2-google-charts/-/ng2-google-charts-7.0.0.tgz#1e601dad1d6f2f964d052a8f6eeb6d56efd04c1a" + integrity sha512-MRc7oIDAvFVdvW2SQvE8xNjzL5NDdnB4GvJEBRW8PYsuNH1gj3RlQRdFRDdkzaRDy4y9jVI6x6yZZAYP3ZCVLw== dependencies: - tslib "^1.9.0" + tslib "^2.3.0" ng2-nouislider@^1.8.3: version "1.8.3" @@ -11440,11 +11439,6 @@ regexpu-core@^5.0.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" -regextras@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.8.0.tgz#ec0f99853d4912839321172f608b544814b02217" - integrity sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ== - registry-auth-token@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" @@ -11928,6 +11922,13 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.8: + version "7.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" + integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== + dependencies: + lru-cache "^6.0.0" + send@0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" From 8eadb062d5ac333ce968a9cbcc81774d0a2403b0 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 30 May 2023 18:05:19 +0200 Subject: [PATCH 024/195] [DSC-38] ng2-google-charts downgrade --- package.json | 2 +- .../statistics-map.component.ts | 20 +++++++++++++++---- yarn.lock | 10 +++++----- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index f14492785a2..872641ebe32 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "morgan": "^1.10.0", "ng-mocks": "^13.1.1", "ng2-file-upload": "1.4.0", - "ng2-google-charts": "^7.0.0", + "ng2-google-charts": "^6.1.0", "ng2-nouislider": "^1.8.3", "ngx-infinite-scroll": "^10.0.1", "ngx-pagination": "5.0.0", diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts index 9f803cf029a..46d28dd303b 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts @@ -1,8 +1,9 @@ -import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, Inject, Input, OnInit, PLATFORM_ID, ViewChild } from '@angular/core'; import { UsageReport } from '../../../core/statistics/models/usage-report.model'; -import { GoogleChartComponent, GoogleChartInterface } from 'ng2-google-charts'; +import { GoogleChartComponent, GoogleChartInterface, GoogleChartType } from 'ng2-google-charts'; import { ExportImageType, ExportService } from '../../../core/export-service/export.service'; import { BehaviorSubject } from 'rxjs'; +import { isPlatformBrowser } from '@angular/common'; @Component({ selector: 'ds-statistics-map', templateUrl: './statistics-map.component.html', @@ -49,9 +50,20 @@ export class StatisticsMapComponent implements OnInit { { type: ExportImageType.jpeg, label: 'JPEG/JPG' } ]; + protected exportService: ExportService; + constructor( - private exportService: ExportService + @Inject(PLATFORM_ID) protected platformId: Object ) { + if (isPlatformBrowser(this.platformId)) { + import('../../../core/export-service/browser-export.service').then((s) => { + this.exportService = new s.BrowserExportService(); + }); + } else { + import('../../../core/export-service/server-export.service').then((s) => { + this.exportService = new s.ServerExportService(); + }); + } } ngOnInit(): void { @@ -77,7 +89,7 @@ export class StatisticsMapComponent implements OnInit { }); this.geoChart = { - chartType: 'GeoChart', + chartType: GoogleChartType.GeoChart, dataTable: [ this.chartColumns, ...this.data diff --git a/yarn.lock b/yarn.lock index 92b0dd79a03..fbb54046db0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8548,12 +8548,12 @@ ng2-file-upload@1.4.0: dependencies: tslib "^1.9.0" -ng2-google-charts@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/ng2-google-charts/-/ng2-google-charts-7.0.0.tgz#1e601dad1d6f2f964d052a8f6eeb6d56efd04c1a" - integrity sha512-MRc7oIDAvFVdvW2SQvE8xNjzL5NDdnB4GvJEBRW8PYsuNH1gj3RlQRdFRDdkzaRDy4y9jVI6x6yZZAYP3ZCVLw== +ng2-google-charts@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ng2-google-charts/-/ng2-google-charts-6.2.0.tgz#c0b89e2b7bde0acbdca7489270e2bb8c91ba75bd" + integrity sha512-dbG93G22hDcNNfdR3vL1GyT8ez0N2J5c8UHrI0MO8+AdFWJm6v4iODRbTGrcUGWYxKsC4V5gRrzfVBtOhMnlhA== dependencies: - tslib "^2.3.0" + tslib "^1.9.0" ng2-nouislider@^1.8.3: version "1.8.3" From ee8c3275fce0f604ca7fa9760226104497c1a2e6 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 30 May 2023 19:14:27 +0200 Subject: [PATCH 025/195] [DSC-38] Unit test fix --- .../statistics-map.component.spec.ts | 24 +++++++++---------- .../statistics-map.component.ts | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts index 0fa818149ed..62ff1e1e30e 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.spec.ts @@ -5,7 +5,7 @@ import { UsageReport } from '../../../core/statistics/models/usage-report.model' import { USAGE_REPORT } from '../../../core/statistics/models/usage-report.resource-type'; import { GoogleChartInterface } from 'ng2-google-charts'; -import { ExportService, ExportImageType } from '../../../core/export-service/export.service'; +import { ExportService } from '../../../core/export-service/export.service'; import { TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; import { StatisticsType } from '../statistics-type.model'; @@ -55,14 +55,17 @@ describe('StatisticsMapComponent', () => { const exportServiceMock: any = { exportAsImage: jasmine.createSpy('exportAsImage'), - exportAsFile: jasmine.createSpy('exportAsFile') + exportAsFile: jasmine.createSpy('exportAsFile'), + exportImageWithBase64: jasmine.createSpy('exportImageWithBase64') }; + + let exportService: ExportService = exportServiceMock; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [TranslateModule.forRoot()], declarations: [ StatisticsMapComponent ], providers: [ - { provide: ExportService, useValue: exportServiceMock } + // { provide: ExportService, useValue: exportServiceMock } ], }) .compileComponents(); @@ -71,6 +74,8 @@ describe('StatisticsMapComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(StatisticsMapComponent); component = fixture.componentInstance; + spyOn(component, 'exportMapAsImage'); + (component as any).exportService = exportServiceMock; fixture.detectChanges(); }); @@ -94,18 +99,11 @@ describe('StatisticsMapComponent', () => { it('should download map as png and jpg', () => { component.report = report; - fixture.detectChanges(); component.ngOnInit(); fixture.detectChanges(); - const downloadPngMapBtn = fixture.debugElement.query(By.css('[data-test="download-png-map-btn"]')); - downloadPngMapBtn.triggerEventHandler('click', null); - fixture.detectChanges(); - const node = fixture.debugElement.query(By.css('[data-test="google-chart-ref"]')).nativeElement; - expect(exportServiceMock.exportAsImage).toHaveBeenCalledWith(node, ExportImageType.png, report.reportType, component.isLoading); - - const downloadJpgMapBtn = fixture.debugElement.query(By.css('[data-test="download-jpg-map-btn"]')); - downloadJpgMapBtn.triggerEventHandler('click', null); + const drpdButton = fixture.debugElement.query(By.css('div[ngbdropdownmenu]>button[ngbdropdownitem]')); + drpdButton.triggerEventHandler('click', null); fixture.detectChanges(); - expect(exportServiceMock.exportAsImage).toHaveBeenCalledWith(node, ExportImageType.jpeg, report.reportType, component.isSecondLoading); + expect(component.exportMapAsImage).toHaveBeenCalled(); }); }); diff --git a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts index 46d28dd303b..fde75a47040 100644 --- a/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts +++ b/src/app/statistics-page/cris-statistics-page/statistics-map/statistics-map.component.ts @@ -102,7 +102,7 @@ export class StatisticsMapComponent implements OnInit { * Export the map as an image * @param type of export */ - exportMapAsImage(type: ExportImageType) { + exportMapAsImage(type: ExportImageType){ this.isLoading$.next(true); const chart = this.googleChartRef.wrapper.getChart(); const imageURI: string = chart?.getImageURI(); From f9627d556b6ab9717ba8d907fb9debf9760b18f6 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 31 May 2023 14:53:24 +0200 Subject: [PATCH 026/195] [DSC-381] Truncate breadcrumb text and show a tooltip for truncated ones --- .../breadcrumb/is-text-truncated.pipe.ts | 22 +++++++++++++++ ...runcate-breadcrumb-item-characters.pipe.ts | 28 +++++++++++++++++++ .../breadcrumbs/breadcrumbs.component.html | 17 +++++++++-- src/app/root.module.ts | 4 +++ src/config/app-config.interface.ts | 1 + src/config/default-app-config.ts | 1 + src/environments/environment.test.ts | 3 +- 7 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 src/app/breadcrumbs/breadcrumb/is-text-truncated.pipe.ts create mode 100644 src/app/breadcrumbs/breadcrumb/truncate-breadcrumb-item-characters.pipe.ts diff --git a/src/app/breadcrumbs/breadcrumb/is-text-truncated.pipe.ts b/src/app/breadcrumbs/breadcrumb/is-text-truncated.pipe.ts new file mode 100644 index 00000000000..10481588267 --- /dev/null +++ b/src/app/breadcrumbs/breadcrumb/is-text-truncated.pipe.ts @@ -0,0 +1,22 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { hasValue } from '../../shared/empty.util'; + +@Pipe({ + name: 'dsIsTextTruncated', +}) +export class IsTextTruncatedPipe implements PipeTransform { + + /** + * @param truncatedText Translated truncated text (text after TruncateBreadcrumbItemCharactersPipe transform) + * @param fullText Full translated text + * @returns {string} The full text if the truncated text contains an ellipses, otherwise an empty string. + * In case an empty string is returned the tooltip will not be shown. + */ + transform(truncatedText: string, fullText: string): string { + if (hasValue(truncatedText) && truncatedText.includes('...')) { + return fullText; + } else { + return ''; + } + } +} diff --git a/src/app/breadcrumbs/breadcrumb/truncate-breadcrumb-item-characters.pipe.ts b/src/app/breadcrumbs/breadcrumb/truncate-breadcrumb-item-characters.pipe.ts new file mode 100644 index 00000000000..e43ea1c7cf3 --- /dev/null +++ b/src/app/breadcrumbs/breadcrumb/truncate-breadcrumb-item-characters.pipe.ts @@ -0,0 +1,28 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { environment } from '../../../environments/environment'; +import { hasValue } from '../../shared/empty.util'; + +@Pipe({ + name: 'dsTruncateText', +}) +export class TruncateBreadcrumbItemCharactersPipe implements PipeTransform { + /** + * The maximum number of characters to display in a breadcrumb item + * @type {number} + */ + readonly charLimit: number = environment.breadcrumbCharLimit; + + /** + * Truncates the text based on the configured char number allowed per breadcrumb element. + * If text is shorter than the number of chars allowed, it will return the text as it is. + * If text is longer than the number of chars allowed, it will return the text truncated with an ellipsis at the end. + * @param text Traslated text to be truncated + */ + transform(text: string): string { + if (hasValue(text) && text.length > this.charLimit) { + return text.substring(0, this.charLimit).concat('...'); + } else { + return text; + } + } +} diff --git a/src/app/breadcrumbs/breadcrumbs.component.html b/src/app/breadcrumbs/breadcrumbs.component.html index 6f52b256479..5ea9b53b33f 100644 --- a/src/app/breadcrumbs/breadcrumbs.component.html +++ b/src/app/breadcrumbs/breadcrumbs.component.html @@ -10,11 +10,24 @@ - + - +
diff --git a/src/app/root.module.ts b/src/app/root.module.ts index 7671217c53b..84841ae7068 100644 --- a/src/app/root.module.ts +++ b/src/app/root.module.ts @@ -42,6 +42,8 @@ import { import { FooterModule } from './footer/footer.module'; import { SocialModule } from './social/social.module'; import { ExploreModule } from './shared/explore/explore.module'; +import { IsTextTruncatedPipe } from './breadcrumbs/breadcrumb/is-text-truncated.pipe'; +import { TruncateBreadcrumbItemCharactersPipe } from './breadcrumbs/breadcrumb/truncate-breadcrumb-item-characters.pipe'; const IMPORTS = [ CommonModule, @@ -83,6 +85,8 @@ const DECLARATIONS = [ ThemedPageErrorComponent, PageErrorComponent, ContextHelpToggleComponent, + TruncateBreadcrumbItemCharactersPipe, + IsTextTruncatedPipe ]; const EXPORTS = [ diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index d0f336d2af4..441d8818c65 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -66,6 +66,7 @@ interface AppConfig extends Config { attachmentRendering: AttachmentRenderingConfig; advancedAttachmentRendering: AdvancedAttachmentRenderingConfig; searchResult: SearchResultConfig; + breadcrumbCharLimit: number; } /** diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 1261ea04ec4..9f3b6dfcd1b 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -713,4 +713,5 @@ export class DefaultAppConfig implements AppConfig { additionalMetadataFields: [] }; + breadcrumbCharLimit = 10; } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 7bdb8492324..a33e5694559 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -532,6 +532,7 @@ export const environment: BuildConfig = { metadataConfiguration: [] } ] - } + }, + breadcrumbCharLimit: 10, }; From 58d6417549e7e7d7b2f7807571c15370b760dcba Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 31 May 2023 16:02:13 +0200 Subject: [PATCH 027/195] [DSC-381] Fixed failing unit tests --- .../breadcrumbs/breadcrumbs.component.spec.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/breadcrumbs/breadcrumbs.component.spec.ts b/src/app/breadcrumbs/breadcrumbs.component.spec.ts index 69387e75346..7c80c87c3af 100644 --- a/src/app/breadcrumbs/breadcrumbs.component.spec.ts +++ b/src/app/breadcrumbs/breadcrumbs.component.spec.ts @@ -10,22 +10,29 @@ import { TranslateLoaderMock } from '../shared/testing/translate-loader.mock'; import { RouterTestingModule } from '@angular/router/testing'; import { of as observableOf } from 'rxjs'; import { DebugElement } from '@angular/core'; +import { IsTextTruncatedPipe } from './breadcrumb/is-text-truncated.pipe'; +import { TruncateBreadcrumbItemCharactersPipe } from './breadcrumb/truncate-breadcrumb-item-characters.pipe'; describe('BreadcrumbsComponent', () => { let component: BreadcrumbsComponent; let fixture: ComponentFixture; let breadcrumbsServiceMock: BreadcrumbsService; + let truncateTextPipe: TruncateBreadcrumbItemCharactersPipe; const expectBreadcrumb = (listItem: DebugElement, text: string, url: string) => { const anchor = listItem.query(By.css('a')); - + const truncatedText = truncateTextPipe.transform(text); if (url == null) { expect(anchor).toBeNull(); - expect(listItem.nativeElement.innerHTML).toEqual(text); + // remove leading whitespace characters + const textWithoutSpaces = listItem.nativeElement.innerHTML.trimStart().replace(/^\s+/, ''); + expect(textWithoutSpaces).toEqual(truncatedText); } else { expect(anchor).toBeInstanceOf(DebugElement); expect(anchor.attributes.href).toEqual(url); - expect(anchor.nativeElement.innerHTML).toEqual(text); + // remove leading whitespace characters + const textWithoutSpaces = anchor.nativeElement.innerHTML.trimStart().replace(/^\s+/, ''); + expect(textWithoutSpaces).toEqual(truncatedText); } }; @@ -43,6 +50,8 @@ describe('BreadcrumbsComponent', () => { declarations: [ BreadcrumbsComponent, VarDirective, + IsTextTruncatedPipe, + TruncateBreadcrumbItemCharactersPipe, ], imports: [ RouterTestingModule.withRoutes([]), @@ -55,10 +64,12 @@ describe('BreadcrumbsComponent', () => { ], providers: [ { provide: BreadcrumbsService, useValue: breadcrumbsServiceMock }, + { provide: TruncateBreadcrumbItemCharactersPipe, useClass: TruncateBreadcrumbItemCharactersPipe }, ], }).compileComponents(); fixture = TestBed.createComponent(BreadcrumbsComponent); + truncateTextPipe = TestBed.inject(TruncateBreadcrumbItemCharactersPipe); component = fixture.componentInstance; fixture.detectChanges(); })); From 43a03ac8e8f76354713612ef82f96f7a048d7fc9 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 2 Jun 2023 17:45:34 +0200 Subject: [PATCH 028/195] [DSC-106]Date input usable via keyboard using tab --- .../date-picker/date-picker.component.ts | 48 ++++++++++++++++++- .../number-picker.component.html | 4 +- .../number-picker/number-picker.component.ts | 1 + .../sections/form/section-form.component.ts | 2 +- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts index 3ff94542a87..e34989f9765 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, HostListener, Inject, Input, OnInit, Output, Renderer2 } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { DynamicDsDatePickerModel } from './date-picker.model'; import { hasValue } from '../../../../../empty.util'; @@ -7,6 +7,8 @@ import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; +import { DOCUMENT } from '@angular/common'; +import { isEqual } from 'lodash'; export const DS_DATE_PICKER_SEPARATOR = '-'; @@ -51,7 +53,9 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement disabledMonth = true; disabledDay = true; constructor(protected layoutService: DynamicFormLayoutService, - protected validationService: DynamicFormValidationService + protected validationService: DynamicFormValidationService, + private renderer: Renderer2, + @Inject(DOCUMENT) private _document: Document ) { super(layoutService, validationService); } @@ -164,6 +168,46 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement this.change.emit(value); } + /** + * Listen to keydown Tab event. + * Get the active element and blur it, in order to focus the next input field. + */ + @HostListener('keydown', ['$event']) + onKeyDown(event: KeyboardEvent) { + if (event.key === 'Tab') { + event.preventDefault(); + const activeElement: Element = this._document.activeElement; + (activeElement as any).blur(); + if (isEqual(activeElement.id, this.model.id.concat('_year')) ) { + this.focusInput('_month'); + } else if (isEqual(activeElement.id, this.model.id.concat('_month'))) { + this.focusInput('_day'); + } + } + } + + /** + * Focus the input field for the given type + * based on the model id. + * Used to focus the next input field + * in case of a disabled field. + * @param type '_month' | '_day' + */ + focusInput(type: '_month' | '_day') { + const field = this._document.getElementById(this.model.id.concat(type)); + if (field) { + + if (hasValue(this.year) && isEqual(type, '_month')) { + this.disabledMonth = false; + } else if (hasValue(this.month) && isEqual(type, '_day')) { + this.disabledDay = false; + } + setTimeout(() => { + this.renderer.selectRootElement(field).focus(); + }, 100); + } + } + onFocus(event) { this.focus.emit(event); } diff --git a/src/app/shared/form/number-picker/number-picker.component.html b/src/app/shared/form/number-picker/number-picker.component.html index 314d9d3b70b..2353a6533cf 100644 --- a/src/app/shared/form/number-picker/number-picker.component.html +++ b/src/app/shared/form/number-picker/number-picker.component.html @@ -20,7 +20,7 @@ - - - - diff --git a/src/app/shared/context-menu/subscription/subscription-menu.component.ts b/src/app/shared/context-menu/subscription/subscription-menu.component.ts index 29f1404fc59..39005009366 100644 --- a/src/app/shared/context-menu/subscription/subscription-menu.component.ts +++ b/src/app/shared/context-menu/subscription/subscription-menu.component.ts @@ -10,6 +10,7 @@ import { rendersContextMenuEntriesForType } from '../context-menu.decorator'; import { ContextMenuEntryType } from '../context-menu-entry-type'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; +import { SubscriptionModalComponent } from '../../subscriptions/subscription-modal/subscription-modal.component'; @Component({ selector: 'ds-subscription-menu', @@ -35,17 +36,6 @@ export class SubscriptionMenuComponent extends ContextMenuEntryComponent impleme */ public modalRef: NgbModalRef; - types = [ - { name: 'Content', value: 'content' }, - { name: 'Statistics', value: 'statistics' }, - { name: 'Content & Statistics', value: 'content+statistics' }, - ]; - - /** - * DSpaceObject that is being viewed - */ - dso: DSpaceObject; - /** * Initialize instance variables * @@ -68,12 +58,11 @@ export class SubscriptionMenuComponent extends ContextMenuEntryComponent impleme } /** - * Open modal - * - * @param content + * Open the modal to subscribe to the related DSpaceObject */ - public openSubscription(content: any) { - this.modalRef = this.modalService.open(content); + public openSubscription() { + this.modalRef = this.modalService.open(SubscriptionModalComponent); + this.modalRef.componentInstance.dso = this.contextMenuObject; } } diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts index f215643ebac..f6a2a47db8f 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts @@ -114,7 +114,12 @@ export class SubscriptionModalComponent implements OnInit { this.subscriptionForm.valueChanges.subscribe((newValue) => { let anyFrequencySelected = false; for (let f of this.frequencyDefaultValues) { - anyFrequencySelected = anyFrequencySelected || newValue.content.frequencies[f]; + for (let type of this.subscriptionDefaultTypes) { + anyFrequencySelected = anyFrequencySelected || newValue[type].frequencies[f]; + } + if (anyFrequencySelected) { + break; + } } this.isValid = anyFrequencySelected; }); From 9833025416e2043a1dd7be803aa76ae4477b0cca Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 22 Jun 2023 10:59:48 +0200 Subject: [PATCH 042/195] [DSC-1130] removed unsupported subscription type --- .../subscription-modal/subscription-modal.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts index f6a2a47db8f..29e287ef1d3 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts @@ -67,7 +67,7 @@ export class SubscriptionModalComponent implements OnInit { /** * Types of subscription to be shown on select */ - subscriptionDefaultTypes = ['content', 'statistics', 'content+statistics']; + subscriptionDefaultTypes = ['content', 'statistics']; /** * Frequencies to be shown as checkboxes From e109046265f9995cf875edcd20ca394954a7eeb9 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 22 Jun 2023 15:09:20 +0200 Subject: [PATCH 043/195] [DSC-1140] Fixes undefined on save --- src/app/submission/sections/form/section-form.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/submission/sections/form/section-form.component.ts b/src/app/submission/sections/form/section-form.component.ts index 2fc8c51bad0..3550a11c56b 100644 --- a/src/app/submission/sections/form/section-form.component.ts +++ b/src/app/submission/sections/form/section-form.component.ts @@ -404,7 +404,7 @@ export class SubmissionSectionFormComponent extends SectionModelComponent implem const metadata = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event); const value = this.formOperationsService.getFieldValueFromChangeEvent(event); - const eventAutoSave = !event.$event.hasOwnProperty('autoSave') || event.$event.autoSave; + const eventAutoSave = !event.$event?.hasOwnProperty('autoSave') || event.$event?.autoSave; if (eventAutoSave && (environment.submission.autosave.metadata.indexOf(metadata) !== -1 && isNotEmpty(value)) || this.hasRelatedCustomError(metadata)) { this.submissionService.dispatchSave(this.submissionId); } From d45ab682d7d5658d066d75d239bf440ef4fd9d1f Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 22 Jun 2023 15:09:40 +0200 Subject: [PATCH 044/195] [DSC-1140] Optional tooltip on dsAuthorityConfidenceState --- .../authority-confidence-state.directive.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/app/shared/form/directives/authority-confidence-state.directive.ts b/src/app/shared/form/directives/authority-confidence-state.directive.ts index b31bc7e2c63..d32f4c587d2 100644 --- a/src/app/shared/form/directives/authority-confidence-state.directive.ts +++ b/src/app/shared/form/directives/authority-confidence-state.directive.ts @@ -48,6 +48,11 @@ export class AuthorityConfidenceStateDirective implements OnChanges, AfterViewIn */ @Input() visibleWhenAuthorityEmpty = true; + /** + * A boolean representing if to show html icon if authority value is empty + */ + @Input() showTooltip = true; + /** * The css class applied before directive changes */ @@ -63,6 +68,11 @@ export class AuthorityConfidenceStateDirective implements OnChanges, AfterViewIn */ @Output() whenClickOnConfidenceNotAccepted: EventEmitter = new EventEmitter(); + /** + * Listener to hover event + */ + private onHoverUnsubscribe: () => void; + /** * Listener to click event */ @@ -91,7 +101,7 @@ export class AuthorityConfidenceStateDirective implements OnChanges, AfterViewIn /** * Listener to hover event */ - @HostListener('mouseover') onHover() { + onHover() { this.renderer.setAttribute( this.elem.nativeElement, 'title', @@ -116,6 +126,21 @@ export class AuthorityConfidenceStateDirective implements OnChanges, AfterViewIn this.renderer.removeClass(this.elem.nativeElement, this.previousClass); this.renderer.addClass(this.elem.nativeElement, this.newClass); } + + if (this.showTooltip && this.onHoverUnsubscribe == null) { + this.listenOnMouseOver(); + } + + if (!changes.showTooltip?.firstChange && !!changes.showTooltip?.currentValue) { + if (this.onHoverUnsubscribe != null) { + this.onHoverUnsubscribe(); + } + this.listenOnMouseOver(); + } + } + + private listenOnMouseOver() { + this.onHoverUnsubscribe = this.renderer.listen(this.elem.nativeElement, 'mouseover', () => this.onHover()); } /** From 02c7c5816f8a70dbb58891ed9b787ef17e8db221 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 22 Jun 2023 15:09:56 +0200 Subject: [PATCH 045/195] [DSC-1140] Fixes double tooltip visualization --- src/app/shared/form/chips/chips.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/form/chips/chips.component.html b/src/app/shared/form/chips/chips.component.html index 937a1af276b..20d782581e8 100644 --- a/src/app/shared/form/chips/chips.component.html +++ b/src/app/shared/form/chips/chips.component.html @@ -34,6 +34,7 @@ dsAuthorityConfidenceState [authorityValue]="c.item[icon.metadata] || c.item" [visibleWhenAuthorityEmpty]="icon.visibleWhenAuthorityEmpty" + [showTooltip]="false" aria-hidden="true" (dragstart)="t.close();" (mouseover)="showTooltip(t, i, icon.metadata)" From b2136c61738797deb6c18cbe5f6b6dbaa6fbfba1 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 22 Jun 2023 15:10:18 +0200 Subject: [PATCH 046/195] [DSC-1140] dsAuthorityConfidenceState onHover unsubscription --- .../authority-confidence-state.directive.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/shared/form/directives/authority-confidence-state.directive.ts b/src/app/shared/form/directives/authority-confidence-state.directive.ts index d32f4c587d2..0b663a85534 100644 --- a/src/app/shared/form/directives/authority-confidence-state.directive.ts +++ b/src/app/shared/form/directives/authority-confidence-state.directive.ts @@ -14,6 +14,7 @@ import { HostListener, Input, OnChanges, + OnDestroy, Output, Renderer2, SimpleChanges, @@ -36,7 +37,7 @@ import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/mod @Directive({ selector: '[dsAuthorityConfidenceState]' }) -export class AuthorityConfidenceStateDirective implements OnChanges, AfterViewInit { +export class AuthorityConfidenceStateDirective implements OnChanges, AfterViewInit, OnDestroy { /** * The metadata value @@ -187,12 +188,18 @@ export class AuthorityConfidenceStateDirective implements OnChanges, AfterViewIn const confidenceIcons: ConfidenceIconConfig[] = environment.submission.icons.authority.confidence; - const confidenceIndex: number = findIndex(confidenceIcons, {value: confidence}); + const confidenceIndex: number = findIndex(confidenceIcons, { value: confidence }); - const defaultConfidenceIndex: number = findIndex(confidenceIcons, {value: 'default' as any}); + const defaultConfidenceIndex: number = findIndex(confidenceIcons, { value: 'default' as any }); const defaultClass: string = (defaultConfidenceIndex !== -1) ? confidenceIcons[defaultConfidenceIndex].style : ''; return (confidenceIndex !== -1) ? confidenceIcons[confidenceIndex].style : defaultClass; } + public ngOnDestroy() { + if (this.onHoverUnsubscribe != null) { + this.onHoverUnsubscribe(); + } + } + } From 79fe9dc8ea381a003e1245f18be34f5a5dd97346 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 22 Jun 2023 15:15:00 +0200 Subject: [PATCH 047/195] [DSC-1140] Refactoring & Fix tooltip visibility on icons --- ...dynamic-relation-group-modal.components.ts | 7 +- .../shared/form/chips/chips.component.html | 2 +- .../shared/form/chips/chips.component.spec.ts | 47 +++++++++--- src/app/shared/form/chips/chips.component.ts | 71 ++++++++++++------- 4 files changed, 90 insertions(+), 37 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/modal/dynamic-relation-group-modal.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/modal/dynamic-relation-group-modal.components.ts index 806e0f7efd3..3e8d80b4781 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/modal/dynamic-relation-group-modal.components.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/modal/dynamic-relation-group-modal.components.ts @@ -288,7 +288,12 @@ export class DsDynamicRelationGroupModalComponent extends DynamicFormControlComp modelRow.group.forEach((control: DynamicInputModel) => { const controlValue: any = (control?.value as any)?.value || control?.value || PLACEHOLDER_PARENT_METADATA; const controlAuthority: any = (control?.value as any)?.authority || null; - item[control.name] = new FormFieldMetadataValueObject(controlValue, (control as any)?.language, (control as any)?.securityLevel, controlAuthority); + item[control.name] = + new FormFieldMetadataValueObject( + controlValue, (control as any)?.language, (control as any)?.securityLevel, controlAuthority, + null, 0, null, + (control?.value as any)?.otherInformation || null + ); }); }); return item; diff --git a/src/app/shared/form/chips/chips.component.html b/src/app/shared/form/chips/chips.component.html index 20d782581e8..4ad7522650f 100644 --- a/src/app/shared/form/chips/chips.component.html +++ b/src/app/shared/form/chips/chips.component.html @@ -2,7 +2,7 @@
-
+
- From 93d2787b4181117940b2920bcf27f2d789cefa64 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 30 Jun 2023 12:38:31 +0200 Subject: [PATCH 051/195] [CST-10304] Submission discarded correctly after a few minutes --- src/app/core/data/collection-data.service.ts | 23 +++++++++++-------- src/app/core/roles/role.service.ts | 19 ++++++++++----- .../my-dspace-configuration.service.ts | 8 +++---- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts index 0e04c27851b..97fc08acbba 100644 --- a/src/app/core/data/collection-data.service.ts +++ b/src/app/core/data/collection-data.service.ts @@ -18,7 +18,7 @@ import { COLLECTION } from '../shared/collection.resource-type'; import { ContentSource } from '../shared/content-source.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { Item } from '../shared/item.model'; -import { getFirstCompletedRemoteData } from '../shared/operators'; +import { getAllCompletedRemoteData, getFirstCompletedRemoteData } from '../shared/operators'; import { ComColDataService } from './comcol-data.service'; import { CommunityDataService } from './community-data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; @@ -77,7 +77,8 @@ export class CollectionDataService extends ComColDataService { }); return this.searchBy(searchHref, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe( - filter((collections: RemoteData>) => !collections.isResponsePending)); + getAllCompletedRemoteData(), + ); } /** @@ -98,7 +99,8 @@ export class CollectionDataService extends ComColDataService { }); return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe( - filter((collections: RemoteData>) => !collections.isResponsePending)); + getAllCompletedRemoteData(), + ); } /** @@ -124,7 +126,8 @@ export class CollectionDataService extends ComColDataService { }); return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe( - filter((collections: RemoteData>) => !collections.isResponsePending)); + getAllCompletedRemoteData(), + ); } /** @@ -154,7 +157,8 @@ export class CollectionDataService extends ComColDataService { }); return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe( - filter((collections: RemoteData>) => !collections.isResponsePending)); + getAllCompletedRemoteData() + ); } /** @@ -178,7 +182,8 @@ export class CollectionDataService extends ComColDataService { }); return this.searchBy(searchHref, options, reRequestOnStale).pipe( - filter((collections: RemoteData>) => !collections.isResponsePending)); + getAllCompletedRemoteData() + ); } /** * Get all collections the user is authorized to submit to, by community and has the metadata @@ -209,7 +214,8 @@ export class CollectionDataService extends ComColDataService { }); return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe( - filter((collections: RemoteData>) => !collections.isResponsePending)); + getAllCompletedRemoteData() + ); } /** @@ -224,8 +230,7 @@ export class CollectionDataService extends ComColDataService { options.elementsPerPage = 1; return this.searchBy(searchHref, options).pipe( - filter((collections: RemoteData>) => !collections.isResponsePending), - take(1), + getAllCompletedRemoteData(), map((collections: RemoteData>) => collections.payload.totalElements > 0) ); } diff --git a/src/app/core/roles/role.service.ts b/src/app/core/roles/role.service.ts index 7a4b6e6ccf9..67b4a54c6cb 100644 --- a/src/app/core/roles/role.service.ts +++ b/src/app/core/roles/role.service.ts @@ -1,10 +1,13 @@ import { Injectable } from '@angular/core'; import { Observable, of as observableOf } from 'rxjs'; -import { distinctUntilChanged } from 'rxjs/operators'; +import { switchMap } from 'rxjs/operators'; import { RoleType } from './role-types'; import { CollectionDataService } from '../data/collection-data.service'; +import { FeatureID } from '../data/feature-authorization/feature-id'; +import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; + /** * A service that provides methods to identify user role. @@ -17,15 +20,20 @@ export class RoleService { * * @param {CollectionDataService} collectionService */ - constructor(private collectionService: CollectionDataService) { + constructor( + private collectionService: CollectionDataService, + private authorizationService: AuthorizationDataService + ) { } /** * Check if current user is a submitter */ isSubmitter(): Observable { - return this.collectionService.hasAuthorizedCollection().pipe( - distinctUntilChanged() + // By applying switchMap, we address the cache problem typically associated with observables + // the switchMap operator cancels the previous inner observable and subscribes to the new one, effectively initiating a fresh request + return observableOf(true).pipe( + switchMap(() => this.collectionService.hasAuthorizedCollection()) ); } @@ -41,8 +49,7 @@ export class RoleService { * Check if current user is an admin */ isAdmin(): Observable { - // TODO find a way to check if user is an admin - return observableOf(false); + return this.authorizationService.isAuthorized(FeatureID.AdministratorOf); } /** diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.ts b/src/app/my-dspace-page/my-dspace-configuration.service.ts index f6a0ac9bf8e..5d86b5fdb70 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { combineLatest, Observable } from 'rxjs'; -import { first, map } from 'rxjs/operators'; +import { first, map, take } from 'rxjs/operators'; import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; import { RoleService } from '../core/roles/role.service'; @@ -103,9 +103,9 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { * Emits the available configuration list */ public getAvailableConfigurationTypes(): Observable { - return combineLatest(this.isSubmitter$, this.isController$, this.isAdmin$).pipe( - first(), - map(([isSubmitter, isController, isAdmin]: [boolean, boolean, boolean]) => { + return combineLatest([this.isSubmitter$, this.isController$, this.isAdmin$]).pipe( + take(1), + map(([isSubmitter, isController, isAdmin]: [boolean, boolean, boolean]) => { const availableConf: MyDSpaceConfigurationValueType[] = []; if (isSubmitter) { availableConf.push(MyDSpaceConfigurationValueType.Workspace); From 02755b3cec5efbdcabe58b0c8a53d1251044a10e Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 9 May 2023 17:27:06 +0200 Subject: [PATCH 052/195] [DSC-978][DURACOM-145] Handled collection step on a submission form --- src/app/shared/mocks/submission.mock.ts | 170 +++++++++++++++++- .../submission-form-collection.component.html | 2 +- ...bmission-form-collection.component.spec.ts | 6 + .../submission-form-collection.component.ts | 11 +- .../form/submission-form.component.html | 5 +- .../form/submission-form.component.spec.ts | 26 +++ .../form/submission-form.component.ts | 43 ++++- src/app/submission/sections/sections-type.ts | 1 + .../submission/sections/visibility-type.ts | 4 + 9 files changed, 257 insertions(+), 11 deletions(-) create mode 100644 src/app/submission/sections/visibility-type.ts diff --git a/src/app/shared/mocks/submission.mock.ts b/src/app/shared/mocks/submission.mock.ts index a7fe1ad6589..51d26abfb04 100644 --- a/src/app/shared/mocks/submission.mock.ts +++ b/src/app/shared/mocks/submission.mock.ts @@ -5,6 +5,7 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { SubmissionObjectState } from '../../submission/objects/submission-objects.reducer'; import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { SubmissionVisibilityValue } from '../../core/config/models/config-submission-section.model'; export const mockSectionsData = { traditionalpageone: { @@ -953,8 +954,8 @@ export const mockSubmissionDefinition: SubmissionDefinitionsModel = { mandatory: true, sectionType: 'utils', visibility: { - main: 'HIDDEN', - other: 'HIDDEN' + submission: SubmissionVisibilityValue.Hidden, + workflow: SubmissionVisibilityValue.Hidden }, type: 'submissionsection', _links: { @@ -966,8 +967,7 @@ export const mockSubmissionDefinition: SubmissionDefinitionsModel = { mandatory: true, sectionType: 'collection', visibility: { - main: 'HIDDEN', - other: 'HIDDEN' + workflow: SubmissionVisibilityValue.Hidden }, type: 'submissionsection', _links: { @@ -1029,6 +1029,168 @@ export const mockSubmissionDefinition: SubmissionDefinitionsModel = { }, } as any; +export const mockSubmissionDefinitionWithReadOnlyCollection: SubmissionDefinitionsModel = { + isDefault: true, + sections: buildPaginatedList(new PageInfo(), [ + { + mandatory: true, + sectionType: 'utils', + visibility: { + submission: SubmissionVisibilityValue.Hidden, + workflow: SubmissionVisibilityValue.Hidden + }, + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/extraction' }, + config: '' + }, + }, + { + mandatory: true, + sectionType: 'collection', + visibility: { + submission: SubmissionVisibilityValue.ReadOnly + }, + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/collection' }, + config: '' + }, + }, + { + header: 'submit.progressbar.describe.stepone', + mandatory: true, + sectionType: 'submission-form', + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/traditionalpageone' }, + config: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionforms/traditionalpageone' } + }, + }, + { + header: 'submit.progressbar.describe.steptwo', + mandatory: false, + sectionType: 'submission-form', + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/traditionalpagetwo' }, + config: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionforms/traditionalpagetwo' } + }, + }, + { + header: 'submit.progressbar.upload', + mandatory: true, + sectionType: 'upload', + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/upload' }, + config: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionuploads/upload' } + }, + }, + { + header: 'submit.progressbar.license', + mandatory: true, + sectionType: 'license', + visibility: { + main: null, + other: 'READONLY' + }, + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/license' }, + config: '' + }, + } + ]), + name: 'traditional', + type: 'submissiondefinition', + _links: { + collections: { href: 'https://rest.api/dspace-spring-rest/api/config/submissiondefinitions/traditional/collections' }, + sections: { href: 'https://rest.api/dspace-spring-rest/api/config/submissiondefinitions/traditional/sections' }, + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissiondefinitions/traditional' } + }, +} as any; +export const mockSubmissionDefinitionWithHiddenCollection: SubmissionDefinitionsModel = { + isDefault: true, + sections: buildPaginatedList(new PageInfo(), [ + { + mandatory: true, + sectionType: 'utils', + visibility: { + submission: SubmissionVisibilityValue.Hidden, + workflow: SubmissionVisibilityValue.Hidden + }, + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/extraction' }, + config: '' + }, + }, + { + mandatory: true, + sectionType: 'collection', + visibility: { + workflow: SubmissionVisibilityValue.Hidden + }, + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/collection' }, + config: '' + }, + }, + { + header: 'submit.progressbar.describe.stepone', + mandatory: true, + sectionType: 'submission-form', + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/traditionalpageone' }, + config: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionforms/traditionalpageone' } + }, + }, + { + header: 'submit.progressbar.describe.steptwo', + mandatory: false, + sectionType: 'submission-form', + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/traditionalpagetwo' }, + config: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionforms/traditionalpagetwo' } + }, + }, + { + header: 'submit.progressbar.upload', + mandatory: true, + sectionType: 'upload', + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/upload' }, + config: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionuploads/upload' } + }, + }, + { + header: 'submit.progressbar.license', + mandatory: true, + sectionType: 'license', + visibility: { + main: null, + other: 'READONLY' + }, + type: 'submissionsection', + _links: { + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissionsections/license' }, + config: '' + }, + } + ]), + name: 'traditional', + type: 'submissiondefinition', + _links: { + collections: { href: 'https://rest.api/dspace-spring-rest/api/config/submissiondefinitions/traditional/collections' }, + sections: { href: 'https://rest.api/dspace-spring-rest/api/config/submissiondefinitions/traditional/sections' }, + self: { href: 'https://rest.api/dspace-spring-rest/api/config/submissiondefinitions/traditional' } + }, +} as any; export const mockDeduplicationMatches = { '78ca1d06-cce7-4ee9-abda-46440d9b0bb7' : { diff --git a/src/app/submission/form/collection/submission-form-collection.component.html b/src/app/submission/form/collection/submission-form-collection.component.html index f8794ed931e..70cb3e1e74b 100644 --- a/src/app/submission/form/collection/submission-form-collection.component.html +++ b/src/app/submission/form/collection/submission-form-collection.component.html @@ -26,7 +26,7 @@ [ngClass]="{'no-caret': !hasChoice}" (blur)="onClose()" (click)="onClose()" - [disabled]="(processingChange$ | async) || !hasChoice" + [disabled]="(processingChange$ | async) || !hasChoice || isReadonly" ngbDropdownToggle> {{ selectedCollectionName$ | async }} diff --git a/src/app/submission/form/collection/submission-form-collection.component.spec.ts b/src/app/submission/form/collection/submission-form-collection.component.spec.ts index 1b606a6aa8d..94c1ae1800b 100644 --- a/src/app/submission/form/collection/submission-form-collection.component.spec.ts +++ b/src/app/submission/form/collection/submission-form-collection.component.spec.ts @@ -249,6 +249,12 @@ describe('SubmissionFormCollectionComponent Component', () => { expect(dropDown).toBeFalsy(); }); + it('the dropdown button should be disabled when isReadonly is true', () => { + comp.isReadonly = true; + fixture.detectChanges(); + expect(dropdowBtn.nativeNode.attributes.disabled).toBeDefined(); + }); + it('should be simulated when the drop-down menu is closed', () => { spyOn(comp, 'onClose'); comp.onClose(); diff --git a/src/app/submission/form/collection/submission-form-collection.component.ts b/src/app/submission/form/collection/submission-form-collection.component.ts index 9b6f549c716..104a854c61b 100644 --- a/src/app/submission/form/collection/submission-form-collection.component.ts +++ b/src/app/submission/form/collection/submission-form-collection.component.ts @@ -52,6 +52,12 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit { */ @Input() currentDefinition: string; + /** + * The entity type input used to create a new submission + * @type {string} + */ + @Input() entityType: string; + /** * The submission id * @type {string} @@ -59,10 +65,9 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit { @Input() submissionId; /** - * The entity type input used to create a new submission - * @type {string} + * Flag to indicate if the submission dropdown is read only */ - @Input() entityType: string; + @Input() isReadonly = false; /** * An event fired when a different collection is selected. diff --git a/src/app/submission/form/submission-form.component.html b/src/app/submission/form/submission-form.component.html index 583df0eab2b..addc41647dc 100644 --- a/src/app/submission/form/submission-form.component.html +++ b/src/app/submission/form/submission-form.component.html @@ -8,12 +8,15 @@
+ +
{ @@ -173,6 +176,29 @@ describe('SubmissionFormComponent Component', () => { done(); }); + it('should return the visibility object of the collection section', () => { + comp.submissionDefinition = submissionDefinition; + fixture.detectChanges(); + const result = compAsAny.getCollectionVisibility(); + expect(result).toEqual({ + workflow: SubmissionVisibilityValue.Hidden + }); + }); + + it('should return true if collection section visibility is hidden', () => { + submissionServiceStub.getSubmissionScope.and.returnValue(SubmissionScopeType.WorkflowItem); + comp.submissionDefinition = mockSubmissionDefinitionWithHiddenCollection; + fixture.detectChanges(); + expect(comp.isSectionHidden).toBe(true); + }); + + it('should return false for isSectionReadonly when collection section visibility is not READONLY', () => { + submissionServiceStub.getSubmissionScope.and.returnValue(SubmissionScopeType.WorkspaceItem); + comp.submissionDefinition = submissionDefinition; + fixture.detectChanges(); + expect(comp.isSectionReadonly).toBe(false); + }); + it('should update properly on collection change', (done) => { comp.collectionId = collectionId; comp.submissionId = submissionId; diff --git a/src/app/submission/form/submission-form.component.ts b/src/app/submission/form/submission-form.component.ts index e4d8bde09e5..8fb6d3eef1f 100644 --- a/src/app/submission/form/submission-form.component.ts +++ b/src/app/submission/form/submission-form.component.ts @@ -1,13 +1,16 @@ import { ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; + import { combineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; +import isEqual from 'lodash/isEqual'; + import { AuthService } from '../../core/auth/auth.service'; import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model'; import { Collection } from '../../core/shared/collection.model'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { SubmissionObject } from '../../core/submission/models/submission-object.model'; import { WorkspaceitemSectionsObject } from '../../core/submission/models/workspaceitem-sections.model'; -import { hasValue, isNotEmpty } from '../../shared/empty.util'; +import { hasValue, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { UploaderOptions } from '../../shared/upload/uploader/uploader-options.model'; import { SubmissionObjectEntry } from '../objects/submission-objects.reducer'; import { SectionDataObject } from '../sections/models/section-data.model'; @@ -16,6 +19,11 @@ import { Item } from '../../core/shared/item.model'; import { SectionsType } from '../sections/sections-type'; import { SectionsService } from '../sections/sections.service'; import { SubmissionError } from '../objects/submission-error.model'; +import { + SubmissionSectionModel, + SubmissionVisibilityType +} from '../../core/config/models/config-submission-section.model'; +import { SubmissionVisibility } from '../utils/visibility.util'; import { MetadataSecurityConfiguration } from '../../core/submission/models/metadata-security-configuration'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; import { MetadataSecurityConfigurationService } from '../../core/submission/metadatasecurityconfig-data.service'; @@ -35,6 +43,7 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { * @type {string} */ @Input() collectionId: string; + @Input() item: Item; /** @@ -202,6 +211,34 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { } } + /** + * Returns the visibility object of the collection section + */ + private getCollectionVisibility(): SubmissionVisibilityType { + const submissionSectionModel: SubmissionSectionModel = + this.submissionDefinition.sections.page.find( + (section) => isEqual(section.sectionType, SectionsType.Collection) + ); + + return isNotUndefined(submissionSectionModel.visibility) ? submissionSectionModel.visibility : null; + } + + /** + * Getter to see if the collection section visibility is hidden + */ + get isSectionHidden(): boolean { + const visibility = this.getCollectionVisibility(); + return SubmissionVisibility.isHidden(visibility, this.submissionService.getSubmissionScope()); + } + + /** + * Getter to see if the collection section visibility is readonly + */ + get isSectionReadonly(): boolean { + const visibility = this.getCollectionVisibility(); + return SubmissionVisibility.isReadOnly(visibility, this.submissionService.getSubmissionScope()); + } + /** * Unsubscribe from all subscriptions, destroy instance variables * and reset submission state @@ -264,7 +301,9 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { protected getSectionsList(): Observable { return this.submissionService.getSubmissionSections(this.submissionId).pipe( filter((sections: SectionDataObject[]) => isNotEmpty(sections)), - map((sections: SectionDataObject[]) => sections)); + map((sections: SectionDataObject[]) => + sections.filter((section: SectionDataObject) => !isEqual(section.sectionType,SectionsType.Collection))), + ); } } diff --git a/src/app/submission/sections/sections-type.ts b/src/app/submission/sections/sections-type.ts index cd19489593a..8e52540cd7d 100644 --- a/src/app/submission/sections/sections-type.ts +++ b/src/app/submission/sections/sections-type.ts @@ -9,6 +9,7 @@ export enum SectionsType { AccessesCondition = 'accessCondition', SherpaPolicies = 'sherpaPolicy', Identifiers = 'identifiers', + Collection = 'collection', DetectDuplicate = 'detect-duplicate', Correction = 'correction' } diff --git a/src/app/submission/sections/visibility-type.ts b/src/app/submission/sections/visibility-type.ts new file mode 100644 index 00000000000..b2e167285c8 --- /dev/null +++ b/src/app/submission/sections/visibility-type.ts @@ -0,0 +1,4 @@ +export enum VisibilityType { + HIDDEN = 'HIDDEN', + READONLY = 'READONLY', +} From 5a5755f61adb330361074474d7464398dfea54e5 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 12 May 2023 15:38:58 +0200 Subject: [PATCH 053/195] [DURACOM-145] Fixed e2e failing tests --- cypress/integration/my-dspace.spec.ts | 13 +++++++++++-- cypress/integration/submission.spec.ts | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/cypress/integration/my-dspace.spec.ts b/cypress/integration/my-dspace.spec.ts index a214a865c93..355cbda51b4 100644 --- a/cypress/integration/my-dspace.spec.ts +++ b/cypress/integration/my-dspace.spec.ts @@ -88,8 +88,17 @@ xdescribe('My DSpace page', () => { // The Submission edit form tag should be visible cy.get('ds-submission-edit').should('be.visible'); - // A Collection menu button should exist & its value should be the selected collection - cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME); + // A Collection menu button should exist(based on collection visibility) & its value should be the selected collection + cy.get('#collectionControlsMenuButton span') + .should(($span) => { + if ($span.length > 0) { + // If the element exists in the DOM (it's visible or read-only) + expect($span).to.have.text(TEST_SUBMIT_COLLECTION_NAME); + } else { + // If the element doesn't exist in the DOM, the length will be 0 + expect($span).to.have.length(0); + } + }); // Now that we've created a submission, we'll test that we can go back and Edit it. // Get our Submission URL, to parse out the ID of this new submission diff --git a/cypress/integration/submission.spec.ts b/cypress/integration/submission.spec.ts index 7c9af9c3074..215c7af600e 100644 --- a/cypress/integration/submission.spec.ts +++ b/cypress/integration/submission.spec.ts @@ -18,8 +18,17 @@ xdescribe('New Submission page', () => { // The Submission edit form tag should be visible cy.get('ds-submission-edit').should('be.visible'); - // A Collection menu button should exist & it's value should be the selected collection - cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME); + // A Collection menu button should exist(if it's visible or read-only) & it's value should be the selected collection + cy.get('#collectionControlsMenuButton span') + .should(($span) => { + if ($span.length > 0) { + // If the element exists in the DOM (it's visible or read-only) + expect($span).to.have.text(TEST_SUBMIT_COLLECTION_NAME); + } else { + // If the element doesn't exist in the DOM, the length will be 0 + expect($span).to.have.length(0); + } + }); // 4 sections should be visible by default cy.get('div#section_traditionalpageone').should('be.visible'); From 6baa8b9a85d917a266fdd612292ec4fb80bc21ad Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 15 May 2023 11:12:50 +0200 Subject: [PATCH 054/195] Revert "[DURACOM-145] Fixed e2e failing tests" This reverts commit b9c7391b1452e9a79d9c5615595b08cd7ac8b14c. --- cypress/integration/my-dspace.spec.ts | 13 ++----------- cypress/integration/submission.spec.ts | 13 ++----------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/cypress/integration/my-dspace.spec.ts b/cypress/integration/my-dspace.spec.ts index 355cbda51b4..a214a865c93 100644 --- a/cypress/integration/my-dspace.spec.ts +++ b/cypress/integration/my-dspace.spec.ts @@ -88,17 +88,8 @@ xdescribe('My DSpace page', () => { // The Submission edit form tag should be visible cy.get('ds-submission-edit').should('be.visible'); - // A Collection menu button should exist(based on collection visibility) & its value should be the selected collection - cy.get('#collectionControlsMenuButton span') - .should(($span) => { - if ($span.length > 0) { - // If the element exists in the DOM (it's visible or read-only) - expect($span).to.have.text(TEST_SUBMIT_COLLECTION_NAME); - } else { - // If the element doesn't exist in the DOM, the length will be 0 - expect($span).to.have.length(0); - } - }); + // A Collection menu button should exist & its value should be the selected collection + cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME); // Now that we've created a submission, we'll test that we can go back and Edit it. // Get our Submission URL, to parse out the ID of this new submission diff --git a/cypress/integration/submission.spec.ts b/cypress/integration/submission.spec.ts index 215c7af600e..7c9af9c3074 100644 --- a/cypress/integration/submission.spec.ts +++ b/cypress/integration/submission.spec.ts @@ -18,17 +18,8 @@ xdescribe('New Submission page', () => { // The Submission edit form tag should be visible cy.get('ds-submission-edit').should('be.visible'); - // A Collection menu button should exist(if it's visible or read-only) & it's value should be the selected collection - cy.get('#collectionControlsMenuButton span') - .should(($span) => { - if ($span.length > 0) { - // If the element exists in the DOM (it's visible or read-only) - expect($span).to.have.text(TEST_SUBMIT_COLLECTION_NAME); - } else { - // If the element doesn't exist in the DOM, the length will be 0 - expect($span).to.have.length(0); - } - }); + // A Collection menu button should exist & it's value should be the selected collection + cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME); // 4 sections should be visible by default cy.get('div#section_traditionalpageone').should('be.visible'); From 8ace8c4deb970202795db831d52bab17ff62879b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 30 Jun 2023 17:14:42 +0200 Subject: [PATCH 055/195] [DSC-978] Fix test --- src/app/shared/mocks/submission.mock.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/shared/mocks/submission.mock.ts b/src/app/shared/mocks/submission.mock.ts index 51d26abfb04..79df319f9ee 100644 --- a/src/app/shared/mocks/submission.mock.ts +++ b/src/app/shared/mocks/submission.mock.ts @@ -871,8 +871,8 @@ export const mockSubmissionDefinitionResponse = { mandatory: true, sectionType: 'utils', visibility: { - main: 'HIDDEN', - other: 'HIDDEN' + submission: SubmissionVisibilityValue.Hidden, + workflow: SubmissionVisibilityValue.Hidden }, type: 'submissionsection', _links: { @@ -884,8 +884,7 @@ export const mockSubmissionDefinitionResponse = { mandatory: true, sectionType: 'collection', visibility: { - main: 'HIDDEN', - other: 'HIDDEN' + workflow: SubmissionVisibilityValue.Hidden }, type: 'submissionsection', _links: { From b97e5032f4a509c52bbd19f2c51e18e357e8225c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 3 Jul 2023 15:00:01 +0200 Subject: [PATCH 056/195] [DSC-978] Fix test --- src/app/core/auth/auth.effects.spec.ts | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/app/core/auth/auth.effects.spec.ts b/src/app/core/auth/auth.effects.spec.ts index 20f4f5d8648..7b0b8dd5d68 100644 --- a/src/app/core/auth/auth.effects.spec.ts +++ b/src/app/core/auth/auth.effects.spec.ts @@ -1,10 +1,13 @@ import { fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { Router } from '@angular/router'; import { provideMockActions } from '@ngrx/effects/testing'; import { Store, StoreModule } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { cold, hot } from 'jasmine-marbles'; +import { cold, getTestScheduler, hot } from 'jasmine-marbles'; import { Observable, of as observableOf, throwError as observableThrow } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { TestScheduler } from 'rxjs/testing'; import { AuthEffects } from './auth.effects'; import { @@ -38,9 +41,8 @@ import { AppState, storeModuleConfig } from '../../app.reducer'; import { StoreActionTypes } from '../../store.actions'; import { isAuthenticated, isAuthenticatedLoaded } from './selectors'; import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; -import { Router } from '@angular/router'; import { RouterStub } from '../../shared/testing/router.stub'; -import { take } from 'rxjs/operators'; + describe('AuthEffects', () => { let authEffects: AuthEffects; @@ -52,6 +54,7 @@ describe('AuthEffects', () => { let routerStub; let redirectUrl; let authStatus; + let scheduler: TestScheduler; const authorizationService = jasmine.createSpyObj(['invalidateAuthorizationsRequestCache']); @@ -484,18 +487,22 @@ describe('AuthEffects', () => { }); describe('refreshTokenAndRedirectSuccess$', () => { - it('should replace token and redirect in response to a REFRESH_TOKEN_AND_REDIRECT_SUCCESS action', (done) => { + + beforeEach(() => { + scheduler = getTestScheduler(); + }); + + it('should replace token and redirect in response to a REFRESH_TOKEN_AND_REDIRECT_SUCCESS action', () => { actions = hot('--a-', { a: { type: AuthActionTypes.REFRESH_TOKEN_AND_REDIRECT_SUCCESS, payload: {token, redirectUrl} } }); spyOn(authServiceStub, 'replaceToken'); - spyOn(routerStub, 'navigateByUrl'); - authEffects.refreshTokenAndRedirectSuccess$.pipe(take(1)).subscribe(() => { - expect(authServiceStub.replaceToken).toHaveBeenCalledWith(token); - expect(routerStub.navigateByUrl).toHaveBeenCalledWith(redirectUrl); - }); - done(); + scheduler.run(() => authEffects.refreshTokenAndRedirectSuccess$.pipe(take(1)).subscribe()); + scheduler.flush(); + + expect(authServiceStub.replaceToken).toHaveBeenCalledWith(token); + expect(routerStub.navigate).toHaveBeenCalledWith([redirectUrl]); }); }); From 1931fb31a4e87f33659e4bab89a2525a51ced5d6 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 4 Jul 2023 12:03:59 +0200 Subject: [PATCH 057/195] [DSC-1153] Advanced attachment responsivity --- .../bitstream-attachment.component.html | 35 +++++++++++-------- .../bitstream-attachment.component.scss | 10 +++--- .../bitstream-attachment.component.ts | 3 ++ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html index 674478e844b..761a978d24f 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html @@ -1,10 +1,25 @@ -
-
+
+ +
-
+
+
+ +
+ +
+
+ +
+ +
+
+
+ +
-

+

{{attachment.firstMetadataValue(attachmentConf.name)}}

@@ -39,16 +54,6 @@
-
- -
- -
-
- -
- -
-
+
diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.scss b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.scss index 12fd88cd7ee..e2c8b57a933 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.scss +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.scss @@ -1,9 +1,3 @@ -.img-file { - max-height: var(--ds-advanced-attachment-image-max-height); - object-fit: var(--ds-advanced-attachment-image-object-fit); - object-position: var(--ds-advanced-attachment-image-object-position); -} - .thumbnail-placeholder { border: var(--ds-thumbnail-placeholder-border); color: var(--ds-thumbnail-placeholder-color); @@ -29,3 +23,7 @@ i.custom-icon { text-align: center; vertical-align: middle; } + +.thumbnail-wrapper { + min-width: 120px; +} diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts index 5cfb3ddd006..4578a1a5d0e 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts @@ -8,6 +8,7 @@ import { Item } from '../../../../../../../../core/shared/item.model'; import { BitstreamDataService } from '../../../../../../../../core/data/bitstream-data.service'; import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute, Router } from '@angular/router'; +import { AttachmentRenderingType } from './attachment-type.decorator'; @Component({ selector: 'ds-bitstream-attachment', @@ -26,6 +27,8 @@ export class BitstreamAttachmentComponent extends BitstreamRenderingModelCompone */ AdvancedAttachmentElementType = AdvancedAttachmentElementType; + AttachmentRenderingType = AttachmentRenderingType; + /** * All item providers to show buttons of */ From e2015675960d8f792f5f0376c0b03523c9da7fab Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 4 Jul 2023 16:28:57 +0200 Subject: [PATCH 058/195] [DSC-1153] Advanced attachment responsivity (fixes) --- .../bitstream-attachment.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html index 761a978d24f..6e75277bf02 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html @@ -1,11 +1,11 @@ -
+
-
+
-
+
@@ -19,7 +19,7 @@
-
+
Date: Tue, 4 Jul 2023 17:00:10 +0200 Subject: [PATCH 059/195] [DSC-1153] Advanced attachment responsivity (fixes) --- .../bitstream-attachment/bitstream-attachment.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html index 6e75277bf02..038da280b9a 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html @@ -1,5 +1,5 @@ -
+
From 5f4e2878a17fc8ed5aa000e85c88f49c9e7f914f Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 5 Jul 2023 17:35:06 +0200 Subject: [PATCH 060/195] [DSC-1152] Fix search form and import/export button responsiveness in MyDSpace --- .../my-dspace-new-bulk-import.component.html | 4 ++-- .../search-form/search-form.component.html | 18 ++++++++---------- .../search-form/search-form.component.scss | 4 ++++ .../item-export-modal-launcher.component.html | 2 +- .../item-export-modal-launcher.component.scss | 3 +++ .../item-export-modal-launcher.component.ts | 1 + src/app/shared/search/search.component.html | 4 ++-- src/app/shared/search/search.component.scss | 4 ++++ 8 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 src/app/shared/search/item-export/item-export-modal-launcher/item-export-modal-launcher.component.scss diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-bulk-import/my-dspace-new-bulk-import.component.html b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-bulk-import/my-dspace-new-bulk-import.component.html index a2888e96280..08bd9cb0359 100644 --- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-bulk-import/my-dspace-new-bulk-import.component.html +++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-bulk-import/my-dspace-new-bulk-import.component.html @@ -1,5 +1,5 @@
- -
- - - - +
+
+
+ + + +
diff --git a/src/app/shared/search-form/search-form.component.scss b/src/app/shared/search-form/search-form.component.scss index cf3a354364f..6e73b4b960f 100644 --- a/src/app/shared/search-form/search-form.component.scss +++ b/src/app/shared/search-form/search-form.component.scss @@ -7,3 +7,7 @@ .scope-button { max-width: var(--ds-search-form-scope-max-width); } + +.form-group { // add margin to parent components + margin-bottom: 0; +} diff --git a/src/app/shared/search/item-export/item-export-modal-launcher/item-export-modal-launcher.component.html b/src/app/shared/search/item-export/item-export-modal-launcher/item-export-modal-launcher.component.html index 3d43a7d6c15..66c44b0fbd1 100644 --- a/src/app/shared/search/item-export/item-export-modal-launcher/item-export-modal-launcher.component.html +++ b/src/app/shared/search/item-export/item-export-modal-launcher/item-export-modal-launcher.component.html @@ -3,7 +3,7 @@
- + (changeSecurityLevel)="addSecurityLevelToMetadata($event)"> + +
+
+ +
+
+ + + + + + + +
+
+
- - - - - - - -
-
- +
diff --git a/src/app/shared/form/form.component.html b/src/app/shared/form/form.component.html index b9ea9895304..29351a7fd81 100644 --- a/src/app/shared/form/form.component.html +++ b/src/app/shared/form/form.component.html @@ -11,20 +11,36 @@ (dfChange)="onChange($event)" (dfFocus)="onFocus($event)" (ngbEvent)="onCustomEvent($event)"> + + +
+ +
+
+
- -
+ +
diff --git a/src/app/shared/form/form.component.spec.ts b/src/app/shared/form/form.component.spec.ts index 2f3be3fded3..be29f7d31f0 100644 --- a/src/app/shared/form/form.component.spec.ts +++ b/src/app/shared/form/form.component.spec.ts @@ -4,9 +4,11 @@ import { CommonModule } from '@angular/common'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { + DynamicFormArrayGroupModel, DynamicFormArrayModel, DynamicFormControlEvent, DynamicFormControlModel, + DynamicFormGroupModel, DynamicFormValidationService, DynamicInputModel } from '@ng-dynamic-forms/core'; @@ -24,6 +26,11 @@ import { FormFieldMetadataValueObject } from './builder/models/form-field-metada import { createTestComponent } from '../testing/utils.test'; import { BehaviorSubject } from 'rxjs'; import { storeModuleConfig } from '../../app.reducer'; +import { + DynamicScrollableDropdownModel, + DynamicScrollableDropdownModelConfig +} from './builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; +import { DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core/lib/model/form-group/dynamic-form-group.model'; let TEST_FORM_MODEL; @@ -433,6 +440,167 @@ describe('FormComponent test suite', () => { expect(formComp.removeArrayItem.emit).toHaveBeenCalled(); })); + + it('should emit removeArrayItem Event when an scrollable dropdown field has been cleaned', inject([FormBuilderService], (service: FormBuilderService) => { + spyOn(formComp.removeArrayItem, 'emit'); + + formComp.clearScrollableDropdown(new Event('click'), formComp.formModel[0] as DynamicFormControlModel); + + expect(formComp.removeArrayItem.emit).toHaveBeenCalled(); + })); + }); + + describe('isArrayGroupEmpty', () => { + init(); + beforeEach(() => { + formFixture = TestBed.createComponent(FormComponent); + store = TestBed.inject(Store as any); + formComp = formFixture.componentInstance; + formComp.formId = 'testFormArray'; + formComp.formModel = TEST_FORM_MODEL_WITH_ARRAY; + formComp.displaySubmit = false; + formComp.displayCancel = false; + formFixture.detectChanges(); + spyOn(store, 'dispatch'); + }); + + afterEach(() => { + formFixture.destroy(); + formComp = null; + }); + + it('should return false if array group has multiple values', () => { + const group = { + context: { + groups: [ + { + group: [ + { + id: 'groupId', + value: 'groupValue1' + } + ], + }, + { + group: [ + { + id: 'groupId', + value: 'groupValue2' + } + ], + } + ] + } + }; + + const result = formComp.isArrayGroupEmpty(group); + + expect(result).toBeFalse(); + }); + + it('should return false if array group has only one value', () => { + const group = { + context: { + groups: [ + { + group: [ + { + id: 'groupId', + value: 'groupValue1' + } + ], + } + ] + } + }; + + const result = formComp.isArrayGroupEmpty(group); + + expect(result).toBeFalse(); + }); + + it('should return true if array group has one group but without value', () => { + const group = { + context: { + groups: [ + { + group: [ + { + id: 'groupId', + value: null + } + ], + } + ] + } + }; + + const result = formComp.isArrayGroupEmpty(group); + + expect(result).toBeTrue(); + }); + + it('should return true if array group does not have any value', () => { + const group = { + context: { + groups: [] + } + }; + + const result = formComp.isArrayGroupEmpty(group); + + expect(result).toBeTrue(); + }); + }); + + describe('isTheOnlyFieldInArrayGroup', () => { + init(); + beforeEach(() => { + formFixture = TestBed.createComponent(FormComponent); + store = TestBed.inject(Store as any); + formComp = formFixture.componentInstance; + formComp.formId = 'testFormArray'; + formComp.formModel = TEST_FORM_MODEL_WITH_ARRAY; + formComp.displaySubmit = false; + formComp.displayCancel = false; + formFixture.detectChanges(); + spyOn(store, 'dispatch'); + }); + + afterEach(() => { + formFixture.destroy(); + formComp = null; + }); + + it('should return true if it is the only field in array group', () => { + const parent = new DynamicFormArrayGroupModel({} as DynamicFormArrayModel, [{} as DynamicFormControlModel]); + const model = new DynamicScrollableDropdownModel({} as DynamicScrollableDropdownModelConfig); + model.parent = parent; + + const result = formComp.isTheOnlyFieldInArrayGroup(model); + + expect(result).toBeTrue(); + }); + + it('should return false if it is not the only field in array group', () => { + const parent = new DynamicFormArrayGroupModel({} as DynamicFormArrayModel, [{} as DynamicFormControlModel, {} as DynamicFormControlModel]); + const model = new DynamicScrollableDropdownModel({} as DynamicScrollableDropdownModelConfig); + model.parent = parent; + + const result = formComp.isTheOnlyFieldInArrayGroup(model); + + expect(result).toBeFalse(); + }); + + it('should return false if it is a field in not array group', () => { + const parent = new DynamicFormGroupModel({} as DynamicFormGroupModelConfig); + const model = new DynamicScrollableDropdownModel({} as DynamicScrollableDropdownModelConfig); + model.parent = parent; + + const result = formComp.isTheOnlyFieldInArrayGroup(model); + + expect(result).toBeFalse(); + }); }); }); diff --git a/src/app/shared/form/form.component.ts b/src/app/shared/form/form.component.ts index 1d06d2cf7d7..05f88c095eb 100644 --- a/src/app/shared/form/form.component.ts +++ b/src/app/shared/form/form.component.ts @@ -4,6 +4,7 @@ import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/for import { Observable, Subscription } from 'rxjs'; import { + DynamicFormArrayGroupModel, DynamicFormArrayModel, DynamicFormControlEvent, DynamicFormControlModel, @@ -18,6 +19,10 @@ import { hasValue, isNotEmpty, isNotNull, isNull } from '../empty.util'; import { FormService } from './form.service'; import { FormEntry, FormError } from './form.reducer'; import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model'; +import cloneDeep from 'lodash/cloneDeep'; +import { + DynamicScrollableDropdownModel +} from './builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; /** * The default form component. @@ -297,7 +302,7 @@ export class FormComponent implements OnDestroy, OnInit { if (this.emitChange) { this.change.emit(event); } -} + } /** * Method called on submit. @@ -332,7 +337,14 @@ export class FormComponent implements OnDestroy, OnInit { // In case of qualdrop value or inline-group remove event must be dispatched before removing the control from array this.removeArrayItem.emit(event); } - this.formBuilderService.removeFormArrayGroup(index, formArrayControl, arrayContext); + if (index === 0 && formArrayControl.value?.length === 1) { + event.model = cloneDeep(event.model); + const fieldId = event.model.id; + formArrayControl.at(0).get(fieldId).setValue(null); + } else { + this.formBuilderService.removeFormArrayGroup(index, formArrayControl, arrayContext); + } + this.formService.changeForm(this.formId, this.formModel); if (!this.formBuilderService.isQualdropGroup(event.model as DynamicFormControlModel) && !this.isInlineGroupForm) { // dispatch remove event for any field type except for qualdrop value and inline-group @@ -340,6 +352,14 @@ export class FormComponent implements OnDestroy, OnInit { } } + clearScrollableDropdown($event, model: DynamicFormControlModel): void { + const control = this.formGroup.get(this.formBuilderService.getPath(model)) as FormControl; + const event = { $event, type: 'remove', model: cloneDeep(model), context: null, control, group: control.parent } as DynamicFormControlEvent; + control.setValue(null); + this.formService.changeForm(this.formId, this.formModel); + this.removeArrayItem.emit(event); + } + insertItem($event, arrayContext: DynamicFormArrayModel, index: number): void { const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray; this.formBuilderService.insertFormArrayGroup(index, formArrayControl, arrayContext); @@ -362,6 +382,14 @@ export class FormComponent implements OnDestroy, OnInit { return isNotEmpty(value) && value.isVirtual; } + isArrayGroupEmpty(group): boolean { + return group.context.groups?.length <= 1 && !group.context.groups?.[0]?.group?.[0]?.value; + } + + isTheOnlyFieldInArrayGroup(model: DynamicScrollableDropdownModel) { + return model.parent instanceof DynamicFormArrayGroupModel && model.parent?.group?.length === 1; + } + protected getEvent($event: any, arrayContext: DynamicFormArrayModel, index: number, type: string, formGroup?: FormGroup): DynamicFormControlEvent { const context = arrayContext.groups[index]; const itemGroupModel = context.context; diff --git a/src/app/submission/sections/form/section-form-operations.service.ts b/src/app/submission/sections/form/section-form-operations.service.ts index 03310df8ca2..fedc5a26dfb 100644 --- a/src/app/submission/sections/form/section-form-operations.service.ts +++ b/src/app/submission/sections/form/section-form-operations.service.ts @@ -322,7 +322,7 @@ export class SectionFormOperationsService { } else if (event.context && event.context instanceof DynamicFormArrayGroupModel) { // Model is a DynamicRowArrayModel this.handleArrayGroupPatch(pathCombiner, event, (event as any).context.context, previousValue); - } else if ((isNotEmpty(value) && typeof value === 'string') || (isNotEmpty(value) && value instanceof FormFieldMetadataValueObject && value.hasValue())) { + } else if ((isNotEmpty(value) && typeof value === 'string') || (isNotEmpty(value) && (value instanceof FormFieldMetadataValueObject || value instanceof VocabularyEntry) && value.hasValue())) { this.operationsBuilder.remove(pathCombiner.getPath(path)); } } diff --git a/src/app/submission/sections/form/section-form.component.spec.ts b/src/app/submission/sections/form/section-form.component.spec.ts index fb40e71e94d..d1e4a9fc715 100644 --- a/src/app/submission/sections/form/section-form.component.spec.ts +++ b/src/app/submission/sections/form/section-form.component.spec.ts @@ -585,11 +585,14 @@ describe('SubmissionSectionFormComponent test suite', () => { it('should call dispatchOperationsFromEvent on form remove event', () => { spyOn(comp, 'hasStoredValue').and.returnValue(false); + formBuilderService.hasMappedGroupValue.and.returnValue(false); + formOperationsService.getFieldValueFromChangeEvent.and.returnValue('test'); comp.onRemove(dynamicFormControlEvent); expect(formOperationsService.dispatchOperationsFromEvent).toHaveBeenCalled(); - + expect(compAsAny.previousValue.path).toBeNull(); + expect(compAsAny.previousValue.value).toBeNull(); }); it('should check if has stored value in the section state', () => { diff --git a/src/app/submission/sections/form/section-form.component.ts b/src/app/submission/sections/form/section-form.component.ts index 3550a11c56b..39329e61b4c 100644 --- a/src/app/submission/sections/form/section-form.component.ts +++ b/src/app/submission/sections/form/section-form.component.ts @@ -429,6 +429,10 @@ export class SubmissionSectionFormComponent extends SectionModelComponent implem * the [[DynamicFormControlEvent]] emitted */ onFocus(event: DynamicFormControlEvent): void { + this.updatePreviousValue(event); + } + + private updatePreviousValue(event: DynamicFormControlEvent): void { const value = this.formOperationsService.getFieldValueFromChangeEvent(event); const path = this.formBuilderService.getPath(event.model); if (this.formBuilderService.hasMappedGroupValue(event.model)) { @@ -440,6 +444,11 @@ export class SubmissionSectionFormComponent extends SectionModelComponent implem } } + private clearPreviousValue(): void { + this.previousValue.path = null; + this.previousValue.value = null; + } + /** * Method called when a form remove event is fired. * Dispatch form operations based on changes. @@ -448,6 +457,7 @@ export class SubmissionSectionFormComponent extends SectionModelComponent implem * the [[DynamicFormControlEvent]] emitted */ onRemove(event: DynamicFormControlEvent): void { + this.updatePreviousValue(event); const fieldId = this.formBuilderService.getId(event.model); const fieldIndex = this.formOperationsService.getArrayIndexFromEvent(event); @@ -465,7 +475,7 @@ export class SubmissionSectionFormComponent extends SectionModelComponent implem event, this.previousValue, this.hasStoredValue(fieldId, fieldIndex)); - + this.clearPreviousValue(); } /** From c99fb5f544a7a6abce92e406e4562ffd25bf7e6d Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 26 Jul 2023 15:14:54 +0200 Subject: [PATCH 067/195] refactoring of advanced attachment rendering component's location --- .../attachment-render/attachment-rendering.module.ts | 8 ++++---- .../file-download-button.component.html | 0 .../file-download-button.component.scss | 0 .../file-download-button.component.spec.ts | 4 ++-- .../file-download-button.component.ts | 7 +++---- 5 files changed, 9 insertions(+), 10 deletions(-) rename src/app/{shared => cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types}/file-download-button/file-download-button.component.html (100%) rename src/app/{shared => cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types}/file-download-button/file-download-button.component.scss (100%) rename src/app/{shared => cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types}/file-download-button/file-download-button.component.spec.ts (93%) rename src/app/{shared => cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types}/file-download-button/file-download-button.component.ts (67%) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-rendering.module.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-rendering.module.ts index 1c8bdc4c502..90fc221b5b5 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-rendering.module.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-rendering.module.ts @@ -1,11 +1,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { - FileDownloadButtonComponent -} from '../../../../../../../../../shared/file-download-button/file-download-button.component'; + +import { TranslateModule } from '@ngx-translate/core'; + +import { FileDownloadButtonComponent } from './types/file-download-button/file-download-button.component'; import { SearchModule } from '../../../../../../../../../shared/search/search.module'; import { SharedModule } from '../../../../../../../../../shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; const COMPONENTS = [ FileDownloadButtonComponent diff --git a/src/app/shared/file-download-button/file-download-button.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.html similarity index 100% rename from src/app/shared/file-download-button/file-download-button.component.html rename to src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.html diff --git a/src/app/shared/file-download-button/file-download-button.component.scss b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.scss similarity index 100% rename from src/app/shared/file-download-button/file-download-button.component.scss rename to src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.scss diff --git a/src/app/shared/file-download-button/file-download-button.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.spec.ts similarity index 93% rename from src/app/shared/file-download-button/file-download-button.component.spec.ts rename to src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.spec.ts index 423b117544c..b6883e74233 100644 --- a/src/app/shared/file-download-button/file-download-button.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.spec.ts @@ -6,8 +6,8 @@ import { of } from 'rxjs'; import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; import { Bitstream } from 'src/app/core/shared/bitstream.model'; import { Item } from 'src/app/core/shared/item.model'; -import { TranslateLoaderMock } from '../mocks/translate-loader.mock'; -import { SharedModule } from '../shared.module'; +import { TranslateLoaderMock } from '../../../../../../../../../../../shared/mocks/translate-loader.mock'; +import { SharedModule } from '../../../../../../../../../../../shared/shared.module'; import { FileDownloadButtonComponent } from './file-download-button.component'; diff --git a/src/app/shared/file-download-button/file-download-button.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.ts similarity index 67% rename from src/app/shared/file-download-button/file-download-button.component.ts rename to src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.ts index 5a560117f80..390856f1fa2 100644 --- a/src/app/shared/file-download-button/file-download-button.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.ts @@ -1,9 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { FileDownloadLinkComponent } from '../file-download-link/file-download-link.component'; import { - AttachmentRenderingType, - AttachmentTypeRendering -} from '../../cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-type.decorator'; + FileDownloadLinkComponent +} from '../../../../../../../../../../../shared/file-download-link/file-download-link.component'; +import { AttachmentRenderingType, AttachmentTypeRendering } from '../../../attachment-type.decorator'; @Component({ selector: 'ds-file-download-button', From 2122d84342176637ce897cf31f0221f04ea26c4c Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 26 Apr 2023 15:33:02 +0200 Subject: [PATCH 068/195] [DURACOM-134] send-back action fixed --- .../workflow-item-action-page.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts b/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts index b8998a6dd72..2ed5639c5a7 100644 --- a/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts +++ b/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { Location } from '@angular/common'; -import { Observable, forkJoin } from 'rxjs'; +import { Observable, combineLatest } from 'rxjs'; import { map, switchMap, take } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { WorkflowItem } from '../core/submission/models/workflowitem.model'; @@ -52,7 +52,7 @@ export abstract class WorkflowItemActionPageComponent implements OnInit { * Performs the action and shows a notification based on the outcome of the action */ performAction() { - forkJoin([this.wfi$, this.requestService.removeByHrefSubstring('/discover')]).pipe( + combineLatest([this.wfi$, this.requestService.removeByHrefSubstring('/discover')]).pipe( take(1), switchMap(([wfi]) => this.sendRequest(wfi.id)) ).subscribe((successful: boolean) => { From 86d41f17d7f55e00ab97524733f8e8aead7c704d Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 28 Apr 2023 18:03:52 +0200 Subject: [PATCH 069/195] [DURACOM-134] Administer workflow actions fixed --- ...m-admin-workflow-actions.component.spec.ts | 4 +- ...e-item-admin-workflow-actions.component.ts | 6 +- .../workflowitems-edit-page-routing-paths.ts | 7 +- ...ed-workspaceitems-delete-page.component.ts | 26 ++++ .../workspaceitems-delete-page.component.html | 24 ++++ .../workspaceitems-delete-page.component.scss | 4 + ...rkspaceitems-delete-page.component.spec.ts | 110 +++++++++++++++++ .../workspaceitems-delete-page.component.ts | 111 ++++++++++++++++++ ...workspaceitems-edit-page-routing.module.ts | 24 +++- .../workspaceitems-edit-page.module.ts | 7 +- src/assets/i18n/en.json5 | 16 ++- .../workspace-items-delete.component.html | 0 .../workspace-items-delete.component.scss | 0 .../workspace-items-delete.component.ts | 10 ++ src/themes/custom/lazy-theme.module.ts | 4 +- 15 files changed, 343 insertions(+), 10 deletions(-) create mode 100644 src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts create mode 100644 src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html create mode 100644 src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.scss create mode 100644 src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts create mode 100644 src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts create mode 100644 src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.html create mode 100644 src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.scss create mode 100644 src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts index 628fc3f89ca..a8f0581ec0e 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts @@ -11,7 +11,7 @@ import { URLCombiner } from '../../../../../core/url-combiner/url-combiner'; import { WorkspaceItemAdminWorkflowActionsComponent } from './workspace-item-admin-workflow-actions.component'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; import { - getWorkflowItemDeleteRoute, + getWorkspaceItemDeleteRoute, } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { Item } from '../../../../../core/shared/item.model'; import { RemoteData } from '../../../../../core/data/remote-data'; @@ -83,7 +83,7 @@ describe('WorkspaceItemAdminWorkflowActionsComponent', () => { it('should render a delete button with the correct link', () => { const button = fixture.debugElement.query(By.css('a.delete-link')); const link = button.nativeElement.href; - expect(link).toContain(new URLCombiner(getWorkflowItemDeleteRoute(wsi.id)).toString()); + expect(link).toContain(new URLCombiner(getWorkspaceItemDeleteRoute(wsi.id)).toString()); }); it('should render a policies button with the correct link', () => { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts index adbd4216289..36678460da1 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts @@ -11,7 +11,7 @@ import { SupervisionOrderGroupSelectorComponent } from './supervision-order-group-selector/supervision-order-group-selector.component'; import { - getWorkflowItemDeleteRoute + getWorkspaceItemDeleteRoute } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../../../item-page/edit-item-page/edit-item-page.routing-paths'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; @@ -105,10 +105,10 @@ export class WorkspaceItemAdminWorkflowActionsComponent implements OnInit { } /** - * Returns the path to the delete page of this workflow item + * Returns the path to the delete page of this workspace item */ getDeleteRoute(): string { - return getWorkflowItemDeleteRoute(this.wsi.id); + return getWorkspaceItemDeleteRoute(this.wsi.id); } /** diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts index ece61f0321c..326eebe4a79 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts @@ -1,5 +1,5 @@ import { URLCombiner } from '../core/url-combiner/url-combiner'; -import { getWorkflowItemModuleRoute } from '../app-routing-paths'; +import { getWorkflowItemModuleRoute, getWorkspaceItemModuleRoute } from '../app-routing-paths'; export function getWorkflowItemPageRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId).toString(); @@ -24,8 +24,13 @@ export function getAdvancedWorkflowRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, ADVANCED_WORKFLOW_PATH).toString(); } +export function getWorkspaceItemDeleteRoute(wsiId: string) { + return new URLCombiner(getWorkspaceItemModuleRoute(), wsiId, WORKSPACE_ITEM_DELETE_PATH).toString(); +} + export const WORKFLOW_ITEM_EDIT_PATH = 'edit'; export const WORKFLOW_ITEM_DELETE_PATH = 'delete'; export const WORKFLOW_ITEM_VIEW_PATH = 'view'; export const WORKFLOW_ITEM_SEND_BACK_PATH = 'sendback'; export const ADVANCED_WORKFLOW_PATH = 'advanced'; +export const WORKSPACE_ITEM_DELETE_PATH = 'delete'; diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts new file mode 100644 index 00000000000..681cba21c86 --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts @@ -0,0 +1,26 @@ +import { ThemedComponent } from '../../shared/theme-support/themed.component'; +import { Component } from '@angular/core'; +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page.component'; + +/** + * Themed wrapper for WorkspaceItemsDeletePageComponent + */ + +@Component({ + selector: 'ds-themed-workspace-items-delete', + styleUrls: [], + templateUrl: './../../shared/theme-support/themed.component.html' +}) +export class ThemedWorkspaceItemsDeletePageComponent extends ThemedComponent { + protected getComponentName(): string { + return 'WorkspaceItemsDeletePageComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./workspaceitems-delete-page.component`); + } +} diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html new file mode 100644 index 00000000000..a0f0a1711ec --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html @@ -0,0 +1,24 @@ +
+

{{ 'workspace-item.delete.header' | translate }}

+ + + +
+ + + + + + diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.scss b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.scss new file mode 100644 index 00000000000..e52175abeaa --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.scss @@ -0,0 +1,4 @@ + +:host ::ng-deep ds-modify-item-overview table { + display: inline-table !important; +} diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts new file mode 100644 index 00000000000..f52dd497d8a --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts @@ -0,0 +1,110 @@ +import { RouteService } from '../..//core/services/route.service'; +import { NotificationsService } from '../..//shared/notifications/notifications.service'; +import { WorkspaceitemDataService } from '../..//core/submission/workspaceitem-data.service'; +import { RouterMock } from './../../shared/mocks/router.mock'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page.component'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; +import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { Location } from '@angular/common'; +import { of as observableOf } from 'rxjs'; +import { routeServiceStub } from '../../shared/testing/route-service.stub'; +import { LocationStub } from '../../shared/testing/location.stub'; +import { By } from '@angular/platform-browser'; +import { ActivatedRouteStub } from 'src/app/shared/testing/active-router.stub'; +import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils'; +import { WorkspaceItem } from 'src/app/core/submission/models/workspaceitem.model'; +import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; + +describe('WorkspaceitemsDeletePageComponent', () => { + let component: WorkspaceItemsDeletePageComponent; + let fixture: ComponentFixture; + + const workspaceitemDataServiceSpy = jasmine.createSpyObj('WorkspaceitemDataService', { + delete: jasmine.createSpy('delete') + }); + + const wsi = new WorkspaceItem(); + wsi.id = '1234'; + const dso = new DSpaceObject(); + dso.uuid = '1234'; + + const translateServiceStub = { + get: () => observableOf('test-message'), + onLangChange: new EventEmitter(), + onTranslationChange: new EventEmitter(), + onDefaultLangChange: new EventEmitter() + }; + + const modalService = { + open: () => {/** empty */}, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [WorkspaceItemsDeletePageComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: new ActivatedRouteStub( + {}, + { + wsi: createSuccessfulRemoteDataObject(wsi), + dso: createSuccessfulRemoteDataObject(dso), + } + ), + }, + { provide: Router, useValue: new RouterMock() }, + { + provide: WorkspaceitemDataService, + useValue: workspaceitemDataServiceSpy, + }, + { provide: Location, useValue: new LocationStub() }, + { provide: NgbModal, useValue: modalService }, + { + provide: NotificationsService, + useValue: new NotificationsServiceStub(), + }, + { provide: TranslateService, useValue: translateServiceStub }, + { provide: RouteService, useValue: routeServiceStub }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(WorkspaceItemsDeletePageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have the current WorkspaceItem', () => { + console.log( (component as any).activatedRoute, 'data wsi'); + (component as any).activatedRoute.data.subscribe((data) => { + console.log(data, 'dataaa'); + expect(data.wsi.payload.id).toEqual('1234'); + }); + }); + + it('should delete the target workspace item', () => { + spyOn((component as any).modalService, 'open').and.returnValue({}); + component.confirmDelete(By.css('#delete-modal')); + fixture.detectChanges(); + expect((component as any).modalService.open).toHaveBeenCalled(); + }); + + it('should call workspaceItemService.delete', () => { + spyOn(workspaceitemDataServiceSpy, 'delete').and.returnValue(observableOf(createSuccessfulRemoteDataObject({}))); + component.sendDeleteRequest(); + expect((component as any).workspaceItemService.delete).toHaveBeenCalledWith('1234'); + }); +}); diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts new file mode 100644 index 00000000000..cff5fd6994c --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts @@ -0,0 +1,111 @@ +import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; +import { NoContent } from './../../core/shared/NoContent.model'; +import { RouteService } from 'src/app/core/services/route.service'; +import { getFirstCompletedRemoteData, getRemoteDataPayload } from './../../core/shared/operators'; +import { RemoteData } from 'src/app/core/data/remote-data'; +import { Component, OnInit } from '@angular/core'; +import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; +import { Observable, map, switchMap, take } from 'rxjs'; +import { ActivatedRoute, Data, Params, Router } from '@angular/router'; +import { Location } from '@angular/common'; +import { WorkspaceitemDataService } from 'src/app/core/submission/workspaceitem-data.service'; +import { TranslateService } from '@ngx-translate/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; + +@Component({ + selector: 'ds-workspaceitems-delete-page', + templateUrl: './workspaceitems-delete-page.component.html', + styleUrls: ['./workspaceitems-delete-page.component.scss'] +}) +export class WorkspaceItemsDeletePageComponent implements OnInit { + + /** + * The workspaceitem to delete + */ + public wsi$: Observable; + + /** + * The dspace object + */ + public dso$: Observable; + + /** + * The previous query parameters + */ + private previousQueryParameters?: Params; + + constructor( + private activatedRoute: ActivatedRoute, + private router: Router, + private routeService: RouteService, + private workspaceItemService: WorkspaceitemDataService, + private notificationsService: NotificationsService, + private translationService: TranslateService, + private location: Location, + private modalService: NgbModal, + ) { } + + ngOnInit(): void { + this.wsi$ = this.activatedRoute.data.pipe(map((data: Data) => data.wsi as RemoteData), getRemoteDataPayload()); + this.dso$ = this.activatedRoute.data.pipe(map((data: Data) => data.dso as RemoteData), getRemoteDataPayload()); + this.previousQueryParameters = (this.location.getState() as { [key: string]: any }).previousQueryParams; + } + + /** + * Navigates to the previous url + * If there's not previous url, it continues to the mydspace page instead + */ + previousPage() { + this.routeService.getPreviousUrl().pipe(take(1)) + .subscribe((url: string) => { + let params: Params = {}; + if (!url) { + url = '/mydspace'; + params = this.previousQueryParameters; + } + if (url.split('?').length > 1) { + for (const param of url.split('?')[1].split('&')) { + params[param.split('=')[0]] = decodeURIComponent(param.split('=')[1]); + } + } + void this.router.navigate([url.split('?')[0]], { queryParams: params }); + } + ); + } + + /** + * Open the modal to confirm the deletion of the workspaceitem + */ + public async confirmDelete(content) { + await this.modalService.open(content).result.then( + (result) => { + if (result === 'ok') { + this.sendDeleteRequest(); + } + } + ); + } + + /** + * Delete the target workspaceitem object + */ + sendDeleteRequest() { + this.wsi$.pipe( + switchMap((wsi: WorkspaceItem) => this.workspaceItemService.delete(wsi.id).pipe( + getFirstCompletedRemoteData(), + )) + ).subscribe((response: RemoteData) => { + if (response.hasSucceeded) { + const title = this.translationService.get('workspace-item.delete.notification.success.title'); + const content = this.translationService.get('workspace-item.delete.title'); + this.notificationsService.success(title, content); + this.previousPage(); + } else { + const title = this.translationService.get('workspace-item.delete.notification.error.title'); + const content = this.translationService.get('workspace-item.delete.notification.error.content'); + this.notificationsService.error(title, content); + } + }); + } +} diff --git a/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts b/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts index a66bec9f6b6..51edeb9ea77 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts @@ -8,6 +8,8 @@ import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item- import { ItemFromWorkspaceResolver } from './item-from-workspace.resolver'; import { WorkspaceItemPageResolver } from './workspace-item-page.resolver'; import { PendingChangesGuard } from '../submission/edit/pending-changes/pending-changes.guard'; +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/workspaceitems-delete-page.component'; +import { ThemedWorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/themed-workspaceitems-delete-page.component'; @NgModule({ imports: [ @@ -36,7 +38,27 @@ import { PendingChangesGuard } from '../submission/edit/pending-changes/pending- breadcrumb: I18nBreadcrumbResolver }, data: { title: 'workspace-item.view.title', breadcrumbKey: 'workspace-item.view' } - } + }, + { + canActivate: [AuthenticatedGuard], + path: 'delete', + component: WorkspaceItemsDeletePageComponent, + resolve: { + dso: ItemFromWorkspaceResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { title: 'workspace-item.delete', breadcrumbKey: 'workspace-item.delete' } + }, + { + canActivate: [AuthenticatedGuard], + path: 'delete', + component: ThemedWorkspaceItemsDeletePageComponent, + resolve: { + dso: ItemFromWorkspaceResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { title: 'workspace-item.delete', breadcrumbKey: 'workspace-item.delete' } + }, ] } ]) diff --git a/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts b/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts index 65a40f3f7c8..77cb7a6e66c 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts @@ -3,6 +3,8 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { WorkspaceitemsEditPageRoutingModule } from './workspaceitems-edit-page-routing.module'; import { SubmissionModule } from '../submission/submission.module'; +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/workspaceitems-delete-page.component'; +import { ThemedWorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/themed-workspaceitems-delete-page.component'; @NgModule({ imports: [ @@ -11,7 +13,10 @@ import { SubmissionModule } from '../submission/submission.module'; SharedModule, SubmissionModule, ], - declarations: [] + declarations: [ + WorkspaceItemsDeletePageComponent, + ThemedWorkspaceItemsDeletePageComponent, + ] }) /** * This module handles all modules that need to access the workspaceitems edit page. diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 2ffd38e2030..a8b00470498 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -6729,9 +6729,23 @@ "workspace-item.view.title": "Workspace View", + "workspace-item.delete.breadcrumbs": "Workspace Delete", - "workflow-item.advanced.title": "Advanced workflow", + "workspace-item.delete.header": "Delete workspace item", + + "workspace-item.delete.button.confirm": "Delete", + + "workspace-item.delete.button.cancel": "Cancel", + + "workspace-item.delete.notification.success.title": "Deleted", + "workspace-item.delete.title": "This workspace item was successfully deleted", + + "workspace-item.delete.notification.error.title": "Something went wrong", + + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + + "workflow-item.advanced.title": "Advanced workflow", "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", diff --git a/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.html b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.scss b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts new file mode 100644 index 00000000000..95494d76dd3 --- /dev/null +++ b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { WorkspaceItemsDeletePageComponent as BaseComponent } from '../../../../../app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component'; + + +@Component({ + selector: 'ds-workspaceitems-delete-page', + templateUrl: '../../../../../app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html', +}) +export class WorkspaceItemsDeletePageComponent extends BaseComponent { +} diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index d26659e3473..1b2ce85ff85 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -137,6 +137,7 @@ import { DsoEditMetadataComponent } from './app/dso-shared/dso-edit-metadata/dso import { DsoSharedModule } from '../../app/dso-shared/dso-shared.module'; import { SystemWideAlertModule } from '../../app/system-wide-alert/system-wide-alert.module'; import { DsoPageModule } from '../../app/shared/dso-page/dso-page.module'; +import { WorkspaceItemsDeletePageComponent } from './app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component'; const DECLARATIONS = [ FileSectionComponent, @@ -193,7 +194,8 @@ const DECLARATIONS = [ ExternalSourceEntryImportModalComponent, ResultsBackButtonComponent, DsoEditMetadataComponent, - BrowseMostElementsComponent + BrowseMostElementsComponent, + WorkspaceItemsDeletePageComponent, ]; @NgModule({ From 30d11d5815cd60119014490e983c283ff4cb3a91 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 2 May 2023 15:49:41 +0200 Subject: [PATCH 070/195] [DURACOM-134] Fix import paths --- .../workspaceitems-delete-page.component.spec.ts | 16 ++++++++-------- .../workspaceitems-delete-page.component.ts | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts index f52dd497d8a..fc0bef83f0f 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts @@ -1,7 +1,7 @@ -import { RouteService } from '../..//core/services/route.service'; -import { NotificationsService } from '../..//shared/notifications/notifications.service'; -import { WorkspaceitemDataService } from '../..//core/submission/workspaceitem-data.service'; -import { RouterMock } from './../../shared/mocks/router.mock'; +import { RouteService } from '../../core/services/route.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; +import { RouterMock } from '../../shared/mocks/router.mock'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page.component'; @@ -15,10 +15,10 @@ import { of as observableOf } from 'rxjs'; import { routeServiceStub } from '../../shared/testing/route-service.stub'; import { LocationStub } from '../../shared/testing/location.stub'; import { By } from '@angular/platform-browser'; -import { ActivatedRouteStub } from 'src/app/shared/testing/active-router.stub'; -import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils'; -import { WorkspaceItem } from 'src/app/core/submission/models/workspaceitem.model'; -import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; +import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; +import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; describe('WorkspaceitemsDeletePageComponent', () => { let component: WorkspaceItemsDeletePageComponent; diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts index cff5fd6994c..77ed0519d6b 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts @@ -1,17 +1,17 @@ -import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; -import { NoContent } from './../../core/shared/NoContent.model'; -import { RouteService } from 'src/app/core/services/route.service'; -import { getFirstCompletedRemoteData, getRemoteDataPayload } from './../../core/shared/operators'; -import { RemoteData } from 'src/app/core/data/remote-data'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { NoContent } from '../../core/shared/NoContent.model'; +import { RouteService } from '../../core/services/route.service'; +import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../core/shared/operators'; +import { RemoteData } from '../../core/data/remote-data'; import { Component, OnInit } from '@angular/core'; import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; -import { Observable, map, switchMap, take } from 'rxjs'; +import { map, Observable, switchMap, take } from 'rxjs'; import { ActivatedRoute, Data, Params, Router } from '@angular/router'; import { Location } from '@angular/common'; -import { WorkspaceitemDataService } from 'src/app/core/submission/workspaceitem-data.service'; +import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; import { TranslateService } from '@ngx-translate/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; @Component({ selector: 'ds-workspaceitems-delete-page', From 7d5f44205ec673e632738b052acb25ac1db96672 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 3 May 2023 11:11:10 +0200 Subject: [PATCH 071/195] [DURACOM-134] Fixes --- .../workspaceitems-delete-page.component.spec.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts index fc0bef83f0f..c11659df24b 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts @@ -25,7 +25,7 @@ describe('WorkspaceitemsDeletePageComponent', () => { let fixture: ComponentFixture; const workspaceitemDataServiceSpy = jasmine.createSpyObj('WorkspaceitemDataService', { - delete: jasmine.createSpy('delete') + delete: observableOf(createSuccessfulRemoteDataObject({})) }); const wsi = new WorkspaceItem(); @@ -88,9 +88,7 @@ describe('WorkspaceitemsDeletePageComponent', () => { }); it('should have the current WorkspaceItem', () => { - console.log( (component as any).activatedRoute, 'data wsi'); (component as any).activatedRoute.data.subscribe((data) => { - console.log(data, 'dataaa'); expect(data.wsi.payload.id).toEqual('1234'); }); }); @@ -103,7 +101,6 @@ describe('WorkspaceitemsDeletePageComponent', () => { }); it('should call workspaceItemService.delete', () => { - spyOn(workspaceitemDataServiceSpy, 'delete').and.returnValue(observableOf(createSuccessfulRemoteDataObject({}))); component.sendDeleteRequest(); expect((component as any).workspaceItemService.delete).toHaveBeenCalledWith('1234'); }); From 39a1f7f196b707d8dd5a019eb6eecf443656bb6e Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 27 Jul 2023 12:14:47 +0200 Subject: [PATCH 072/195] [CST-10123] fix issue with infinite RetrieveAuthenticatedEpersonSuccessAction dispatched --- src/app/core/auth/auth.effects.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index e7301d1ae42..2ed01e1eb86 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -137,6 +137,7 @@ export class AuthEffects { user$ = this.authService.retrieveAuthenticatedUserByHref(action.payload); } return user$.pipe( + take(1), map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user)), catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error)))); }) From 996013716bbdcc1865aadb7475c9aeb4e72f642d Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 18 May 2023 19:03:37 +0200 Subject: [PATCH 073/195] Fix test --- .../workspaceitems-delete-page.component.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts index c11659df24b..e2135636a54 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts @@ -6,7 +6,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page.component'; import { ActivatedRoute, Router } from '@angular/router'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; @@ -46,7 +46,10 @@ describe('WorkspaceitemsDeletePageComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot()], + imports: [ + NgbModalModule, + TranslateModule.forRoot() + ], declarations: [WorkspaceItemsDeletePageComponent], providers: [ { @@ -65,7 +68,6 @@ describe('WorkspaceitemsDeletePageComponent', () => { useValue: workspaceitemDataServiceSpy, }, { provide: Location, useValue: new LocationStub() }, - { provide: NgbModal, useValue: modalService }, { provide: NotificationsService, useValue: new NotificationsServiceStub(), From fde497e34f273798aec5f8a301701a92848a123e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 18 May 2023 13:28:08 -0500 Subject: [PATCH 074/195] Comment out problematic test in "workspaceitems-delete-page" component --- .../workspaceitems-delete-page.component.spec.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts index e2135636a54..c0094e36aa6 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts @@ -14,7 +14,6 @@ import { Location } from '@angular/common'; import { of as observableOf } from 'rxjs'; import { routeServiceStub } from '../../shared/testing/route-service.stub'; import { LocationStub } from '../../shared/testing/location.stub'; -import { By } from '@angular/platform-browser'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; @@ -95,12 +94,12 @@ describe('WorkspaceitemsDeletePageComponent', () => { }); }); - it('should delete the target workspace item', () => { + /*it('should delete the target workspace item', () => { spyOn((component as any).modalService, 'open').and.returnValue({}); component.confirmDelete(By.css('#delete-modal')); fixture.detectChanges(); expect((component as any).modalService.open).toHaveBeenCalled(); - }); + });*/ it('should call workspaceItemService.delete', () => { component.sendDeleteRequest(); From 5a26a320604b94deb13883e481f23cffadaabfaf Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 18 May 2023 23:21:45 +0200 Subject: [PATCH 075/195] Fix test --- .../workspaceitems-delete-page.component.spec.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts index c0094e36aa6..ac2878e8bd8 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts @@ -14,6 +14,7 @@ import { Location } from '@angular/common'; import { of as observableOf } from 'rxjs'; import { routeServiceStub } from '../../shared/testing/route-service.stub'; import { LocationStub } from '../../shared/testing/location.stub'; +import { By } from '@angular/platform-browser'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; @@ -39,10 +40,6 @@ describe('WorkspaceitemsDeletePageComponent', () => { onDefaultLangChange: new EventEmitter() }; - const modalService = { - open: () => {/** empty */}, - }; - beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ @@ -94,12 +91,12 @@ describe('WorkspaceitemsDeletePageComponent', () => { }); }); - /*it('should delete the target workspace item', () => { - spyOn((component as any).modalService, 'open').and.returnValue({}); + it('should delete the target workspace item', () => { + spyOn((component as any).modalService, 'open').and.returnValue({result: Promise.resolve('ok')}); component.confirmDelete(By.css('#delete-modal')); fixture.detectChanges(); expect((component as any).modalService.open).toHaveBeenCalled(); - });*/ + }); it('should call workspaceItemService.delete', () => { component.sendDeleteRequest(); From ae5a22ea7816fb8482dd739e6633eb3977ecc7ce Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 18 May 2023 23:21:45 +0200 Subject: [PATCH 076/195] [DSC-1091] Fix failing tests --- src/app/shared/form/form.component.html | 1 - src/app/shared/form/form.component.spec.ts | 15 ++++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/shared/form/form.component.html b/src/app/shared/form/form.component.html index 29351a7fd81..8fb82659811 100644 --- a/src/app/shared/form/form.component.html +++ b/src/app/shared/form/form.component.html @@ -15,7 +15,6 @@
diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html index 477f96e19e2..09594bd56ac 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html @@ -1,6 +1,6 @@ - From 59cb63a32efee2209797e6efaba14c94d38d48e5 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Thu, 7 Sep 2023 10:03:36 +0300 Subject: [PATCH 090/195] [DSC-1235] adjust display of cris-layout-metrics-box --- .../boxes/metrics/cris-layout-metrics-box.component.html | 2 +- .../boxes/metrics/metric-row/metric-row.component.html | 2 +- .../shared/metric/metric-loader/metric-loader.component.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.html index d1176864adf..fc6fabc57a7 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.html @@ -4,7 +4,7 @@
+ class="d-flex flex-wrap gap-3">
diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html index 09594bd56ac..50584b04c41 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/metric-row/metric-row.component.html @@ -1,6 +1,6 @@ - diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.html b/src/app/shared/metric/metric-loader/metric-loader.component.html index f8f338c4545..4d3eb179215 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.html +++ b/src/app/shared/metric/metric-loader/metric-loader.component.html @@ -1,3 +1,3 @@ - From f6d7b0d2b2a8cd4b60f235f087e21533de2df53a Mon Sep 17 00:00:00 2001 From: Nikita Krivonosov Date: Fri, 8 Sep 2023 09:17:46 +0200 Subject: [PATCH 093/195] [CST-11736] Submission form: Wrong mapping between validation error and form model of a group --- src/app/shared/form/form.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/form/form.component.ts b/src/app/shared/form/form.component.ts index 05f88c095eb..34d2d2266d6 100644 --- a/src/app/shared/form/form.component.ts +++ b/src/app/shared/form/form.component.ts @@ -203,10 +203,10 @@ export class FormComponent implements OnDestroy, OnInit { } if (field) { - const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel); + const modelArrayIndex = fieldIndex > 0 ? fieldIndex : null; + const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel, modelArrayIndex); this.formService.addErrorToField(field, model, error.message); this.changeDetectorRef.detectChanges(); - } }); From 28527c24a48e2ead45c21734ce81ac5c403091a3 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 8 Sep 2023 16:08:16 +0200 Subject: [PATCH 094/195] [DSC-1224] Fixes failing tests --- .../metadata-registry.component.spec.ts | 13 ++++++++-- .../metadata-registry.component.ts | 2 -- .../metadata-schema-export.service.spec.ts | 24 +++++++++++++++++-- .../metadata-schema-export.service.ts | 6 ++--- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts index 944288a7a51..36ecdaf3162 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts @@ -1,6 +1,6 @@ import { MetadataRegistryComponent } from './metadata-registry.component'; import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; -import { of as observableOf } from 'rxjs'; +import { of, of as observableOf } from 'rxjs'; import { buildPaginatedList } from '../../../core/data/paginated-list.model'; import { TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; @@ -20,6 +20,9 @@ import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { + MetadataSchemaExportService +} from '../../../shared/metadata-export/metadata-schema-export/metadata-schema-export.service'; describe('MetadataRegistryComponent', () => { let comp: MetadataRegistryComponent; @@ -75,7 +78,13 @@ describe('MetadataRegistryComponent', () => { { provide: RegistryService, useValue: registryServiceStub }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: PaginationService, useValue: paginationService }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { + provide: MetadataSchemaExportService, + useValue: jasmine.createSpyObj('metadataSchemaExportService', { + exportSchema: of(1), + }) + } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(MetadataRegistryComponent, { diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts index 723338ff523..0f052ba25ef 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts @@ -7,7 +7,6 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio import { filter, map, switchMap, take } from 'rxjs/operators'; import { hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; -import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; import { toFindListOptions } from '../../../shared/pagination/pagination.utils'; @@ -49,7 +48,6 @@ export class MetadataRegistryComponent { constructor(private registryService: RegistryService, private notificationsService: NotificationsService, - private router: Router, private paginationService: PaginationService, private translateService: TranslateService, private readonly metadataSchemaExportService: MetadataSchemaExportService) { diff --git a/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.spec.ts b/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.spec.ts index 9b3a5b96f50..cca41263ab5 100644 --- a/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.spec.ts +++ b/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.spec.ts @@ -1,16 +1,36 @@ import { TestBed } from '@angular/core/testing'; import { MetadataSchemaExportService } from './metadata-schema-export.service'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import { ScriptDataService } from '../../../core/data/processes/script-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; describe('MetadataSchemaExportService', () => { let service: MetadataSchemaExportService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule], + providers: [ + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { + provide: ScriptDataService, + useValue: jasmine.createSpyObj('scriptDataService', { + invoke: createSuccessfulRemoteDataObject$({ processId: '45' }), + }) + } + ], + }); service = TestBed.inject(MetadataSchemaExportService); }); it('should be created', () => { expect(service).toBeTruthy(); }); -}); +}) +; diff --git a/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.ts b/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.ts index 674b82e80e0..9190ddb86a3 100644 --- a/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.ts +++ b/src/app/shared/metadata-export/metadata-schema-export/metadata-schema-export.service.ts @@ -18,9 +18,9 @@ export class MetadataSchemaExportService { private readonly SCRIPT_NAME = 'export-schema'; constructor( - protected notificationsService: NotificationsService, - protected translate: TranslateService, - protected scriptDataService: ScriptDataService + protected readonly notificationsService: NotificationsService, + protected readonly translate: TranslateService, + protected readonly scriptDataService: ScriptDataService ) { } From f8abdb8561e6112942c81d817228698724f4b04c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 11 Sep 2023 09:37:30 +0200 Subject: [PATCH 095/195] [DSC-1224] Use button for registry download --- .../metadata-registry.component.html | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.html b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.html index dc0fc48401c..fde93042e09 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.html +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.html @@ -44,11 +44,13 @@
-
{{ 'cris-layout.rendering.collections.mapped-collection.label' | translate }}
-
+
{{ 'cris-layout.rendering.collections.mapped-collection.label' | translate }}
+
{{ mappedCollection.firstMetadataValue('dc.title') }} From d445f51c983269fc9fae652309078a684908802d Mon Sep 17 00:00:00 2001 From: nikunj59 Date: Wed, 25 May 2022 08:19:34 +0200 Subject: [PATCH 112/195] [DSC-552] changes for plumx attribute added in metrix-plumx --- .../metric-plumx/metric-plumx.component.html | 15 +++---- .../metric-plumx.component.spec.ts | 45 ++++++++++++++++++- ...bmission-import-external.component.spec.ts | 2 +- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index 208e148888a..72a7b054258 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -1,6 +1,7 @@
- - {{ remark | dsListMetricProps: "placeholder":isListElement }} - + >{{ remark | dsListMetricProps: "placeholder":isListElement }}
- - {{ remark | dsListMetricProps: "placeholder":isListElement }} - + >{{ remark | dsListMetricProps: "placeholder":isListElement }}
diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts b/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts index 45c33735f6f..174bccfc455 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts @@ -20,7 +20,39 @@ describe('MetricPlumxComponent', () => { metricCount: 333, metricType: 'plumX', rank: null, - remark: '{"type":"Publication","src":"//cdn.plu.mx/widget-popup.js","href":"https://plu.mx/plum/a/?doi=10.1056/NEJMe2025111", "list-data-publication-badge-enabled":"true","data-publication-badge-enabled":"true"}', + remark: '{' + + ' "type":"Publication",' + + ' "src":"//cdn.plu.mx/widget-popup.js",' + + ' "href":"https://plu.mx/plum/a/?doi=10.1056/NEJMe2025111",' + + ' "list-data-publication-badge-enabled":"true",' + + ' "data-publication-badge-enabled":"true"},' + + ' "data-popup": "left",' + + ' "data-hide-when-empty": true,' + + ' "data-hide-usage": false,' + + ' "data-hide-captures": false,' + + ' "data-hide-mentions": false,' + + ' "data-hide-socialmedia": false,' + + ' "data-hide-citations": false,' + + ' "data-pass-hidden-categories": false,' + + ' "data-detail-same-page": false,' + + ' "list-data-lang": "en",' + + ' "list-data-no-name": true,' + + ' "list-data-num-artifacts": 5,' + + ' "list-data-width": "6em",' + + ' "list-data-no-description": true,' + + ' "list-data-no-stats": true,' + + ' "list-data-no-thumbnail": true,' + + ' "list-data-no-artifacts": true,' + + ' "list-data-popup": "bottom",' + + ' "list-data-hide-when-empty": true,' + + ' "list-data-hide-usage": false,' + + ' "list-data-hide-captures": false,' + + ' "list-data-hide-mentions": false,' + + ' "list-data-hide-socialmedia": false,' + + ' "list-data-hide-citations": false,' + + ' "list-data-pass-hidden-categories": false,' + + ' "list-data-detail-same-page": false' + + '}', startDate: null, type: null, _links: null @@ -48,13 +80,24 @@ describe('MetricPlumxComponent', () => { component.metric = metricMock; fixture.detectChanges(); }); + it('should create', () => { expect(component).toBeTruthy(); }); + it('should render plumx widget', (done) => { const innerHtmlMetric = fixture.debugElement.query(By.css('a')); expect(innerHtmlMetric.nativeElement.className).toEqual('plumx-plum-print-popup'); expect(innerHtmlMetric.nativeElement.href).toEqual('https://plu.mx/plum/a/?doi=10.1056/NEJMe2025111'); + expect(innerHtmlMetric.nativeElement.attributes['data-popup'].nodeValue).toEqual('left'); + expect(innerHtmlMetric.nativeElement.attributes['data-hide-when-empty'].nodeValue).toEqual('true'); + expect(innerHtmlMetric.nativeElement.attributes['data-hide-usage']).toBeUndefined(); + expect(innerHtmlMetric.nativeElement.attributes['data-hide-captures']).toBeUndefined(); + expect(innerHtmlMetric.nativeElement.attributes['data-hide-mentions']).toBeUndefined(); + expect(innerHtmlMetric.nativeElement.attributes['data-hide-socialmedia']).toBeUndefined(); + expect(innerHtmlMetric.nativeElement.attributes['data-hide-citations']).toBeUndefined(); + expect(innerHtmlMetric.nativeElement.attributes['data-pass-hidden-categories']).toBeUndefined(); + expect(innerHtmlMetric.nativeElement.attributes['data-detail-same-page']).toBeUndefined(); done(); }); }); diff --git a/src/app/submission/import-external/submission-import-external.component.spec.ts b/src/app/submission/import-external/submission-import-external.component.spec.ts index cc299886b51..f6e1ceb1f94 100644 --- a/src/app/submission/import-external/submission-import-external.component.spec.ts +++ b/src/app/submission/import-external/submission-import-external.component.spec.ts @@ -172,7 +172,7 @@ describe('SubmissionImportExternalComponent test suite', () => { ngbModal.open.and.returnValue({componentInstance: { externalSourceEntry: null}}); comp.import(entry); - expect(compAsAny.modalService.open).toHaveBeenCalledWith(SubmissionImportExternalPreviewComponent, { size: 'lg', scrollable: true }); + expect(compAsAny.modalService.open).toHaveBeenCalledWith(SubmissionImportExternalPreviewComponent, { size: 'lg', scrollable: true }); expect(comp.modalRef.componentInstance.externalSourceEntry).toEqual(entry); }); From b1f46a8273c9d78adc55fc329a2cc0838f51dec1 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 11 Jul 2023 16:05:33 +0200 Subject: [PATCH 113/195] [DURACOM-177] gap-* classes --- src/styles/_global-styles.scss | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/styles/_global-styles.scss b/src/styles/_global-styles.scss index 3322a2c6cc2..6a606d07db2 100644 --- a/src/styles/_global-styles.scss +++ b/src/styles/_global-styles.scss @@ -270,3 +270,27 @@ ul.dso-edit-menu-dropdown > li .nav-item.nav-link { padding: 0; display: inline; } + + +/* Flexbox gap */ + +.gap-0 { gap: 0; } +.gap-1 { gap: calc(#{$spacer} * .25); } +.gap-2 { gap: calc(#{$spacer} * .5); } +.gap-3 { gap: #{$spacer}; } +.gap-4 { gap: calc(#{$spacer} * 1.5); } +.gap-5 { gap: calc(#{$spacer} * 3); } + +.gapx-0 { column-gap: 0; } +.gapx-1 { column-gap: calc(#{$spacer} * .25); } +.gapx-2 { column-gap: calc(#{$spacer} * .5); } +.gapx-3 { column-gap: #{$spacer}; } +.gapx-4 { column-gap: calc(#{$spacer} * 1.5); } +.gapx-5 { column-gap: calc(#{$spacer} * 3); } + +.gapy-0 { row-gap: 0; } +.gapy-1 { row-gap: calc(#{$spacer} * .25); } +.gapy-2 { row-gap: calc(#{$spacer} * .5); } +.gapy-3 { row-gap: #{$spacer}; } +.gapy-4 { row-gap: calc(#{$spacer} * 1.5); } +.gapy-5 { row-gap: calc(#{$spacer} * 3); } From 23c557789e9af75d658531fb2d0131229f8338fd Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 21 Sep 2023 13:03:54 +0200 Subject: [PATCH 114/195] [DSC-552] Layout fixes --- .../person-search-result-list-element.component.html | 10 +++++----- .../metric-donuts/metric-donuts.component.html | 2 +- .../item-search-result-list-element.component.html | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html index cacbc2e1c82..9c3f012dfd2 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html @@ -17,9 +17,9 @@
-
-
-
+
+ diff --git a/src/app/shared/object-list/metric-donuts/metric-donuts.component.html b/src/app/shared/object-list/metric-donuts/metric-donuts.component.html index 87e0f108d36..8e75097b496 100644 --- a/src/app/shared/object-list/metric-donuts/metric-donuts.component.html +++ b/src/app/shared/object-list/metric-donuts/metric-donuts.component.html @@ -1,4 +1,4 @@ -
+
-
-
-
+
+
+
From 9f16c6dabd0f3bb2aaea0252df7edc2db8b9f8bb Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 21 Sep 2023 18:02:42 +0200 Subject: [PATCH 115/195] [DSC-552] Test fixed --- .../metric-plumx/metric-plumx.component.html | 60 ++++++++-------- .../metric-plumx.component.spec.ts | 69 ++++++++++--------- 2 files changed, 65 insertions(+), 64 deletions(-) diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index 72a7b054258..088361c2be4 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -1,33 +1,33 @@
- diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts b/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts index 174bccfc455..0eaf1f16c45 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.spec.ts @@ -6,11 +6,12 @@ import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; import { By } from '@angular/platform-browser'; import { ListMetricPropsPipe } from '../pipes/list-metric-props/list-metric-props.pipe'; import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service'; +import { Metric } from '../../../core/shared/metric.model'; describe('MetricPlumxComponent', () => { let component: MetricPlumxComponent; let fixture: ComponentFixture; - const metricMock = { + const metricMock: Metric = { acquisitionDate: new Date(), deltaPeriod1: null, deltaPeriod2: null, @@ -20,39 +21,39 @@ describe('MetricPlumxComponent', () => { metricCount: 333, metricType: 'plumX', rank: null, - remark: '{' + - ' "type":"Publication",' + - ' "src":"//cdn.plu.mx/widget-popup.js",' + - ' "href":"https://plu.mx/plum/a/?doi=10.1056/NEJMe2025111",' + - ' "list-data-publication-badge-enabled":"true",' + - ' "data-publication-badge-enabled":"true"},' + - ' "data-popup": "left",' + - ' "data-hide-when-empty": true,' + - ' "data-hide-usage": false,' + - ' "data-hide-captures": false,' + - ' "data-hide-mentions": false,' + - ' "data-hide-socialmedia": false,' + - ' "data-hide-citations": false,' + - ' "data-pass-hidden-categories": false,' + - ' "data-detail-same-page": false,' + - ' "list-data-lang": "en",' + - ' "list-data-no-name": true,' + - ' "list-data-num-artifacts": 5,' + - ' "list-data-width": "6em",' + - ' "list-data-no-description": true,' + - ' "list-data-no-stats": true,' + - ' "list-data-no-thumbnail": true,' + - ' "list-data-no-artifacts": true,' + - ' "list-data-popup": "bottom",' + - ' "list-data-hide-when-empty": true,' + - ' "list-data-hide-usage": false,' + - ' "list-data-hide-captures": false,' + - ' "list-data-hide-mentions": false,' + - ' "list-data-hide-socialmedia": false,' + - ' "list-data-hide-citations": false,' + - ' "list-data-pass-hidden-categories": false,' + - ' "list-data-detail-same-page": false' + - '}', + remark: JSON.stringify({ + 'type': 'Publication', + 'src': '//cdn.plu.mx/widget-popup.js', + 'href': 'https://plu.mx/plum/a/?doi=10.1056/NEJMe2025111', + 'list-data-publication-badge-enabled': true, + 'data-publication-badge-enabled': true, + 'data-popup': 'left', + 'data-hide-when-empty': true, + 'data-hide-usage': false, + 'data-hide-captures': false, + 'data-hide-mentions': false, + 'data-hide-socialmedia': false, + 'data-hide-citations': false, + 'data-pass-hidden-categories': false, + 'data-detail-same-page': false, + 'list-data-lang': 'en', + 'list-data-no-name': true, + 'list-data-num-artifacts': 5, + 'list-data-width': '6em', + 'list-data-no-description': true, + 'list-data-no-stats': true, + 'list-data-no-thumbnail': true, + 'list-data-no-artifacts': true, + 'list-data-popup': 'bottom', + 'list-data-hide-when-empty': true, + 'list-data-hide-usage': false, + 'list-data-hide-captures': false, + 'list-data-hide-mentions': false, + 'list-data-hide-socialmedia': false, + 'list-data-hide-citations': false, + 'list-data-pass-hidden-categories': false, + 'list-data-detail-same-page': false + }), startDate: null, type: null, _links: null From a00c9831b5f1e39a7e1b3d02803c6471e8166e87 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 21 Sep 2023 18:48:54 +0000 Subject: [PATCH 116/195] Merged in dspace-cris-7-DSC-291 (pull request #864) Add pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline * [DSC-291] WIP pipeline --- bitbucket-pipelines.yml | 58 ++++++++++------------------------------- 1 file changed, 14 insertions(+), 44 deletions(-) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index e458902a0d0..da014253bc1 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,58 +1,28 @@ +options: + runs-on: ubuntu-latest + definitions: steps: - - step: &test-code-checks - services: - - docker + - step: &unittest-code-checks + name: test-code-checks + image: + name: cypress/browsers:node18.12.0-chrome107 + run-as-user: 1000 + size: 2x + caches: + - node script: - yarn install --frozen-lockfile - yarn run lint --quiet - yarn run check-circ-deps - yarn run build:prod - yarn run test:headless - - docker-compose -f ./docker/docker-compose-ci.yml up -d - - docker-compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli - - docker container ls - - yarn run serve:ssr - - yarn run e2e pipelines: - default: - - stage: - name: - image: node:18 - caches: - - node - steps: - - step: *test-code-checks branches: 'dspace-cris-7': - - parallel: - - stage: - name: - image: node:16 - caches: - - node - steps: - - step: *test-code-checks - - stage: - image: node:18 - caches: - - node - steps: - - step: *test-code-checks - + - step: *unittest-code-checks pull-requests: '**': - - parallel: - - stage: - image: node:16 - caches: - - node - steps: - - step: *test-code-checks - - stage: - image: node:18 - caches: - - node - steps: - - step: *test-code-checks + - step: *unittest-code-checks + From 5e2960de90ad37db7f3aeab7a1fef9d36c226fb1 Mon Sep 17 00:00:00 2001 From: Andrea Barbasso <´andrea.barbasso@4science.com´> Date: Fri, 22 Sep 2023 17:41:38 +0200 Subject: [PATCH 117/195] [DSC-1255] complete translation of it.json5 --- src/assets/i18n/it.json5 | 1866 +++++++++++++------------------------- 1 file changed, 606 insertions(+), 1260 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 81336daf11f..92ddf9d6d2d 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -323,7 +323,6 @@ // "admin.registries.bitstream-formats.table.name": "Name", "admin.registries.bitstream-formats.table.name": "Nome", // "admin.registries.bitstream-formats.table.id" : "ID", - // TODO New key - Add a translation "admin.registries.bitstream-formats.table.id" : "ID", // "admin.registries.bitstream-formats.table.return": "Back", @@ -382,7 +381,6 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namespace", // "admin.registries.metadata.schemas.table.downloads": "Download", - // TODO New key - Add a translation "admin.registries.metadata.schemas.table.downloads": "Download", // "admin.registries.metadata.title": "Metadata Registry", @@ -408,7 +406,6 @@ // "admin.registries.schema.fields.table.field": "Field", "admin.registries.schema.fields.table.field": "Campo", // "admin.registries.schema.fields.table.id" : "ID", - // TODO New key - Add a translation "admin.registries.schema.fields.table.id" : "ID", // "admin.registries.schema.fields.table.scopenote": "Scope Note", @@ -942,16 +939,13 @@ "admin.access-control.groups.form.return": "Indietro", // "admin.access-control.groups.form.tooltip.editGroupPage": "On this page, you can modify the properties and members of a group. In the top section, you can edit the group name and description, unless this is an admin group for a collection or community, in which case the group name and description are auto-generated and cannot be edited. In the following sections, you can edit group membership. See [the wiki](https://wiki.lyrasis.org/display/DSDOC7x/Create+or+manage+a+user+group) for more details.", - // TODO New key - Add a translation - "admin.access-control.groups.form.tooltip.editGroupPage": "On this page, you can modify the properties and members of a group. In the top section, you can edit the group name and description, unless this is an admin group for a collection or community, in which case the group name and description are auto-generated and cannot be edited. In the following sections, you can edit group membership. See [the wiki](https://wiki.lyrasis.org/display/DSDOC7x/Create+or+manage+a+user+group) for more details.", + "admin.access-control.groups.form.tooltip.editGroupPage": "In questa pagina è possibile modificare le proprietà e i membri di un gruppo. Nella sezione in alto, è possibile modificare il nome e la descrizione del gruppo, a meno che non si tratti di un gruppo di amministratori di una collection o una community. In quel caso il nome e la descrizione del gruppo sono generati automaticamente e non possono essere modificati. Nelle sezioni successive è possibile modificare l'appartenenza al gruppo. Per maggiori dettagli, vedere [la wiki](https://wiki.lyrasis.org/display/DSDOC7x/Create+o+gestire+un+gruppo+utente)", // "admin.access-control.groups.form.tooltip.editGroup.addEpeople": "To add or remove an EPerson to/from this group, either click the 'Browse All' button or use the search bar below to search for users (use the dropdown to the left of the search bar to choose whether to search by metadata or by email). Then click the plus icon for each user you wish to add in the list below, or the trash can icon for each user you wish to remove. The list below may have several pages: use the page controls below the list to navigate to the next pages. Once you are ready, save your changes by clicking the 'Save' button in the top section.", - // TODO New key - Add a translation - "admin.access-control.groups.form.tooltip.editGroup.addEpeople": "To add or remove an EPerson to/from this group, either click the 'Browse All' button or use the search bar below to search for users (use the dropdown to the left of the search bar to choose whether to search by metadata or by email). Then click the plus icon for each user you wish to add in the list below, or the trash can icon for each user you wish to remove. The list below may have several pages: use the page controls below the list to navigate to the next pages. Once you are ready, save your changes by clicking the 'Save' button in the top section.", + "admin.access-control.groups.form.tooltip.editGroup.addEpeople": "Per aggiungere o rimuovere un EPerson a/da questo gruppo, cliccare sul pulsante 'Sfoglia tutti' o utilizzare la barra di ricerca in basso per cercare gli utenti (utilizzare il menu a tendina a sinistra della barra di ricerca per selezionare la ricerca per metadati o per e-mail). Cliccare quindi sull'icona + per ogni utente che si desidera aggiungere nell'elenco sottostante o sull'icona del cestino per ogni utente che si desidera rimuovere. L'elenco può avere diverse pagine: utilizzare i controlli di pagina in basso per spostarsi alle pagine successive. Per salvare le modifiche, cliccare sul pulsante 'Salvare' nella sezione in alto.", // "admin.access-control.groups.form.tooltip.editGroup.addSubgroups": "To add or remove a Subgroup to/from this group, either click the 'Browse All' button or use the search bar below to search for users. Then click the plus icon for each user you wish to add in the list below, or the trash can icon for each user you wish to remove. The list below may have several pages: use the page controls below the list to navigate to the next pages. Once you are ready, save your changes by clicking the 'Save' button in the top section.", - // TODO New key - Add a translation - "admin.access-control.groups.form.tooltip.editGroup.addSubgroups": "To add or remove a Subgroup to/from this group, either click the 'Browse All' button or use the search bar below to search for users. Then click the plus icon for each user you wish to add in the list below, or the trash can icon for each user you wish to remove. The list below may have several pages: use the page controls below the list to navigate to the next pages. Once you are ready, save your changes by clicking the 'Save' button in the top section.", + "admin.access-control.groups.form.tooltip.editGroup.addSubgroups": "Per aggiungere o rimuovere un Sottogruppo a/da questo gruppo, cliccare sul pulsante 'Sfoglia tutti' o utilizzare la barra di ricerca in basso per cercare gli utenti. Cliccare quindi sull'icona + per ogni utente che si desidera aggiungere nell'elenco sottostante o sull'icona del cestino per ogni utente che si desidera rimuovere. L'elenco può avere diverse pagine: utilizzare i controlli di pagina in basso per spostarsi alle pagine successive. Per salvare le modifiche, cliccare sul pulsante 'Salva' nella sezione in alto.", // "admin.notifications.openairebroker.breadcrumbs": "OpenAIRE Broker", "admin.notifications.openairebroker.breadcrumbs": "OpenAIRE Broker", @@ -1021,7 +1015,6 @@ "admin.workflow.item.workflow": "Flusso di lavoro", // "admin.workflow.item.workspace": "Workspace", - // TODO New key - Add a translation "admin.workflow.item.workspace": "Workspace", // "admin.workflow.item.delete": "Delete", @@ -1031,12 +1024,10 @@ "admin.workflow.item.send-back": "Rinviare", // "admin.workflow.item.policies": "Policies", - // TODO New key - Add a translation - "admin.workflow.item.policies": "Policies", + "admin.workflow.item.policies": "Policy", // "admin.workflow.item.supervision": "Supervision", - // TODO New key - Add a translation - "admin.workflow.item.supervision": "Supervision", + "admin.workflow.item.supervision": "Supervisione", @@ -1044,43 +1035,37 @@ "admin.metadata-import.breadcrumbs": "Importare metadati", // "admin.batch-import.breadcrumbs": "Import Batch", - // TODO New key - Add a translation - "admin.batch-import.breadcrumbs": "Import Batch", + "admin.batch-import.breadcrumbs": "Batch Import", // "admin.metadata-import.title": "Import Metadata", "admin.metadata-import.title": "Importare metadati", // "admin.batch-import.title": "Import Batch", - // TODO New key - Add a translation - "admin.batch-import.title": "Import Batch", + "admin.batch-import.title": "Batch Import", // "admin.metadata-import.page.header": "Import Metadata", "admin.metadata-import.page.header": "Importare metadati", // "admin.batch-import.page.header": "Import Batch", - // TODO New key - Add a translation - "admin.batch-import.page.header": "Import Batch", + "admin.batch-import.page.header": "Batch Import", // "admin.metadata-import.page.help": "You can drop or browse CSV files that contain batch metadata operations on files here", - "admin.metadata-import.page.help": "È possibile eliminare o sfogliare i file CSV che contengono operazioni di metadati batch sui file qui", + "admin.metadata-import.page.help": "È possibile rilasciare o sfogliare i file CSV che contengono operazioni di metadati batch sui file qui", // "admin.batch-import.page.help": "Select the Collection to import into. Then, drop or browse to a Simple Archive Format (SAF) zip file that includes the Items to import", - // TODO New key - Add a translation - "admin.batch-import.page.help": "Select the Collection to import into. Then, drop or browse to a Simple Archive Format (SAF) zip file that includes the Items to import", + "admin.batch-import.page.help": "Selezionare la Collection in cui effettuare l'import. Quindi, rilasciare o sfogliare un file zip Simple Archive Format (SAF) che include gli elementi da importare", // "admin.metadata-import.page.dropMsg": "Drop a metadata CSV to import", "admin.metadata-import.page.dropMsg": "Rilasciare un CSV di metadati da importare", // "admin.batch-import.page.dropMsg": "Drop a batch ZIP to import", - // TODO New key - Add a translation - "admin.batch-import.page.dropMsg": "Drop a batch ZIP to import", + "admin.batch-import.page.dropMsg": "Rilasciare un batch ZIP da importare", // "admin.metadata-import.page.dropMsgReplace": "Drop to replace the metadata CSV to import", - "admin.metadata-import.page.dropMsgReplace": "Rilascia per sostituire i metadati CSV da importare", + "admin.metadata-import.page.dropMsgReplace": "Rilasciare per sostituire i metadati CSV da importare", // "admin.batch-import.page.dropMsgReplace": "Drop to replace the batch ZIP to import", - // TODO New key - Add a translation - "admin.batch-import.page.dropMsgReplace": "Drop to replace the batch ZIP to import", + "admin.batch-import.page.dropMsgReplace": "Rilasciare per sostituire il batch ZIP da importare", // "admin.metadata-import.page.button.return": "Back", "admin.metadata-import.page.button.return": "Indietro", @@ -1089,15 +1074,13 @@ "admin.metadata-import.page.button.proceed": "Procedere", // "admin.metadata-import.page.button.select-collection": "Select Collection", - // TODO New key - Add a translation - "admin.metadata-import.page.button.select-collection": "Select Collection", + "admin.metadata-import.page.button.select-collection": "Selezionare una Collection", // "admin.metadata-import.page.error.addFile": "Select file first!", "admin.metadata-import.page.error.addFile": "Seleziona prima il file!", // "admin.batch-import.page.error.addFile": "Select Zip file first!", - // TODO New key - Add a translation - "admin.batch-import.page.error.addFile": "Select Zip file first!", + "admin.batch-import.page.error.addFile": "Seleziona prima il file ZIP!", // "admin.metadata-import.page.validateOnly": "Validate Only", "admin.metadata-import.page.validateOnly": "Solo Validazione", @@ -1106,138 +1089,105 @@ "admin.metadata-import.page.validateOnly.hint": "Una volta selezionato, il CSV caricato sarà validato. Riceverai un report delle modifiche rilevate, ma nessuna modifica verrà salvata.", // "advanced-workflow-action.rating.form.rating.label": "Rating", - // TODO New key - Add a translation - "advanced-workflow-action.rating.form.rating.label": "Rating", + "advanced-workflow-action.rating.form.rating.label": "Valutazione", // "advanced-workflow-action.rating.form.rating.error": "You must rate the item", - // TODO New key - Add a translation - "advanced-workflow-action.rating.form.rating.error": "You must rate the item", + "advanced-workflow-action.rating.form.rating.error": "È necessario valutare l'item", // "advanced-workflow-action.rating.form.review.label": "Review", - // TODO New key - Add a translation - "advanced-workflow-action.rating.form.review.label": "Review", + "advanced-workflow-action.rating.form.review.label": "Revisione", // "advanced-workflow-action.rating.form.review.error": "You must enter a review to submit this rating", - // TODO New key - Add a translation - "advanced-workflow-action.rating.form.review.error": "You must enter a review to submit this rating", + "advanced-workflow-action.rating.form.review.error": "Per inviare questa valutazione è necessario inserire una revisione", // "advanced-workflow-action.rating.description": "Please select a rating below", - // TODO New key - Add a translation - "advanced-workflow-action.rating.description": "Please select a rating below", + "advanced-workflow-action.rating.description": "Selezionare una valutazione qui sotto", // "advanced-workflow-action.rating.description-requiredDescription": "Please select a rating below and also add a review", - // TODO New key - Add a translation - "advanced-workflow-action.rating.description-requiredDescription": "Please select a rating below and also add a review", + "advanced-workflow-action.rating.description-requiredDescription": "Selezionare una valutazione qui sotto e aggiungere anche una revisione", // "advanced-workflow-action.select-reviewer.description-single": "Please select a single reviewer below before submitting", - // TODO New key - Add a translation - "advanced-workflow-action.select-reviewer.description-single": "Please select a single reviewer below before submitting", + "advanced-workflow-action.select-reviewer.description-single": "Selezionare un solo revisione prima di procedere con l'inserimento", // "advanced-workflow-action.select-reviewer.description-multiple": "Please select one or more reviewers below before submitting", - // TODO New key - Add a translation - "advanced-workflow-action.select-reviewer.description-multiple": "Please select one or more reviewers below before submitting", + "advanced-workflow-action.select-reviewer.description-multiple": "Selezionare uno o più revisori prima di procedere con l'inserimento", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.head": "EPeople", - // TODO New key - Add a translation "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.head": "EPeople", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.head": "Add EPeople", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.head": "Add EPeople", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.head": "Aggiungi EPeople", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.button.see-all": "Browse All", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.button.see-all": "Browse All", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.button.see-all": "Sfoglia tutto", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.headMembers": "Current Members", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.headMembers": "Current Members", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.headMembers": "Membri attuali", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.metadata": "Metadata", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.metadata": "Metadata", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.metadata": "Metadati", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.email": "E-mail (exact)", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.email": "E-mail (exact)", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.email": "E-mail (esatta)", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.button": "Search", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.button": "Search", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.button": "Ricerca", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.id": "ID", - // TODO New key - Add a translation "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.id": "ID", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.name": "Name", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.name": "Name", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.name": "Nome", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.identity": "Identity", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.identity": "Identity", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.identity": "Identità", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.email": "Email", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.email": "Email", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.email": "E-mail", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.netid": "NetID", - // TODO New key - Add a translation "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.netid": "NetID", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit": "Remove / Add", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit": "Remove / Add", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit": "Rimuovi / Aggiungi", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.remove": "Remove member with name \"{{name}}\"", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.remove": "Remove member with name \"{{name}}\"", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.remove": "Rimuovi membro con il nome \"{{name}}\"", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.addMember": "Successfully added member: \"{{name}}\"", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.addMember": "Successfully added member: \"{{name}}\"", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.addMember": "Membro inserito con successo: \"{{name}}\"", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.addMember": "Failed to add member: \"{{name}}\"", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.addMember": "Failed to add member: \"{{name}}\"", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.addMember": "Impossibile aggiungere il membro: \"{{name}}\"", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.deleteMember": "Successfully deleted member: \"{{name}}\"", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.deleteMember": "Successfully deleted member: \"{{name}}\"", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.deleteMember": "Membro eliminato con successo: \"{{name}}\"", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.deleteMember": "Failed to delete member: \"{{name}}\"", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.deleteMember": "Failed to delete member: \"{{name}}\"", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.deleteMember": "Impossibile eliminare il membro: \"{{name}}\"", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.add": "Add member with name \"{{name}}\"", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.add": "Add member with name \"{{name}}\"", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.add": "Aggiungi membro con il nome \"{{name}}\"", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.noActiveGroup": "No current active group, submit a name first.", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.noActiveGroup": "No current active group, submit a name first.", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.noActiveGroup": "Nessun gruppo attivo al momento, inserire prima un nome.", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-members-yet": "No members in group yet, search and add.", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-members-yet": "No members in group yet, search and add.", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-members-yet": "Questo gruppo è vuoto, cercare e aggiungere membri.", // "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-items": "No EPeople found in that search", - // TODO New key - Add a translation - "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-items": "No EPeople found in that search", + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-items": "La ricerca non ha trovato EPeople", // "advanced-workflow-action.select-reviewer.no-reviewer-selected.error": "No reviewer selected.", - // TODO New key - Add a translation - "advanced-workflow-action.select-reviewer.no-reviewer-selected.error": "No reviewer selected.", + "advanced-workflow-action.select-reviewer.no-reviewer-selected.error": "Nessun revisore selezionato.", // "admin.batch-import.page.validateOnly.hint": "When selected, the uploaded ZIP will be validated. You will receive a report of detected changes, but no changes will be saved.", - // TODO New key - Add a translation - "admin.batch-import.page.validateOnly.hint": "When selected, the uploaded ZIP will be validated. You will receive a report of detected changes, but no changes will be saved.", + "admin.batch-import.page.validateOnly.hint": "Una volta selezionato, il file ZIP caricato verrà convalidato. Si riceverà un rapporto sulle modifiche rilevate, ma non verrà salvata alcuna modifica.", // "admin.batch-import.page.remove": "remove", - // TODO New key - Add a translation - "admin.batch-import.page.remove": "remove", + "admin.batch-import.page.remove": "rimuovere", // "alert.close.aria": "Close", @@ -1548,8 +1498,7 @@ "pagination.next.button.disabled.tooltip": "Non ci sono ulteriori pagine di risultati", // "browse.startsWith": ", starting with {{ startsWith }}", - // TODO New key - Add a translation - "browse.startsWith": ", starting with {{ startsWith }}", + "browse.startsWith": ", inizia per {{ startsWith }}", // "browse.startsWith.choose_start": "(Choose start)", "browse.startsWith.choose_start": "(Scegli start)", @@ -1625,8 +1574,7 @@ // "search.browse.item-back": "Back to Results", - // TODO New key - Add a translation - "search.browse.item-back": "Back to Results", + "search.browse.item-back": "Torna ai risultati", @@ -1637,7 +1585,6 @@ "bulk-import.back": "Indietro", // "bulk-import.breadcrumbs" : "Bulk import", - // TODO New key - Add a translation "bulk-import.breadcrumbs" : "Bulk import", // "bulk-import.collection-name" : "Collection", @@ -1647,11 +1594,9 @@ "bulk-import.error": "Si è verificato un errore durante la creazione del processo di importazione", // "bulk-import.file" : "Source file", - // TODO New key - Add a translation "bulk-import.file" : "Source file", // "bulk-import.header" : "Bulk import", - // TODO New key - Add a translation "bulk-import.header" : "Bulk import", // "bulk-import.processing": "Processing...", @@ -1664,7 +1609,6 @@ "bulk-import.submit": "Avvia importazione", // "bulk-import.title" : "Bulk import", - // TODO New key - Add a translation "bulk-import.title" : "Bulk import", @@ -1676,23 +1620,20 @@ "chart.load-more": "Carica più risultati", // "claimed-approved-search-result-list-element.title": "Approved", - // TODO New key - Add a translation - "claimed-approved-search-result-list-element.title": "Approved", + "claimed-approved-search-result-list-element.title": "Approvato", // "claimed-declined-search-result-list-element.title": "Rejected, sent back to submitter", - // TODO New key - Add a translation - "claimed-declined-search-result-list-element.title": "Rejected, sent back to submitter", + "claimed-declined-search-result-list-element.title": "Respinto, rinviato al submitter", // "claimed-declined-task-search-result-list-element.title": "Declined, sent back to Review Manager's workflow", - // TODO New key - Add a translation - "claimed-declined-task-search-result-list-element.title": "Declined, sent back to Review Manager's workflow", + "claimed-declined-task-search-result-list-element.title": "Rifiutato, rinviato al flusso di lavoro del Responsabile delle revisioni", // "collection.create.head": "Create a Collection", "collection.create.head": "Creare una collection", // "collection.create.notifications.success": "Successfully created the Collection", - "collection.create.notifications.success": "Creata con successo la collection", + "collection.create.notifications.success": "Collection creata con successo", // "collection.create.sub-head": "Create a Collection for Community {{ parent }}", "collection.create.sub-head": "Creare una collection per la Community {{ parent }}", @@ -1849,8 +1790,7 @@ "collection.edit.item.authorizations.load-more-button": "Carica più risorse", // "collection.edit.item.authorizations.show-bitstreams-button": "Show bitstream policies for bundle", - // TODO New key - Add a translation - "collection.edit.item.authorizations.show-bitstreams-button": "Show bitstream policies for bundle", + "collection.edit.item.authorizations.show-bitstreams-button": "Mostra le policy del bitstream per il bundle", // "collection.edit.tabs.metadata.head": "Edit Metadata", "collection.edit.tabs.metadata.head": "Modifica metadati", @@ -1922,8 +1862,7 @@ "collection.edit.tabs.source.notifications.discarded.content": "Le modifiche sono state annullate. Per ripristinarle, fai clic sul pulsante \"Annulla\"", // "collection.edit.tabs.source.notifications.discarded.title": "Changes discarded", - // TODO Source message changed - Revise the translation - "collection.edit.tabs.source.notifications.discarded.title": "Modifiche eliminate", + "collection.edit.tabs.source.notifications.discarded.title": "Modifiche annullate", // "collection.edit.tabs.source.notifications.invalid.content": "Your changes were not saved. Please make sure all fields are valid before you save.", "collection.edit.tabs.source.notifications.invalid.content": "Le modifiche non sono state salvate. Assicurati che tutti i campi siano validi prima di salvare.", @@ -2105,16 +2044,12 @@ // "collection.source.controls.reset.running": "Resetting and reimporting...", "collection.source.controls.reset.running": "Reimpostazione e reimportazione...", // "collection.source.controls.harvest.status": "Harvest status:", - // TODO New key - Add a translation - "collection.source.controls.harvest.status": "Harvest status:", + "collection.source.controls.harvest.status": "Stato dell'harvest:", // "collection.source.controls.harvest.start": "Harvest start time:", - // TODO New key - Add a translation - "collection.source.controls.harvest.start": "Harvest start time:", + "collection.source.controls.harvest.start": "Ora di inizio dell'harvest:", // "collection.source.controls.harvest.last": "Last time harvested:", - // TODO New key - Add a translation - "collection.source.controls.harvest.last": "Last time harvested:", + "collection.source.controls.harvest.last": "Ulimo harvest effettuato:", // "collection.source.controls.harvest.message": "Harvest info:", - // TODO New key - Add a translation "collection.source.controls.harvest.message": "Harvest info:", // "collection.source.controls.harvest.no-information": "N/A", "collection.source.controls.harvest.no-information": "N/D", @@ -2341,12 +2276,10 @@ // "comcol-role.edit.scorereviewers.name": "Score Reviewers", - // TODO New key - Add a translation - "comcol-role.edit.scorereviewers.name": "Score Reviewers", + "comcol-role.edit.scorereviewers.name": "Valutatori", // "comcol-role.edit.scorereviewers.description": "Reviewers are able to give a score to incoming submissions, this will define whether the submission will be rejected or not.", - // TODO New key - Add a translation - "comcol-role.edit.scorereviewers.description": "Reviewers are able to give a score to incoming submissions, this will define whether the submission will be rejected or not.", + "comcol-role.edit.scorereviewers.description": "I valutatori possono assegnare un punteggio ai contributi in entrata, questo definirà se il contributo verrà rifiutato o meno.", @@ -2562,8 +2495,7 @@ // "checksum-tooltip.info.MD5": "The MD5 message-digest algorithm can be used to verify the integrity of the file that you have uploaded. You can calculate its value locally via tools that are generally available in each operative system like md5sum", - // TODO New key - Add a translation - "checksum-tooltip.info.MD5": "The MD5 message-digest algorithm can be used to verify the integrity of the file that you have uploaded. You can calculate its value locally via tools that are generally available in each operative system like md5sum", + "checksum-tooltip.info.MD5": "L'algoritmo MD5 message-digest algorithm può essere utilizzato per verificare l'integrità del file caricato. Puoi calcolarne il valore localmente tramite strumenti generalmente disponibili in ciascun sistema operativo come md5sum", // "cookies.consent.accept-all": "Accept all", "cookies.consent.accept-all": "Accetta tutto", @@ -2587,12 +2519,10 @@ "cookies.consent.app.required.title": "(sempre obbligatorio)", // "cookies.consent.app.disable-all.description": "Use this switch to enable or disable all services.", - // TODO New key - Add a translation - "cookies.consent.app.disable-all.description": "Use this switch to enable or disable all services.", + "cookies.consent.app.disable-all.description": "Utilizzare questo interruttore per abilitare o disabilitare tutti i servizi.", // "cookies.consent.app.disable-all.title": "Enable or disable all services", - // TODO New key - Add a translation - "cookies.consent.app.disable-all.title": "Enable or disable all services", + "cookies.consent.app.disable-all.title": "Abilitare o disabilitare tutti i servizi", // "cookies.consent.update": "There were changes since your last visit, please update your consent.", "cookies.consent.update": "Ci sono stati dei cambiamenti dal tuo ultimo accesso, aggiorna il tuo consenso.", @@ -2604,23 +2534,19 @@ "cookies.consent.decline": "Declinare", // "cookies.consent.ok": "That's ok", - // TODO New key - Add a translation - "cookies.consent.ok": "That's ok", + "cookies.consent.ok": "Accetta", // "cookies.consent.save": "Save", - // TODO New key - Add a translation - "cookies.consent.save": "Save", + "cookies.consent.save": "Salvare", // "cookies.consent.content-notice.title": "Cookie Consent", - // TODO New key - Add a translation - "cookies.consent.content-notice.title": "Cookie Consent", + "cookies.consent.content-notice.title": "Consenso ai cookie", // "cookies.consent.content-notice.description": "We collect and process your personal information for the following purposes: Authentication, Preferences, Acknowledgement and Statistics.
To learn more, please read our {privacyPolicy}.", - "cookies.consent.content-notice.description": "Raccogliamo e processiamo le tue informazioni personali per i seguenti scopi: Autenticazione, Preferenze, Accettazione e Statistiche.
Per saperne di più, leggi la nostra {privacyPolicy}.", + "cookies.consent.content-notice.description": "Raccogliamo ed elaboriamo le tue informazioni personali per i seguenti scopi: Autenticazione, Preferenze, Accettazione e Statistiche.
Per saperne di più, leggi la nostra {privacyPolicy}.", // "cookies.consent.content-notice.description.no-privacy": "We collect and process your personal information for the following purposes: Authentication, Preferences, Acknowledgement and Statistics.", - // TODO New key - Add a translation - "cookies.consent.content-notice.description.no-privacy": "We collect and process your personal information for the following purposes: Authentication, Preferences, Acknowledgement and Statistics.", + "cookies.consent.content-notice.description.no-privacy": "Raccogliamo ed elaboriamo le tue informazioni personali per i seguenti scopi: Autenticazione, Preferenze, Accettazione e Statistiche.", // "cookies.consent.content-notice.learnMore": "Customize", "cookies.consent.content-notice.learnMore": "Personalizza", @@ -2638,12 +2564,10 @@ "cookies.consent.content-modal.title": "Informazioni che raccogliamo", // "cookies.consent.content-modal.services": "services", - // TODO New key - Add a translation - "cookies.consent.content-modal.services": "services", + "cookies.consent.content-modal.services": "servizi", // "cookies.consent.content-modal.service": "service", - // TODO New key - Add a translation - "cookies.consent.content-modal.service": "service", + "cookies.consent.content-modal.service": "servizio", // "cookies.consent.app.title.authentication": "Authentication", "cookies.consent.app.title.authentication": "Autenticazione", @@ -2677,12 +2601,10 @@ // "cookies.consent.app.title.google-recaptcha": "Google reCaptcha", - // TODO New key - Add a translation "cookies.consent.app.title.google-recaptcha": "Google reCaptcha", // "cookies.consent.app.description.google-recaptcha": "We use google reCAPTCHA service during registration and password recovery", - // TODO New key - Add a translation - "cookies.consent.app.description.google-recaptcha": "We use google reCAPTCHA service during registration and password recovery", + "cookies.consent.app.description.google-recaptcha": "Utilizziamo il servizio Google reCAPTCHA nelle fasi di registrazione e recupero password", // "cookies.consent.purpose.functional": "Functional", @@ -2692,16 +2614,14 @@ "cookies.consent.purpose.statistical": "Statistico", // "cookies.consent.purpose.registration-password-recovery": "Registration and Password recovery", - // TODO New key - Add a translation - "cookies.consent.purpose.registration-password-recovery": "Registration and Password recovery", + "cookies.consent.purpose.registration-password-recovery": "Registrazione e recupero password", // "cookies.consent.purpose.sharing": "Sharing", "cookies.consent.purpose.sharing": "Condivisione", // "curation-task.task.citationpage.label": "Generate Citation Page", - // TODO New key - Add a translation - "curation-task.task.citationpage.label": "Generate Citation Page", + "curation-task.task.citationpage.label": "Genera una pagina di citazioni", // "cris-layout.toggle.open": "Open section", @@ -2723,12 +2643,10 @@ "cris-layout.advanced-attachment.size": "Dimensione", // "cris-layout.advanced-attachment.checksum": "Checksum", - // TODO New key - Add a translation "cris-layout.advanced-attachment.checksum": "Checksum", // "cris-layout.advanced-attachment.checksum.info.MD5": "The MD5 message-digest algorithm can be used to verify the integrity of the file that you have uploaded. You can calculate its value locally via tools that are generally available in each operative system like md5sum", - // TODO New key - Add a translation - "cris-layout.advanced-attachment.checksum.info.MD5": "The MD5 message-digest algorithm can be used to verify the integrity of the file that you have uploaded. You can calculate its value locally via tools that are generally available in each operative system like md5sum", + "cris-layout.advanced-attachment.checksum.info.MD5": "L'algoritmo MD5 message-digest algorithm può essere utilizzato per verificare l'integrità del file caricato. Puoi calcolarne il valore localmente tramite strumenti generalmente disponibili in ciascun sistema operativo come md5sum", // "cris-layout.advanced-attachment.format": "Format", "cris-layout.advanced-attachment.format": "Formato", @@ -2764,8 +2682,7 @@ "curation-task.task.profileformats.label": "Formati Bitstream del profilo", // "curation-task.task.registerdoi.label": "Mint DOI", - // TODO New key - Add a translation - "curation-task.task.registerdoi.label": "Mint DOI", + "curation-task.task.registerdoi.label": "Genera DOI", // "curation-task.task.requiredmetadata.label": "Check for Required Metadata", "curation-task.task.requiredmetadata.label": "Verifica la disponibilità di metadati richiesti", @@ -2777,8 +2694,7 @@ "curation-task.task.vscan.label": "Scansione antivirus", // "curation-task.task.register-doi.label": "Register DOI", - // TODO New key - Add a translation - "curation-task.task.register-doi.label": "Register DOI", + "curation-task.task.register-doi.label": "Registra DOI", @@ -2846,8 +2762,7 @@ "dso-selector.create.community.head": "Nuova Community", // "dso-selector.create.community.or-divider": "or", - // TODO New key - Add a translation - "dso-selector.create.community.or-divider": "or", + "dso-selector.create.community.or-divider": "oppure", // "dso-selector.create.community.sub-level": "Create a new community in", "dso-selector.create.community.sub-level": "Creare una nuova community in", @@ -2880,12 +2795,10 @@ "dso-selector.export-metadata.dspaceobject.head": "Esportare metadati da", // "dso-selector.export-batch.dspaceobject.head": "Export Batch (ZIP) from", - // TODO New key - Add a translation - "dso-selector.export-batch.dspaceobject.head": "Export Batch (ZIP) from", + "dso-selector.export-batch.dspaceobject.head": "Esporta batch (ZIP) da", // "dso-selector.import-batch.dspaceobject.head": "Import batch from", - // TODO New key - Add a translation - "dso-selector.import-batch.dspaceobject.head": "Import batch from", + "dso-selector.import-batch.dspaceobject.head": "Importa batch da", // "dso-selector.import-item.item.head": "Import items", "dso-selector.import-item.item.head": "Importare item", @@ -2912,8 +2825,7 @@ "dso-selector.set-scope.community.button": "Cerca in tutto DSpace", // "dso-selector.set-scope.community.or-divider": "or", - // TODO New key - Add a translation - "dso-selector.set-scope.community.or-divider": "or", + "dso-selector.set-scope.community.or-divider": "oppure", // "dso-selector.set-scope.community.input-header": "Search for a community or collection", "dso-selector.set-scope.community.input-header": "Cercare una community o una collection", @@ -2934,60 +2846,46 @@ "dso-selector.export-item.sub-level": "Esporta in item Excel in", // "dso-selector.results-could-not-be-retrieved": "Something went wrong, please refresh again ↻", - // TODO New key - Add a translation - "dso-selector.results-could-not-be-retrieved": "Something went wrong, please refresh again ↻", + "dso-selector.results-could-not-be-retrieved": "Qualcosa è andato storto, aggiorna di nuovo ↻", // "supervision-group-selector.header": "Supervision Group Selector", - // TODO New key - Add a translation - "supervision-group-selector.header": "Supervision Group Selector", + "supervision-group-selector.header": "Selezione del gruppo di supervisione", // "supervision-group-selector.select.type-of-order.label": "Select a type of Order", - // TODO New key - Add a translation - "supervision-group-selector.select.type-of-order.label": "Select a type of Order", + "supervision-group-selector.select.type-of-order.label": "Seleziona un tipo di ordine", // "supervision-group-selector.select.type-of-order.option.none": "NONE", - // TODO New key - Add a translation - "supervision-group-selector.select.type-of-order.option.none": "NONE", + "supervision-group-selector.select.type-of-order.option.none": "NESSUNO", // "supervision-group-selector.select.type-of-order.option.editor": "EDITOR", - // TODO New key - Add a translation - "supervision-group-selector.select.type-of-order.option.editor": "EDITOR", + "supervision-group-selector.select.type-of-order.option.editor": "EDITORE", // "supervision-group-selector.select.type-of-order.option.observer": "OBSERVER", - // TODO New key - Add a translation - "supervision-group-selector.select.type-of-order.option.observer": "OBSERVER", + "supervision-group-selector.select.type-of-order.option.observer": "OSSERVATORE", // "supervision-group-selector.select.group.label": "Select a Group", - // TODO New key - Add a translation - "supervision-group-selector.select.group.label": "Select a Group", + "supervision-group-selector.select.group.label": "Seleziona un gruppo", // "supervision-group-selector.button.cancel": "Cancel", - // TODO New key - Add a translation - "supervision-group-selector.button.cancel": "Cancel", + "supervision-group-selector.button.cancel": "Annulla", // "supervision-group-selector.button.save": "Save", - // TODO New key - Add a translation - "supervision-group-selector.button.save": "Save", + "supervision-group-selector.button.save": "Salva", // "supervision-group-selector.select.type-of-order.error": "Please select a type of order", - // TODO New key - Add a translation - "supervision-group-selector.select.type-of-order.error": "Please select a type of order", + "supervision-group-selector.select.type-of-order.error": "Seleziona un tipo di ordine", // "supervision-group-selector.select.group.error": "Please select a group", - // TODO New key - Add a translation - "supervision-group-selector.select.group.error": "Please select a group", + "supervision-group-selector.select.group.error": "Seleziona un gruppo", // "supervision-group-selector.notification.create.success.title": "Successfully created supervision order for group {{ name }}", - // TODO New key - Add a translation - "supervision-group-selector.notification.create.success.title": "Successfully created supervision order for group {{ name }}", + "supervision-group-selector.notification.create.success.title": "Ordine di supervisione per il gruppo {{ name }} creato con successo", // "supervision-group-selector.notification.create.failure.title": "Error", - // TODO New key - Add a translation - "supervision-group-selector.notification.create.failure.title": "Error", + "supervision-group-selector.notification.create.failure.title": "Errore", // "supervision-group-selector.notification.create.already-existing" : "A supervision order already exists on this item for selected group", - // TODO New key - Add a translation - "supervision-group-selector.notification.create.already-existing" : "A supervision order already exists on this item for selected group", + "supervision-group-selector.notification.create.already-existing" : "Per questo item esiste già un ordine di supervisione per il gruppo selezionato", // "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}", "confirmation-modal.export-metadata.header": "Esportare i metadati per {{ dsoName }}", @@ -3002,20 +2900,16 @@ "confirmation-modal.export-metadata.confirm": "Export", // "confirmation-modal.export-batch.header": "Export batch (ZIP) for {{ dsoName }}", - // TODO New key - Add a translation - "confirmation-modal.export-batch.header": "Export batch (ZIP) for {{ dsoName }}", + "confirmation-modal.export-batch.header": "Esporta batch (ZIP) per {{ dsoName }}", // "confirmation-modal.export-batch.info": "Are you sure you want to export batch (ZIP) for {{ dsoName }}", - // TODO New key - Add a translation - "confirmation-modal.export-batch.info": "Are you sure you want to export batch (ZIP) for {{ dsoName }}", + "confirmation-modal.export-batch.info": "Sei sicuro di voler esportare batch (ZIP) per {{ dsoName }}?", // "confirmation-modal.export-batch.cancel": "Cancel", - // TODO New key - Add a translation - "confirmation-modal.export-batch.cancel": "Cancel", + "confirmation-modal.export-batch.cancel": "Annulla", // "confirmation-modal.export-batch.confirm": "Export", - // TODO New key - Add a translation - "confirmation-modal.export-batch.confirm": "Export", + "confirmation-modal.export-batch.confirm": "Esporta", // "confirmation-modal.delete-eperson.header": "Delete EPerson \"{{ dsoName }}\"", "confirmation-modal.delete-eperson.header": "Elimina EPerson \"{{ dsoName }}\"", @@ -3042,7 +2936,6 @@ "confirmation-modal.delete-profile.confirm": "Elimina", // "confirmation-modal.delete-subscription.header": "Delete Subscription", - // TODO Source message changed - Revise the translation "confirmation-modal.delete-subscription.header": "Elimina iscrizione per \"{{ dsoName }}\"", // "confirmation-modal.delete-subscription.info": "Are you sure you want to delete subscription for \"{{ dsoName }}\"", @@ -3134,11 +3027,10 @@ "error.validation.custom-url.invalid-characters": "L'URL personalizzato contiene caratteri non validi.", // "error.validation.license.notgranted": "You must grant this license to complete your submission. If you are unable to grant this license at this time you may save your work and return later or remove the submission.", - "error.validation.license.notgranted": "È necessario concedere questa licenza per completare la submission. Se non sei in grado di concedere questa licenza in questo momento, puoi salvare il tuo lavoro e tornare più tardi o rimuovere la submission.", + "error.validation.license.notgranted": "È necessario concedere questa licenza per completare l'immisione. Se non sei in grado di concedere questa licenza in questo momento, puoi salvare il tuo lavoro e tornare più tardi o rimuovere l'immissione.", // "error.validation.pattern": "This input is restricted by the current pattern: {{ pattern }}.", - // TODO New key - Add a translation - "error.validation.pattern": "This input is restricted by the current pattern: {{ pattern }}.", + "error.validation.pattern": "L'input è limitato dal pattern in uso: {{ pattern }}.", // "error.validation.filerequired": "The file upload is mandatory", "error.validation.filerequired": "Il caricamento del file è obbligatorio", @@ -3156,94 +3048,74 @@ "error.validation.groupExists": "Gruppo già esistente", // "error.validation.fundingInvestigatorOrLeadOrganizationRequired" : "At least one investigator or one lead organization is required", - // TODO New key - Add a translation - "error.validation.fundingInvestigatorOrLeadOrganizationRequired" : "At least one investigator or one lead organization is required", + "error.validation.fundingInvestigatorOrLeadOrganizationRequired" : "È richiesto almeno un ricercatore o una unità principale", // "error.validation.notRepeatable": "This field is not repeatable, please choose only one value and discard the others.", - // TODO New key - Add a translation - "error.validation.notRepeatable": "This field is not repeatable, please choose only one value and discard the others.", + "error.validation.notRepeatable": "Questo campo non è ripetibile, scegliere un solo valore e scartare gli altri.", // "event.listelement.badge" : "Event", - // TODO New key - Add a translation - "event.listelement.badge" : "Event", + "event.listelement.badge" : "Evento", // "explore.browse-section.title" : "Browse", - // TODO New key - Add a translation - "explore.browse-section.title" : "Browse", + "explore.browse-section.title" : "Sfoglia", // "explore.counters-section.rprofiles" : "People", - // TODO New key - Add a translation - "explore.counters-section.rprofiles" : "People", + "explore.counters-section.rprofiles" : "Persone", // "explore.counters-section.publications" : "Research outputs", "explore.counters-section.publications" : "Output della ricerca", // "explore.counters-section.project_funding" : "Projects", - // TODO New key - Add a translation - "explore.counters-section.project_funding" : "Projects", + "explore.counters-section.project_funding" : "Progetti", // "explore.facet-section.title" : "Discover", - // TODO New key - Add a translation - "explore.facet-section.title" : "Discover", + "explore.facet-section.title" : "Scopri", // "explore.index.all" : "All", - // TODO New key - Add a translation - "explore.index.all" : "All", + "explore.index.all" : "Tutti", // "explore.index.author" : "Author", - // TODO New key - Add a translation - "explore.index.author" : "Author", + "explore.index.author" : "Autore", // "explore.index.graphitemtype" : "Graph by type", - // TODO New key - Add a translation - "explore.index.graphitemtype" : "Graph by type", + "explore.index.graphitemtype" : "Grafico per tipo", // "explore.index.graphpubldate" : "Graph by date", - // TODO New key - Add a translation - "explore.index.graphpubldate" : "Graph by date", + "explore.index.graphpubldate" : "Grafico per data", // "explore.index.birthDate" : "Birthday", - // TODO New key - Add a translation - "explore.index.birthDate" : "Birthday", + "explore.index.birthDate" : "Data di nascita", // "explore.index.metric.view" : "Most viewed", - // TODO New key - Add a translation - "explore.index.metric.view" : "Most viewed", + "explore.index.metric.view" : "Più visitati", // "explore.index.metric.download" : "Most downloaded", - // TODO New key - Add a translation - "explore.index.metric.download" : "Most downloaded", + "explore.index.metric.download" : "Più scaricati", // "explore.index.metric.scopus.citation" : "Most cited", - // TODO New key - Add a translation - "explore.index.metric.scopus.citation" : "Most cited", + "explore.index.metric.scopus.citation" : "Più citati", // "explore.index.chart.pie.itemtype_filter" : "Item Type", - // TODO New key - Add a translation - "explore.index.chart.pie.itemtype_filter" : "Item Type", + "explore.index.chart.pie.itemtype_filter" : "Tipo di item", // "explore.index.chart.bar.dateIssued.year": "Year", "explore.index.chart.bar.dateIssued.year": "Anno", // "explore.index.dateIssued" : "Date issued", - // TODO New key - Add a translation - "explore.index.dateIssued" : "Date issued", + "explore.index.dateIssued" : "Data di inserimento", // "explore.index.dateissued" : "Date issued", - // TODO New key - Add a translation - "explore.index.dateissued" : "Date issued", + "explore.index.dateissued" : "Data di inserimento", // "explore.index.dc.date.accessioned" : "Recent Additions", - // TODO New key - Add a translation - "explore.index.dc.date.accessioned" : "Recent Additions", + "explore.index.dc.date.accessioned" : "Aggiunte recenti", // "explore.index.dc.title" : "Title", "explore.index.dc.title" : "Titolo", // "explore.index.editor" : "Editor", - // TODO New key - Add a translation - "explore.index.editor" : "Editor", + "explore.index.editor" : "Editore", // "explore.index.eqtitle" : "Title", "explore.index.eqtitle" : "Titolo", @@ -3264,8 +3136,7 @@ "explore.index.funding" : "Finanziamento", // "explore.index.givenName" : "Given name", - // TODO New key - Add a translation - "explore.index.givenName" : "Given name", + "explore.index.givenName" : "Nome", // "explore.index.has_content_in_original_bundle" : "Has content in original bundle", "explore.index.has_content_in_original_bundle" : "Sono presenti dei contenuti nel bundle original", @@ -3319,8 +3190,7 @@ "explore.index.itemidentifier" : "Identificativo dell'item", // "explore.index.jobTitle" : "Job title", - // TODO New key - Add a translation - "explore.index.jobTitle" : "Job title", + "explore.index.jobTitle" : "Ruolo", // "explore.index.knowsLanguage" : "Knows languages", "explore.index.knowsLanguage" : "Lingue conosciute", @@ -3410,12 +3280,10 @@ "explore.infrastructure.breadcrumbs": "Infrastrutture", // "explore.fundings.breadcrumbs": "Fundings", - // TODO Source message changed - Revise the translation "explore.fundings.breadcrumbs": "Progetti", // "explore.projects.breadcrumbs": "Projects", - // TODO New key - Add a translation - "explore.projects.breadcrumbs": "Projects", + "explore.projects.breadcrumbs": "Progetti", // "explore.fundings_and_projects.breadcrumbs": "Projects", "explore.fundings_and_projects.breadcrumbs": "Progetti", @@ -3437,7 +3305,6 @@ // "feed.description": "Syndication feed", - // TODO New key - Add a translation "feed.description": "Syndication feed", @@ -3484,8 +3351,7 @@ "forgot-email.form.header": "Password dimenticata", // "forgot-email.form.info": "Enter the email address associated with the account.", - // TODO Source message changed - Revise the translation - "forgot-email.form.info": "Accedi a registra un account per iscriverti alle collections, per ricevere aggiornamenti via email e per poter inserire nuovi item in DSpace.", + "forgot-email.form.info": "Inserisci l'indirizzo email associato all'account.", // "forgot-email.form.email": "Email Address *", "forgot-email.form.email": "Indirizzo email *", @@ -3494,31 +3360,25 @@ "forgot-email.form.email.error.required": "Si prega di inserire un indirizzo email", // "forgot-email.form.email.error.not-email-form": "Please fill in a valid email address", - // TODO New key - Add a translation - "forgot-email.form.email.error.not-email-form": "Please fill in a valid email address", + "forgot-email.form.email.error.not-email-form": "Si prega di inserire un idirizzo email valido", // "forgot-email.form.email.hint": "An email will be sent to this address with a further instructions.", - // TODO Source message changed - Revise the translation - "forgot-email.form.email.hint": "Questo indirizzo verrà verificato e utilizzato come login name.", + "forgot-email.form.email.hint": "Una email con ulteriori indicazioni sarà invita a questo indrizzo.", // "forgot-email.form.submit": "Reset password", - // TODO Source message changed - Revise the translation - "forgot-email.form.submit": "Salva", + "forgot-email.form.submit": "Reimposta la password", // "forgot-email.form.success.head": "Password reset email sent", - // TODO Source message changed - Revise the translation - "forgot-email.form.success.head": "Email di verifica inviata", + "forgot-email.form.success.head": "Email di reimpostazione password inviata", // "forgot-email.form.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", "forgot-email.form.success.content": "È stata inviata un'email a {{ email }} contenente un URL speciale e ulteriori indicazioni.", // "forgot-email.form.error.head": "Error when trying to reset password", - // TODO Source message changed - Revise the translation - "forgot-email.form.error.head": "Errore durante il tentativo di registrare l'email", + "forgot-email.form.error.head": "Errore durante il tentativo di reimpostare la password", // "forgot-email.form.error.content": "An error occured when attempting to reset the password for the account associated with the following email address: {{ email }}", - // TODO New key - Add a translation - "forgot-email.form.error.content": "An error occured when attempting to reset the password for the account associated with the following email address: {{ email }}", + "forgot-email.form.error.content": "Si è verificato un errore durante il tentativo di reimpostare la password per l'account associato al seguente indirizzo email: {{ email }}", @@ -3529,8 +3389,7 @@ "forgot-password.form.head": "Password dimenticata", // "forgot-password.form.info": "Enter a new password in the box below, and confirm it by typing it again into the second box.", - // TODO Source message changed - Revise the translation - "forgot-password.form.info": "Inserisci una nuova password nella casella qui sotto e confermala digitandola di nuovo nella seconda casella. Dovrebbe avere una lunghezza di almeno sei caratteri.", + "forgot-password.form.info": "Inserisci una nuova password nella casella qui sotto e confermala digitandola di nuovo nella seconda casella.", // "forgot-password.form.card.security": "Security", "forgot-password.form.card.security": "Sicurezza", @@ -3752,49 +3611,39 @@ // "health.breadcrumbs": "Health", - // TODO New key - Add a translation "health.breadcrumbs": "Health", // "health-page.heading" : "Health", - // TODO New key - Add a translation "health-page.heading" : "Health", // "health-page.info-tab" : "Info", - // TODO New key - Add a translation - "health-page.info-tab" : "Info", + "health-page.info-tab" : "Informazioni", // "health-page.status-tab" : "Status", - // TODO New key - Add a translation - "health-page.status-tab" : "Status", + "health-page.status-tab" : "Stato", // "health-page.error.msg": "The health check service is temporarily unavailable", "health-page.error.msg": "Il servizio di health check è temporaneamente non disponibile.", // "health-page.property.status": "Status code", - // TODO New key - Add a translation - "health-page.property.status": "Status code", + "health-page.property.status": "Codice di stato", // "health-page.section.db.title": "Database", "health-page.section.db.title": "Database", // "health-page.section.geoIp.title": "GeoIp", - // TODO New key - Add a translation "health-page.section.geoIp.title": "GeoIp", // "health-page.section.solrAuthorityCore.title": "Solr: authority core", - // TODO New key - Add a translation "health-page.section.solrAuthorityCore.title": "Solr: authority core", // "health-page.section.solrOaiCore.title": "Solr: oai core", - // TODO New key - Add a translation "health-page.section.solrOaiCore.title": "Solr: oai core", // "health-page.section.solrSearchCore.title": "Solr: search core", - // TODO New key - Add a translation "health-page.section.solrSearchCore.title": "Solr: search core", // "health-page.section.solrStatisticsCore.title": "Solr: statistics core", - // TODO New key - Add a translation "health-page.section.solrStatisticsCore.title": "Solr: statistics core", // "health-page.section-info.app.title": "Application Backend", @@ -3804,12 +3653,10 @@ "health-page.section-info.java.title": "Java", // "health-page.status": "Status", - // TODO New key - Add a translation - "health-page.status": "Status", + "health-page.status": "Stato", // "health-page.status.ok.info": "Operational", - // TODO New key - Add a translation - "health-page.status.ok.info": "Operational", + "health-page.status.ok.info": "Operativo", // "health-page.status.error.info": "Problems detected", "health-page.status.error.info": "Sono stati rilevati dei problemi", @@ -3818,7 +3665,6 @@ "health-page.status.warning.info": "Sono stati rilevati dei potenziali problemi", // "health-page.title": "Health", - // TODO New key - Add a translation "health-page.title": "Health", // "health-page.section.no-issues": "No issues detected", @@ -3939,7 +3785,6 @@ // "item.badge.private": "Non-discoverable", - // TODO Source message changed - Revise the translation "item.badge.private": "Privato", // "item.badge.withdrawn": "Withdrawn", @@ -3951,7 +3796,6 @@ "item.bitstreams.upload.bundle": "Bundle", // "item.bitstreams.upload.bundle.placeholder": "Select a bundle or input new bundle name", - // TODO Source message changed - Revise the translation "item.bitstreams.upload.bundle.placeholder": "Seleziona un bundle o inserisci il nome di un nuovo bundle", // "item.bitstreams.upload.bundle.new": "Create bundle", @@ -4110,88 +3954,67 @@ "item.edit.tabs.item-mapper.title": "Modifica item - Mapper Collection", // "item.edit.identifiers.doi.status.UNKNOWN": "Unknown", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.UNKNOWN": "Unknown", + "item.edit.identifiers.doi.status.UNKNOWN": "Sconosciuto", // "item.edit.identifiers.doi.status.TO_BE_REGISTERED": "Queued for registration", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.TO_BE_REGISTERED": "Queued for registration", + "item.edit.identifiers.doi.status.TO_BE_REGISTERED": "In coda per la registrazione", // "item.edit.identifiers.doi.status.TO_BE_RESERVED": "Queued for reservation", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.TO_BE_RESERVED": "Queued for reservation", + "item.edit.identifiers.doi.status.TO_BE_RESERVED": "In coda per l'assegnazione", // "item.edit.identifiers.doi.status.IS_REGISTERED": "Registered", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.IS_REGISTERED": "Registered", + "item.edit.identifiers.doi.status.IS_REGISTERED": "Registrato", // "item.edit.identifiers.doi.status.IS_RESERVED": "Reserved", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.IS_RESERVED": "Reserved", + "item.edit.identifiers.doi.status.IS_RESERVED": "Assegnato", // "item.edit.identifiers.doi.status.UPDATE_RESERVED": "Reserved (update queued)", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.UPDATE_RESERVED": "Reserved (update queued)", + "item.edit.identifiers.doi.status.UPDATE_RESERVED": "Assegnato (aggiornamento in coda)", // "item.edit.identifiers.doi.status.UPDATE_REGISTERED": "Registered (update queued)", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.UPDATE_REGISTERED": "Registered (update queued)", + "item.edit.identifiers.doi.status.UPDATE_REGISTERED": "Registrato (aggiornamento in coda)", // "item.edit.identifiers.doi.status.UPDATE_BEFORE_REGISTRATION": "Queued for update and registration", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.UPDATE_BEFORE_REGISTRATION": "Queued for update and registration", + "item.edit.identifiers.doi.status.UPDATE_BEFORE_REGISTRATION": "In coda per aggiornamento e registrazione", // "item.edit.identifiers.doi.status.TO_BE_DELETED": "Queued for deletion", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.TO_BE_DELETED": "Queued for deletion", + "item.edit.identifiers.doi.status.TO_BE_DELETED": "In coda per l'eliminazione", // "item.edit.identifiers.doi.status.DELETED": "Deleted", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.DELETED": "Deleted", + "item.edit.identifiers.doi.status.DELETED": "Eliminato", // "item.edit.identifiers.doi.status.PENDING": "Pending (not registered)", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.PENDING": "Pending (not registered)", + "item.edit.identifiers.doi.status.PENDING": "In sospeso (non registrato)", // "item.edit.identifiers.doi.status.MINTED": "Minted (not registered)", - // TODO New key - Add a translation - "item.edit.identifiers.doi.status.MINTED": "Minted (not registered)", + "item.edit.identifiers.doi.status.MINTED": "Creato (non registrato)", // "item.edit.tabs.status.buttons.register-doi.label": "Register a new or pending DOI", - // TODO New key - Add a translation - "item.edit.tabs.status.buttons.register-doi.label": "Register a new or pending DOI", + "item.edit.tabs.status.buttons.register-doi.label": "Registra un nuovo DOI o un DOI in sospeso", // "item.edit.tabs.status.buttons.register-doi.button": "Register DOI...", - // TODO New key - Add a translation - "item.edit.tabs.status.buttons.register-doi.button": "Register DOI...", + "item.edit.tabs.status.buttons.register-doi.button": "Registra DOI...", // "item.edit.register-doi.header": "Register a new or pending DOI", - // TODO New key - Add a translation - "item.edit.register-doi.header": "Register a new or pending DOI", + "item.edit.register-doi.header": "Registra un nuovo DOI o un DOI in sospeso", // "item.edit.register-doi.description": "Review any pending identifiers and item metadata below and click Confirm to proceed with DOI registration, or Cancel to back out", - // TODO New key - Add a translation - "item.edit.register-doi.description": "Review any pending identifiers and item metadata below and click Confirm to proceed with DOI registration, or Cancel to back out", + "item.edit.register-doi.description": "Esaminare gli identificatori e i metadati dell'item in sospeso e cliccare su Conferma per procedere con la registrazione DOI, oppure su Annulla per tornare indietro", // "item.edit.register-doi.confirm": "Confirm", - // TODO New key - Add a translation - "item.edit.register-doi.confirm": "Confirm", + "item.edit.register-doi.confirm": "Conferma", // "item.edit.register-doi.cancel": "Cancel", - // TODO New key - Add a translation - "item.edit.register-doi.cancel": "Cancel", + "item.edit.register-doi.cancel": "Annulla", // "item.edit.register-doi.success": "DOI queued for registration successfully.", - // TODO New key - Add a translation - "item.edit.register-doi.success": "DOI queued for registration successfully.", + "item.edit.register-doi.success": "DOI in coda per la registrazione.", // "item.edit.register-doi.error": "Error registering DOI", - // TODO New key - Add a translation - "item.edit.register-doi.error": "Error registering DOI", + "item.edit.register-doi.error": "Errore nella registrazione del DOI", // "item.edit.register-doi.to-update": "The following DOI has already been minted and will be queued for registration online", - // TODO New key - Add a translation - "item.edit.register-doi.to-update": "The following DOI has already been minted and will be queued for registration online", + "item.edit.register-doi.to-update": "Il seguente DOI è stato già creato e sarà inserito in coda per la registrazione online", // "item.edit.item-mapper.buttons.add": "Map item to selected collections", "item.edit.item-mapper.buttons.add": "Mappare l'item nelle collections selezionate", @@ -4256,12 +4079,10 @@ "item.edit.metadata.discard-button": "Annulla", // "item.edit.metadata.edit.buttons.confirm": "Confirm", - // TODO New key - Add a translation - "item.edit.metadata.edit.buttons.confirm": "Confirm", + "item.edit.metadata.edit.buttons.confirm": "Conferma", // "item.edit.metadata.edit.buttons.drag": "Drag to reorder", - // TODO New key - Add a translation - "item.edit.metadata.edit.buttons.drag": "Drag to reorder", + "item.edit.metadata.edit.buttons.drag": "Trascina per riordinare", // "item.edit.metadata.edit.buttons.edit": "Edit", "item.edit.metadata.edit.buttons.edit": "Edita", @@ -4276,8 +4097,7 @@ "item.edit.metadata.edit.buttons.unedit": "Interrompi la modifica", // "item.edit.metadata.edit.buttons.virtual": "This is a virtual metadata value, i.e. a value inherited from a related entity. It can’t be modified directly. Add or remove the corresponding relationship in the \"Relationships\" tab", - // TODO New key - Add a translation - "item.edit.metadata.edit.buttons.virtual": "This is a virtual metadata value, i.e. a value inherited from a related entity. It can’t be modified directly. Add or remove the corresponding relationship in the \"Relationships\" tab", + "item.edit.metadata.edit.buttons.virtual": "Si tratta di un valore virtuale di metadati, cioè un valore ereditato da un'entità correlata. Non può essere modificato direttamente. Aggiungere o rimuovere la relazione corrispondente nella scheda 'Relazioni'.", // "item.edit.metadata.empty": "The item currently doesn't contain any metadata. Click Add to start adding a metadata value.", "item.edit.metadata.empty": "Attualmente l'item non contiene metadati. Fai clic su Aggiungi per iniziare ad aggiungere il valore di un metadata.", @@ -4298,8 +4118,7 @@ "item.edit.metadata.headers.value": "Valore", // "item.edit.metadata.metadatafield.error": "An error occurred validating the metadata field", - // TODO New key - Add a translation - "item.edit.metadata.metadatafield.error": "An error occurred validating the metadata field", + "item.edit.metadata.metadatafield.error": "Si è verificato un errore nella validazione del campo dei metadati", // "item.edit.metadata.metadatafield.invalid": "Please choose a valid metadata field", "item.edit.metadata.metadatafield.invalid": "Scegli un campo di metadati valido", @@ -4308,7 +4127,6 @@ "item.edit.metadata.notifications.discarded.content": "Le modifiche sono state annullate. Per ripristinarle, fai clic sul pulsante \"Annulla\"", // "item.edit.metadata.notifications.discarded.title": "Changes discarded", - // TODO Source message changed - Revise the translation "item.edit.metadata.notifications.discarded.title": "Modifiche eliminate", // "item.edit.metadata.notifications.error.title": "An error occurred", @@ -4324,7 +4142,6 @@ "item.edit.metadata.notifications.outdated.content": "L'item su cui stai attualmente lavorando è stato modificato da un altro utente. Le modifiche correnti verranno eliminate per evitare conflitti", // "item.edit.metadata.notifications.outdated.title": "Changes outdated", - // TODO Source message changed - Revise the translation "item.edit.metadata.notifications.outdated.title": "Modifiche obsolete", // "item.edit.metadata.notifications.saved.content": "Your changes to this item's metadata were saved.", @@ -4337,8 +4154,7 @@ "item.edit.metadata.reinstate-button": "Annulla", // "item.edit.metadata.reset-order-button": "Undo reorder", - // TODO New key - Add a translation - "item.edit.metadata.reset-order-button": "Undo reorder", + "item.edit.metadata.reset-order-button": "Annulla il riordinamento", // "item.edit.metadata.save-button": "Save", "item.edit.metadata.save-button": "Salva", @@ -4401,16 +4217,13 @@ "item.edit.private.cancel": "Annulla", // "item.edit.private.confirm": "Make it non-discoverable", - // TODO Source message changed - Revise the translation "item.edit.private.confirm": "Rendilo privato", // "item.edit.private.description": "Are you sure this item should be made non-discoverable in the archive?", - // TODO Source message changed - Revise the translation - "item.edit.private.description": "Sei sicuro che questo oggetto debba essere reso privato nell'archivio?", + "item.edit.private.description": "Sei sicuro che questo item debba essere reso privato nell'archivio?", // "item.edit.private.error": "An error occurred while making the item non-discoverable", - // TODO Source message changed - Revise the translation - "item.edit.private.error": "Si è verificato un errore durante la creazione di un item privato", + "item.edit.private.error": "Si è verificato un errore durante la modifica dell'item in privato", // "item.edit.private.header": "Make item non-discoverable: {{ id }}", "item.edit.private.header": "Rendi privato l'item: {{ id }}", @@ -4424,22 +4237,18 @@ "item.edit.public.cancel": "Annulla", // "item.edit.public.confirm": "Make it discoverable", - // TODO Source message changed - Revise the translation "item.edit.public.confirm": "Rendilo pubblico", // "item.edit.public.description": "Are you sure this item should be made discoverable in the archive?", - // TODO Source message changed - Revise the translation - "item.edit.public.description": "Sei sicuro che questo articolo debba essere reso pubblico nell'archivio?", + "item.edit.public.description": "Sei sicuro che questo item debba essere reso pubblico nell'archivio?", // "item.edit.public.error": "An error occurred while making the item discoverable", - // TODO Source message changed - Revise the translation - "item.edit.public.error": "Si è verificato un errore durante la creazione di un item pubblico", + "item.edit.public.error": "Si è verificato un errore durante la modifica dell'item in pubblico", // "item.edit.public.header": "Make item discoverable: {{ id }}", "item.edit.public.header": "Rendi pubblico l'item: {{ id }}", // "item.edit.public.success": "The item is now discoverable", - // TODO Source message changed - Revise the translation "item.edit.public.success": "L'item è ora pubblico", @@ -4557,26 +4366,21 @@ "item.edit.tabs.status.buttons.mappedCollections.label": "Gestire le collections mappate", // "item.edit.tabs.status.buttons.move.button": "Move this Item to a different Collection", - // TODO Source message changed - Revise the translation - "item.edit.tabs.status.buttons.move.button": "Sposta...", + "item.edit.tabs.status.buttons.move.button": "Sposta questo item in un'altra collection...", // "item.edit.tabs.status.buttons.move.label": "Move item to another collection", "item.edit.tabs.status.buttons.move.label": "Spostare l'item in un'altra collection", // "item.edit.tabs.status.buttons.private.button": "Make it non-discoverable...", - // TODO Source message changed - Revise the translation - "item.edit.tabs.status.buttons.private.button": "...", + "item.edit.tabs.status.buttons.private.button": "Rendilo privato...", // "item.edit.tabs.status.buttons.private.label": "Make item non-discoverable", - // TODO Source message changed - Revise the translation "item.edit.tabs.status.buttons.private.label": "Rendere l'item privato", // "item.edit.tabs.status.buttons.public.button": "Make it discoverable...", - // TODO Source message changed - Revise the translation - "item.edit.tabs.status.buttons.public.button": "...", + "item.edit.tabs.status.buttons.public.button": "Rendilo pubblico...", // "item.edit.tabs.status.buttons.public.label": "Make item discoverable", - // TODO Source message changed - Revise the translation "item.edit.tabs.status.buttons.public.label": "Rendere l'item pubblico", // "item.edit.tabs.status.buttons.reinstate.button": "Reinstate...", @@ -4589,8 +4393,7 @@ "item.edit.tabs.status.buttons.unauthorized": "Non sei autorizzato a eseguire questa azione", // "item.edit.tabs.status.buttons.withdraw.button": "Withdraw this item", - // TODO Source message changed - Revise the translation - "item.edit.tabs.status.buttons.withdraw.button": "Rimozione...", + "item.edit.tabs.status.buttons.withdraw.button": "Rimuovi questo item", // "item.edit.tabs.status.buttons.withdraw.label": "Withdraw item from the repository", "item.edit.tabs.status.buttons.withdraw.label": "Rimuovere l'item dal repository", @@ -4720,36 +4523,28 @@ "item.truncatable-part.show-less": "Riduci", // "workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order", - // TODO New key - Add a translation - "workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order", + "workflow-item.search.result.delete-supervision.modal.header": "Elimina l'ordine di supervisione", // "workflow-item.search.result.delete-supervision.modal.info": "Are you sure you want to delete Supervision Order", - // TODO New key - Add a translation - "workflow-item.search.result.delete-supervision.modal.info": "Are you sure you want to delete Supervision Order", + "workflow-item.search.result.delete-supervision.modal.info": "Sei sicuro di voler eliminare l'ordine di supervisione?", // "workflow-item.search.result.delete-supervision.modal.cancel": "Cancel", - // TODO New key - Add a translation - "workflow-item.search.result.delete-supervision.modal.cancel": "Cancel", + "workflow-item.search.result.delete-supervision.modal.cancel": "Annulla", // "workflow-item.search.result.delete-supervision.modal.confirm": "Delete", - // TODO New key - Add a translation - "workflow-item.search.result.delete-supervision.modal.confirm": "Delete", + "workflow-item.search.result.delete-supervision.modal.confirm": "Elimina", // "workflow-item.search.result.notification.deleted.success": "Successfully deleted supervision order \"{{name}}\"", - // TODO New key - Add a translation - "workflow-item.search.result.notification.deleted.success": "Successfully deleted supervision order \"{{name}}\"", + "workflow-item.search.result.notification.deleted.success": "Ordine di supervisione \"{{name}}\" eliminato con successo", // "workflow-item.search.result.notification.deleted.failure": "Failed to delete supervision order \"{{name}}\"", - // TODO New key - Add a translation - "workflow-item.search.result.notification.deleted.failure": "Failed to delete supervision order \"{{name}}\"", + "workflow-item.search.result.notification.deleted.failure": "Impossibile eliminare l'ordine di supervisione \"{{name}}\"", // "workflow-item.search.result.list.element.supervised-by": "Supervised by:", - // TODO New key - Add a translation - "workflow-item.search.result.list.element.supervised-by": "Supervised by:", + "workflow-item.search.result.list.element.supervised-by": "Supervisionato da:", // "workflow-item.search.result.list.element.supervised.remove-tooltip": "Remove supervision group", - // TODO New key - Add a translation - "workflow-item.search.result.list.element.supervised.remove-tooltip": "Remove supervision group", + "workflow-item.search.result.list.element.supervised.remove-tooltip": "Rimuovi gruppo di supervisione", @@ -4902,11 +4697,9 @@ "item.page.bitstreams.collapse": "Riduci", // "item.page.filesection.original.bundle" : "Original bundle", - // TODO New key - Add a translation "item.page.filesection.original.bundle" : "Original bundle", // "item.page.filesection.license.bundle" : "License bundle", - // TODO New key - Add a translation "item.page.filesection.license.bundle" : "License bundle", // "item.page.return": "Back", @@ -4955,8 +4748,7 @@ "item.preview.dc.publisher" : "Editore", // "item.preview.dc.relation.grantno" : "Grant no.", - // TODO New key - Add a translation - "item.preview.dc.relation.grantno" : "Grant no.", + "item.preview.dc.relation.grantno" : "Numero di finanziamento", // "item.preview.dc.relation.ispartofseries" : "Serie", "item.preview.dc.relation.ispartofseries" : "Serie", @@ -4965,8 +4757,7 @@ "item.preview.dc.identifier.uri": "Identificativo:", // "item.preview.dc.contributor.advisor" : "Advisor", - // TODO New key - Add a translation - "item.preview.dc.contributor.advisor" : "Advisor", + "item.preview.dc.contributor.advisor" : "Consulente", // "item.preview.dc.contributor.author": "Authors:", "item.preview.dc.contributor.author": "Autori:", @@ -4975,8 +4766,7 @@ "item.preview.dc.contributor.applicant": "Richiedente", // "item.preview.dc.contributor.editor": "Editors:", - // TODO New key - Add a translation - "item.preview.dc.contributor.editor": "Editors:", + "item.preview.dc.contributor.editor": "Editori:", // "item.preview.dc.date.embargoEnd" : "Embargo End", "item.preview.dc.date.embargoEnd" : "Fine embargo", @@ -4988,14 +4778,12 @@ "item.preview.dc.description.abstract": "Abstract:", // "item.preview.dc.description.sponsorship" : "Sponsorship", - // TODO New key - Add a translation "item.preview.dc.description.sponsorship" : "Sponsorship", // "item.preview.dc.identifier.other": "Other identifier:", "item.preview.dc.identifier.other": "Altri identificativi:", // "item.preview.dc.identifier.pmid" : "Pubmed ID", - // TODO New key - Add a translation "item.preview.dc.identifier.pmid" : "Pubmed ID", // "item.preview.dc.language.iso": "Language:", @@ -5011,8 +4799,7 @@ "item.preview.dc.rights.uri" : "URI Diritti", // "item.preview.dc.subject": "Subjects:", - // TODO New key - Add a translation - "item.preview.dc.subject": "Subjects:", + "item.preview.dc.subject": "Soggetti:", // "item.preview.dc.title": "Title:", "item.preview.dc.title": "Titolo:", @@ -5072,8 +4859,7 @@ "item.preview.oairecerif.funder" : "Finanziatore", // "item.preview.oairecerif.funding.identifier" : "Grant Number / Funding identifier", - // TODO New key - Add a translation - "item.preview.oairecerif.funding.identifier" : "Grant Number / Funding identifier", + "item.preview.oairecerif.funding.identifier" : "Numero di finanziamento / Identificativo di finanziamento", // "item.preview.oairecerif.funding.endDate" : "End Date", "item.preview.oairecerif.funding.endDate" : "Data finale", @@ -5280,23 +5066,19 @@ "item-export.modal-launcher.multiple.btn": "Export", // "item-export.alert.single": "Export item {{title}}", - // TODO Source message changed - Revise the translation - "item-export.alert.single": "Esporta item ", + "item-export.alert.single": "Esporta item {{title}}", // "item-export.alert.multiple": "Export all items of a particular entity type belonging to the current search.", "item-export.alert.multiple": "Esportare tutti gli item di una determinata entità compresi nella ricerca corrente.", // "item-export.alert.multiple-with-type": "Export all items with type {{type}} belonging to the current search.", - // TODO New key - Add a translation - "item-export.alert.multiple-with-type": "Export all items with type {{type}} belonging to the current search.", + "item-export.alert.multiple-with-type": "Esporta tutti gli item del tipo {{type}} che fanno parte della ricerca corrente.", // "item-export.alert.export.limit": "This export will be limited to a maximum of {{limit}} items.", - // TODO New key - Add a translation - "item-export.alert.export.limit": "This export will be limited to a maximum of {{limit}} items.", + "item-export.alert.export.limit": "Sarà possibile esportare solo un massimo di {{limit}} item.", // "item-export.cannot-msg": "There are no items to export with the selected configuration", - // TODO New key - Add a translation - "item-export.cannot-msg": "There are no items to export with the selected configuration", + "item-export.cannot-msg": "Non ci sono item da esportare con la configurazione selezionata", // "item-export.form.entityType": "Entity Type", "item-export.form.entityType": "Tipo di entità", @@ -5317,131 +5099,100 @@ "item-export.form.btn.cancel": "Annulla", // "item-export.form.selection": "Export mode", - // TODO New key - Add a translation - "item-export.form.selection": "Export mode", + "item-export.form.selection": "Modalità di export", // "item-export.form.selection.all": "Export all", - // TODO New key - Add a translation - "item-export.form.selection.all": "Export all", + "item-export.form.selection.all": "Esporta tutti", // "item-export.form.selection.only-selected": "Export only selected", - // TODO New key - Add a translation - "item-export.form.selection.only-selected": "Export only selected", + "item-export.form.selection.only-selected": "Esporta solo gli item selezionati", // "item-export.modal.title": "Item Export Process Launcher", - // TODO New key - Add a translation - "item-export.modal.title": "Item Export Process Launcher", + "item-export.modal.title": "Avvio del processo di esportazione degli item", // "item-export.process.title": "Bulk export item", "item-export.process.title": "Bulk export dell'item", // "itemtemplate.edit.metadata.add-button": "Add", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.add-button": "Add", + "itemtemplate.edit.metadata.add-button": "Aggiungi", // "itemtemplate.edit.metadata.discard-button": "Discard", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.discard-button": "Discard", + "itemtemplate.edit.metadata.discard-button": "Elimina", // "itemtemplate.edit.metadata.edit.buttons.confirm": "Confirm", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.edit.buttons.confirm": "Confirm", + "itemtemplate.edit.metadata.edit.buttons.confirm": "Conferma", // "itemtemplate.edit.metadata.edit.buttons.drag": "Drag to reorder", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.edit.buttons.drag": "Drag to reorder", + "itemtemplate.edit.metadata.edit.buttons.drag": "Trascina per riordinare", // "itemtemplate.edit.metadata.edit.buttons.edit": "Edit", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.edit.buttons.edit": "Edit", + "itemtemplate.edit.metadata.edit.buttons.edit": "Modifica", // "itemtemplate.edit.metadata.edit.buttons.remove": "Remove", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.edit.buttons.remove": "Remove", + "itemtemplate.edit.metadata.edit.buttons.remove": "Rimuovi", // "itemtemplate.edit.metadata.edit.buttons.undo": "Undo changes", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.edit.buttons.undo": "Undo changes", + "itemtemplate.edit.metadata.edit.buttons.undo": "Annulla le modifiche", // "itemtemplate.edit.metadata.edit.buttons.unedit": "Stop editing", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.edit.buttons.unedit": "Stop editing", + "itemtemplate.edit.metadata.edit.buttons.unedit": "Smetti di modificare", // "itemtemplate.edit.metadata.empty": "The item template currently doesn't contain any metadata. Click Add to start adding a metadata value.", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.empty": "The item template currently doesn't contain any metadata. Click Add to start adding a metadata value.", + "itemtemplate.edit.metadata.empty": "Il template dell'item al momento non contiene nessun metadato. Clicca su 'Aggiungi' per inserire un metadato.", // "itemtemplate.edit.metadata.headers.edit": "Edit", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.headers.edit": "Edit", + "itemtemplate.edit.metadata.headers.edit": "Modifica", // "itemtemplate.edit.metadata.headers.field": "Field", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.headers.field": "Field", + "itemtemplate.edit.metadata.headers.field": "Campo", // "itemtemplate.edit.metadata.headers.language": "Lang", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.headers.language": "Lang", + "itemtemplate.edit.metadata.headers.language": "Lingua", // "itemtemplate.edit.metadata.headers.value": "Value", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.headers.value": "Value", + "itemtemplate.edit.metadata.headers.value": "Valore", // "itemtemplate.edit.metadata.metadatafield.error": "An error occurred validating the metadata field", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.metadatafield.error": "An error occurred validating the metadata field", + "itemtemplate.edit.metadata.metadatafield.error": "Si è verificato un errore durante la validazione del campo dei metadati", // "itemtemplate.edit.metadata.metadatafield.invalid": "Please choose a valid metadata field", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.metadatafield.invalid": "Please choose a valid metadata field", + "itemtemplate.edit.metadata.metadatafield.invalid": "Si prega di scegliere un campo di metadati valido", // "itemtemplate.edit.metadata.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", + "itemtemplate.edit.metadata.notifications.discarded.content": "Le tue modifiche sono state eliminate. Per riprtinarle, cliccare sul tasto 'Annulla'", // "itemtemplate.edit.metadata.notifications.discarded.title": "Changed discarded", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.discarded.title": "Changed discarded", + "itemtemplate.edit.metadata.notifications.discarded.title": "Modifiche eliminate", // "itemtemplate.edit.metadata.notifications.error.title": "An error occurred", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.error.title": "An error occurred", + "itemtemplate.edit.metadata.notifications.error.title": "Si è verificato un errore", // "itemtemplate.edit.metadata.notifications.invalid.content": "Your changes were not saved. Please make sure all fields are valid before you save.", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.invalid.content": "Your changes were not saved. Please make sure all fields are valid before you save.", + "itemtemplate.edit.metadata.notifications.invalid.content": "Le tue modifiche non sono state salvate. Assicurati che tutti i campi siano validi prima di salvare.", // "itemtemplate.edit.metadata.notifications.invalid.title": "Metadata invalid", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.invalid.title": "Metadata invalid", + "itemtemplate.edit.metadata.notifications.invalid.title": "Metadati non validi", // "itemtemplate.edit.metadata.notifications.outdated.content": "The item template you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.outdated.content": "The item template you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts", + "itemtemplate.edit.metadata.notifications.outdated.content": "Il template dell'item su cui stai lavorando è stato modificato da un altro utente. Le tue ultime modifiche sono state eliminate per prevenire conflitti", // "itemtemplate.edit.metadata.notifications.outdated.title": "Changed outdated", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.outdated.title": "Changed outdated", + "itemtemplate.edit.metadata.notifications.outdated.title": "Modifiche obsolete", // "itemtemplate.edit.metadata.notifications.saved.content": "Your changes to this item template's metadata were saved.", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.saved.content": "Your changes to this item template's metadata were saved.", + "itemtemplate.edit.metadata.notifications.saved.content": "Le tue modifiche ai metadati di questo template sono state salvate.", // "itemtemplate.edit.metadata.notifications.saved.title": "Metadata saved", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.notifications.saved.title": "Metadata saved", + "itemtemplate.edit.metadata.notifications.saved.title": "Metadati salvati", // "itemtemplate.edit.metadata.reinstate-button": "Undo", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.reinstate-button": "Undo", + "itemtemplate.edit.metadata.reinstate-button": "Annulla", // "itemtemplate.edit.metadata.reset-order-button": "Undo reorder", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.reset-order-button": "Undo reorder", + "itemtemplate.edit.metadata.reset-order-button": "Annulla riordinamento", // "itemtemplate.edit.metadata.save-button": "Save", - // TODO New key - Add a translation - "itemtemplate.edit.metadata.save-button": "Save", + "itemtemplate.edit.metadata.save-button": "Salva", @@ -5676,12 +5427,10 @@ "manage.relationships.unhide": "Mostrare", // "manage.relationships.search-list.title" : "All your items", - // TODO New key - Add a translation - "manage.relationships.search-list.title" : "All your items", + "manage.relationships.search-list.title" : "Tutti i tuoi item", // "manage.relationships.selected-list.title" : "Selected items", - // TODO New key - Add a translation - "manage.relationships.selected-list.title" : "Selected items", + "manage.relationships.selected-list.title" : "Item selezionati", // "manage.relationships.no.relationships.found" : "Empty selection", "manage.relationships.no.relationships.found" : "Selezione vuota", @@ -5690,28 +5439,22 @@ "manage.relationships.no.data.found" : "Nessun dato trovato", // "manage.relationships.error.select" : "An unexpected error occurs while selecting the entity.", - // TODO New key - Add a translation - "manage.relationships.error.select" : "An unexpected error occurs while selecting the entity.", + "manage.relationships.error.select" : "Si è verificato un errore inaspettato durante la selezione dell'entità.", // "manage.relationships.error.unselect" : "An unexpected error occurs while unselecting the entity.", - // TODO New key - Add a translation - "manage.relationships.error.unselect" : "An unexpected error occurs while unselecting the entity.", + "manage.relationships.error.unselect" : "Si è verificato un errore inaspettato durante la deselezione dell'entità.", // "manage.relationships.error.hide" : "An unexpected error occurs while hiding the entity.", - // TODO New key - Add a translation - "manage.relationships.error.hide" : "An unexpected error occurs while hiding the entity.", + "manage.relationships.error.hide" : "Si è verificato un errore inaspettato mentre si nascondeva l'entità", // "manage.relationships.error.unhide" : "An unexpected error occurs while exposing the entity.", - // TODO New key - Add a translation - "manage.relationships.error.unhide" : "An unexpected error occurs while exposing the entity.", + "manage.relationships.error.unhide" : "Si è verificato un errore inaspettato durante l'esposizione dell'entità.", // "manage.relationships.error.sort" : "An unexpected error occurs while sorting entities.", - // TODO New key - Add a translation - "manage.relationships.error.sort" : "An unexpected error occurs while sorting entities.", + "manage.relationships.error.sort" : "Si è verificato un errore inaspettato durante l'ordinamento delle entità.", // "manage.relationships.hidden-related-items-alert" : "Please note that hidden related items will always be visible among Item’s relations to administrators and owner of the Item", - // TODO New key - Add a translation - "manage.relationships.hidden-related-items-alert" : "Please note that hidden related items will always be visible among Item’s relations to administrators and owner of the Item", + "manage.relationships.hidden-related-items-alert" : "Si prega di notare che gli item correlati nascosti saranno sempre visibili tra le relazioni degli item agli amministratori e al proprietario dell'item", // "menu.header.admin": "Management", @@ -5774,7 +5517,6 @@ "menu.section.explore_publications": "Output della ricerca", // "menu.section.explore_researchoutputs": "Research Outputs", - // TODO Source message changed - Revise the translation "menu.section.explore_researchoutputs": "Output della ricerca", // "menu.section.explore_researcherprofiles": "People", @@ -5784,16 +5526,13 @@ "menu.section.explore_orgunits": "Strutture", // "menu.section.explore_fundings": "Fundings", - // TODO Source message changed - Revise the translation "menu.section.explore_fundings": "Progetti", // "menu.section.explore_projects": "Projects", - // TODO New key - Add a translation - "menu.section.explore_projects": "Projects", + "menu.section.explore_projects": "Progetti", // "menu.section.explore_fundings_and_projects": "Fundings & Projects", - // TODO Source message changed - Revise the translation - "menu.section.explore_fundings_and_projects": "Progetti", + "menu.section.explore_fundings_and_projects": "Finanziamenti & progetti", // "menu.section.communities_and_collections": "Communities & Collections", "menu.section.communities_and_collections": "Community & Collezioni", @@ -5838,14 +5577,12 @@ "menu.section.export_metadata": "Metadati", // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation "menu.section.export_batch": "Batch Export (ZIP)", // "menu.section.import_from_excel": "Import from excel", "menu.section.import_from_excel": "Importa da Excel", // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation "menu.section.export_batch": "Batch Export (ZIP)", // "menu.section.export_to_excel": "Export to excel", @@ -5876,8 +5613,7 @@ "menu.section.icon.find": "Trova sezione menu", // "menu.section.icon.health": "Health check menu section", - // TODO New key - Add a translation - "menu.section.icon.health": "Health check menu section", + "menu.section.icon.health": "Sezione del menu Health check", // "menu.section.icon.import": "Import menu section", "menu.section.icon.import": "Sezione del menu Importa", @@ -5892,8 +5628,7 @@ "menu.section.icon.pin": "Fissa la barra laterale", // "menu.section.icon.processes": "Processes Health", - // TODO Source message changed - Revise the translation - "menu.section.icon.processes": "Sezione del menu Processi", + "menu.section.icon.processes": "Processi", // "menu.section.icon.registries": "Registries menu section", "menu.section.icon.registries": "Sezione del menu Registri", @@ -6044,12 +5779,10 @@ "statistics.workflow.page.current-table.reviewstep" : "Riesamina", // "statistics.workflow.page.current-table.editstep" : "Edit", - // TODO New key - Add a translation - "statistics.workflow.page.current-table.editstep" : "Edit", + "statistics.workflow.page.current-table.editstep" : "Modifica", // "statistics.workflow.page.current-table.finaleditstep" : "Final edit", - // TODO New key - Add a translation - "statistics.workflow.page.current-table.finaleditstep" : "Final edit", + "statistics.workflow.page.current-table.finaleditstep" : "Modifica finale", // "statistics.workflow.page.current-table.step": "Step", "statistics.workflow.page.current-table.step": "Passo", @@ -6310,8 +6043,7 @@ "mydspace.results.no-uri": "Nessun URI", // "mydspace.results.is-correction": "Is a request of correction", - // TODO New key - Add a translation - "mydspace.results.is-correction": "Is a request of correction", + "mydspace.results.is-correction": "È una richiesta di correzione", // "mydspace.search-form.placeholder": "Search in mydspace...", "mydspace.search-form.placeholder": "Cerca in mydspace...", @@ -6323,12 +6055,10 @@ "mydspace.show.workspace": "I tuoi contributi", // "mydspace.show.supervisedWorkspace": "Supervised items", - // TODO New key - Add a translation - "mydspace.show.supervisedWorkspace": "Supervised items", + "mydspace.show.supervisedWorkspace": "Item supervisionati", // "mydspace.show.otherworkspace": "Other Workspace Submissions", - // TODO New key - Add a translation - "mydspace.show.otherworkspace": "Other Workspace Submissions", + "mydspace.show.otherworkspace": "Altri insierimenti in workspace", // "mydspace.status.archived": "Archived", "mydspace.status.archived": "Archiviati", @@ -6379,8 +6109,7 @@ "nav.community-browse.header": "Per Community", // "nav.context-help-toggle": "Toggle context help", - // TODO New key - Add a translation - "nav.context-help-toggle": "Toggle context help", + "nav.context-help-toggle": "Attivare la guida contestuale", // "nav.language": "Language switch", "nav.language": "Cambio di lingua", @@ -6389,12 +6118,10 @@ "nav.login": "Accedi", // "nav.user-profile-menu-and-logout": "User profile menu and Log Out", - // TODO New key - Add a translation - "nav.user-profile-menu-and-logout": "User profile menu and Log Out", + "nav.user-profile-menu-and-logout": "Menu profilo utente e Disconnetti", // "nav.logout": "Log Out", - // TODO Source message changed - Revise the translation - "nav.logout": "Menu profilo utente e Disconnetti", + "nav.logout": "Disconnetti", // "nav.main.description": "Main navigation bar", "nav.main.description": "Barra di navigazione principale", @@ -6415,23 +6142,19 @@ "nav.stop-impersonating": "Smetti di impersonare EPerson", // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", + "nav.subscriptions" : "Subscription", // "nav.toggle" : "Toggle navigation", - // TODO New key - Add a translation - "nav.toggle" : "Toggle navigation", + "nav.toggle" : "Attivare la navigazione", // "nav.user.description" : "User profile bar", - // TODO New key - Add a translation - "nav.user.description" : "User profile bar", + "nav.user.description" : "Barra del profilo utente", // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", + "nav.subscriptions" : "Subscription", // "none.listelement.badge": "Item", - "none.listelement.badge": "Articolo", + "none.listelement.badge": "Item", // "openaire.broker.title": "OpenAIRE Broker", "openaire.broker.title": "OpenAIRE Broker", @@ -6470,8 +6193,7 @@ "openaire.broker.events.description": "Di seguito l'elenco di tutti i suggerimenti ricevuti da OpenAIRE per l'argomento selezionato.", // "openaire.broker.events.topic": "Topic:", - // TODO New key - Add a translation - "openaire.broker.events.topic": "Topic:", + "openaire.broker.events.topic": "Argomento:", // "openaire.broker.noEvents": "No suggestions found.", "openaire.broker.noEvents": "Nessun suggerimento trovato.", @@ -6504,16 +6226,13 @@ "openaire.broker.event.action.import": "Importa progetto e accetta suggerimenti", // "openaire.broker.event.table.pidtype": "PID Type:", - // TODO New key - Add a translation - "openaire.broker.event.table.pidtype": "PID Type:", + "openaire.broker.event.table.pidtype": "Tipo di PID", // "openaire.broker.event.table.pidvalue": "PID Value:", - // TODO New key - Add a translation - "openaire.broker.event.table.pidvalue": "PID Value:", + "openaire.broker.event.table.pidvalue": "Valore di PID:", // "openaire.broker.event.table.subjectValue": "Subject Value:", - // TODO New key - Add a translation - "openaire.broker.event.table.subjectValue": "Subject Value:", + "openaire.broker.event.table.subjectValue": "Valore del soggetto:", // "openaire.broker.event.table.abstract": "Abstract:", "openaire.broker.event.table.abstract": "Abstract:", @@ -6522,28 +6241,22 @@ "openaire.broker.event.table.suggestedProject": "Dati del progetto suggerito da OpenAIRE", // "openaire.broker.event.table.project": "Project title:", - // TODO New key - Add a translation - "openaire.broker.event.table.project": "Project title:", + "openaire.broker.event.table.project": "Titolo del progetto:", // "openaire.broker.event.table.acronym": "Acronym:", - // TODO New key - Add a translation - "openaire.broker.event.table.acronym": "Acronym:", + "openaire.broker.event.table.acronym": "Acronimo:", // "openaire.broker.event.table.code": "Code:", - // TODO New key - Add a translation - "openaire.broker.event.table.code": "Code:", + "openaire.broker.event.table.code": "Codice:", // "openaire.broker.event.table.funder": "Funder:", - // TODO New key - Add a translation - "openaire.broker.event.table.funder": "Funder:", + "openaire.broker.event.table.funder": "Ente finanziatore:", // "openaire.broker.event.table.fundingProgram": "Funding program:", - // TODO New key - Add a translation - "openaire.broker.event.table.fundingProgram": "Funding program:", + "openaire.broker.event.table.fundingProgram": "Programma di finanziamento:", // "openaire.broker.event.table.jurisdiction": "Jurisdiction:", - // TODO New key - Add a translation - "openaire.broker.event.table.jurisdiction": "Jurisdiction:", + "openaire.broker.event.table.jurisdiction": "Giurisdizione:", // "openaire.broker.events.back": "Back to topics", "openaire.broker.events.back": "Torna agli argomenti", @@ -6555,8 +6268,7 @@ "openaire.broker.event.table.more": "Mostra di più", // "openaire.broker.event.project.found": "Bound to the local record:", - // TODO New key - Add a translation - "openaire.broker.event.project.found": "Bound to the local record:", + "openaire.broker.event.project.found": "Legato al record locale:", // "openaire.broker.event.project.notFound": "No local record found", "openaire.broker.event.project.notFound": "Nessun record locale trovato", @@ -6586,12 +6298,10 @@ "openaire.broker.event.modal.project.title": "Scegliere un progetto da rilegare", // "openaire.broker.event.modal.project.publication": "Publication:", - // TODO New key - Add a translation - "openaire.broker.event.modal.project.publication": "Publication:", + "openaire.broker.event.modal.project.publication": "Pubblicazione:", // "openaire.broker.event.modal.project.bountToLocal": "Bound to the local record:", - // TODO New key - Add a translation - "openaire.broker.event.modal.project.bountToLocal": "Bound to the local record:", + "openaire.broker.event.modal.project.bountToLocal": "Legato al record locale:", // "openaire.broker.event.modal.project.select": "Project search", "openaire.broker.event.modal.project.select": "Ricerca del progetto", @@ -6702,8 +6412,7 @@ "reciter.suggestion.hideEvidence": "Nascondi le prove", // "reciter.suggestion.not-found": "No suggestion found with the given identifier.", - // TODO New key - Add a translation - "reciter.suggestion.not-found": "No suggestion found with the given identifier.", + "reciter.suggestion.not-found": "Non è stato trovato alcun suggerimento con l'identificativo indicato", // "reciter.suggestion.suggestionFor": "Suggestion for", "reciter.suggestion.suggestionFor": "Suggerimento per", @@ -6724,16 +6433,14 @@ "reciter.suggestion.type.oaire": "Pubblicazioni", // "reciter.suggestion.type.orcidWorks": "Publications", - // TODO New key - Add a translation - "reciter.suggestion.type.orcidWorks": "Publications", + "reciter.suggestion.type.orcidWorks": "Pubblicazioni", // "orgunit.listelement.badge": "Organizational Unit", "orgunit.listelement.badge": "Unità organizzativa", // "orgunit.listelement.no-title": "Untitled", - // TODO New key - Add a translation - "orgunit.listelement.no-title": "Untitled", + "orgunit.listelement.no-title": "Senza titolo", // "orgunit.page.city": "City", "orgunit.page.city": "Città", @@ -6754,8 +6461,7 @@ "orgunit.page.id": "ID", // "orgunit.page.titleprefix": "Organizational Unit: ", - // TODO New key - Add a translation - "orgunit.page.titleprefix": "Organizational Unit: ", + "orgunit.page.titleprefix": "Unità organizzativa: ", @@ -6801,8 +6507,7 @@ "person.page.lastname": "Cognome", // "person.page.name": "Name", - // TODO New key - Add a translation - "person.page.name": "Name", + "person.page.name": "Nome", // "person.page.link.full": "Show all metadata", "person.page.link.full": "Mostra tutti i metadati", @@ -6817,11 +6522,10 @@ "person.page.titleprefix": "Persona: ", // "person.search.results.head": "Person Search Results", - "person.search.results.head": "Risultati della ricerca per Ricercatore", + "person.search.results.head": "Risultati della ricerca per persona", // "person-relationships.search.results.head": "Person Search Results", - // TODO New key - Add a translation - "person-relationships.search.results.head": "Person Search Results", + "person-relationships.search.results.head": "Risultati della ricerca per persona", // "person.search.title": "Person Search", "person.search.title": "Cerca i Ricercatori", @@ -6862,8 +6566,7 @@ "process.new.parameter.type.file": "file", // "process.new.parameter.required.missing": "The following parameters are required but still missing:", - // TODO New key - Add a translation - "process.new.parameter.required.missing": "The following parameters are required but still missing:", + "process.new.parameter.required.missing": "I seguenti parametri mancanti sono obbligatori:", // "process.new.notification.success.title": "Success", "process.new.notification.success.title": "Successo", @@ -6881,8 +6584,7 @@ "process.new.notification.process.processing": "Elaborazione...", // "process.new.notification.process.files": "Output Files: ", - // TODO New key - Add a translation - "process.new.notification.process.files": "Output Files: ", + "process.new.notification.process.files": "File di output: ", // "process.new.header": "Create a new process", "process.new.header": "Creare un nuovo processo", @@ -6896,20 +6598,16 @@ // "process.detail.arguments" : "Arguments", - // TODO New key - Add a translation - "process.detail.arguments" : "Arguments", + "process.detail.arguments" : "Parametri", // "process.detail.arguments.empty" : "This process doesn't contain any arguments", - // TODO New key - Add a translation - "process.detail.arguments.empty" : "This process doesn't contain any arguments", + "process.detail.arguments.empty" : "Questo processo non contiene alcun parametro", // "process.detail.back" : "Back", - // TODO New key - Add a translation - "process.detail.back" : "Back", + "process.detail.back" : "Indietro", // "process.detail.output" : "Process Output", - // TODO New key - Add a translation - "process.detail.output" : "Process Output", + "process.detail.output" : "Output del processo", // "process.detail.logs.button": "Retrieve process output", "process.detail.logs.button": "Recupera l'output del processo", @@ -6921,100 +6619,76 @@ "process.detail.logs.none": "Questo processo non ha output", // "process.detail.output-files" : "Output Files", - // TODO New key - Add a translation - "process.detail.output-files" : "Output Files", + "process.detail.output-files" : "File di output", // "process.detail.output-files.empty" : "This process doesn't contain any output files", - // TODO New key - Add a translation - "process.detail.output-files.empty" : "This process doesn't contain any output files", + "process.detail.output-files.empty" : "Questo processo non contiene nessun file di output", // "process.detail.script" : "Script", - // TODO New key - Add a translation "process.detail.script" : "Script", // "process.detail.title" : "Process: {{ id }} - {{ name }}", - // TODO New key - Add a translation - "process.detail.title" : "Process: {{ id }} - {{ name }}", + "process.detail.title" : "Processo: {{ id }} - {{ name }}", // "process.detail.start-time" : "Start time", - // TODO New key - Add a translation - "process.detail.start-time" : "Start time", + "process.detail.start-time" : "Orario di inizio", // "process.detail.end-time" : "Finish time", - // TODO New key - Add a translation - "process.detail.end-time" : "Finish time", + "process.detail.end-time" : "Orario di fine", // "process.detail.status" : "Status", - // TODO New key - Add a translation - "process.detail.status" : "Status", + "process.detail.status" : "Stato", // "process.detail.create" : "Create similar process", - // TODO New key - Add a translation - "process.detail.create" : "Create similar process", + "process.detail.create" : "Crea un processo analogo", // "process.detail.actions": "Actions", - // TODO New key - Add a translation - "process.detail.actions": "Actions", + "process.detail.actions": "Azioni", // "process.detail.delete.button": "Delete process", - // TODO New key - Add a translation - "process.detail.delete.button": "Delete process", + "process.detail.delete.button": "Elimina processo", // "process.detail.delete.header": "Delete process", - // TODO New key - Add a translation - "process.detail.delete.header": "Delete process", + "process.detail.delete.header": "Elimina processo", // "process.detail.delete.body": "Are you sure you want to delete the current process?", - // TODO New key - Add a translation - "process.detail.delete.body": "Are you sure you want to delete the current process?", + "process.detail.delete.body": "Sei sicuro di voler eliminare il processo corrente?", // "process.detail.delete.cancel": "Cancel", - // TODO New key - Add a translation - "process.detail.delete.cancel": "Cancel", + "process.detail.delete.cancel": "Annulla", // "process.detail.delete.confirm": "Delete process", - // TODO New key - Add a translation - "process.detail.delete.confirm": "Delete process", + "process.detail.delete.confirm": "Elimina processo", // "process.detail.delete.success": "The process was successfully deleted.", - // TODO New key - Add a translation - "process.detail.delete.success": "The process was successfully deleted.", + "process.detail.delete.success": "Il processo è stato eliminato con successo", // "process.detail.delete.error": "Something went wrong when deleting the process", - // TODO New key - Add a translation - "process.detail.delete.error": "Something went wrong when deleting the process", + "process.detail.delete.error": "Qualcosa è andato storto durante l'elininazione del processo", // "process.overview.delete.failed" : "An error occurs deleting the process.", - // TODO New key - Add a translation - "process.overview.delete.failed" : "An error occurs deleting the process.", + "process.overview.delete.failed" : "Si è verificato un errore durante l'eliminazine del processo.", // "process.overview.delete.success" : "Process deleted with success", - // TODO New key - Add a translation - "process.overview.delete.success" : "Process deleted with success", + "process.overview.delete.success" : "Processo eliminato con successo", // "process.overview.table.finish" : "Finish time (UTC)", - // TODO New key - Add a translation - "process.overview.table.finish" : "Finish time (UTC)", + "process.overview.table.finish" : "Orario di fine (UTC)", // "process.overview.table.id" : "Process ID", - // TODO New key - Add a translation - "process.overview.table.id" : "Process ID", + "process.overview.table.id" : "ID del processo", // "process.overview.table.name" : "Name", - // TODO New key - Add a translation - "process.overview.table.name" : "Name", + "process.overview.table.name" : "Nome", // "process.overview.table.start" : "Start time (UTC)", - // TODO New key - Add a translation - "process.overview.table.start" : "Start time (UTC)", + "process.overview.table.start" : "Orario di inizio (UTC)", // "process.overview.table.status" : "Status", - // TODO New key - Add a translation - "process.overview.table.status" : "Status", + "process.overview.table.status" : "Stato", // "process.overview.table.user" : "User", - // TODO New key - Add a translation - "process.overview.table.user" : "User", + "process.overview.table.user" : "Utente", // "process.overview.title": "Processes Overview", "process.overview.title": "Panoramica dei processi", @@ -7026,119 +6700,94 @@ "process.overview.new": "Nuovo", // "process.overview.table.actions": "Actions", - // TODO New key - Add a translation - "process.overview.table.actions": "Actions", + "process.overview.table.actions": "Azioni", // "process.overview.delete": "Delete {{count}} processes", - // TODO New key - Add a translation - "process.overview.delete": "Delete {{count}} processes", + "process.overview.delete": "Elimina {{count}} processi", // "process.overview.delete.clear": "Clear delete selection", - // TODO New key - Add a translation - "process.overview.delete.clear": "Clear delete selection", + "process.overview.delete.clear": "Ripulisci la sezione Elimina", // "process.overview.delete.processing": "{{count}} process(es) are being deleted. Please wait for the deletion to fully complete. Note that this can take a while.", - // TODO New key - Add a translation - "process.overview.delete.processing": "{{count}} process(es) are being deleted. Please wait for the deletion to fully complete. Note that this can take a while.", + "process.overview.delete.processing": "{{count}} processi sono in fase di eliminazione. Attendere che l'attività sia completata. Potrebbe volerci qualche minuto.", // "process.overview.delete.body": "Are you sure you want to delete {{count}} process(es)?", - // TODO New key - Add a translation - "process.overview.delete.body": "Are you sure you want to delete {{count}} process(es)?", + "process.overview.delete.body": "Sei sicuro di voler eliminare {{count}} processo/i?", // "process.overview.delete.header": "Delete processes", - // TODO New key - Add a translation - "process.overview.delete.header": "Delete processes", + "process.overview.delete.header": "Elimina processi", // "process.bulk.delete.error.head": "Error on deleteing process", - // TODO New key - Add a translation - "process.bulk.delete.error.head": "Error on deleteing process", + "process.bulk.delete.error.head": "Errore nell'eliminazione dei processi", // "process.bulk.delete.error.body": "The process with ID {{processId}} could not be deleted. The remaining processes will continue being deleted. ", - // TODO New key - Add a translation - "process.bulk.delete.error.body": "The process with ID {{processId}} could not be deleted. The remaining processes will continue being deleted. ", + "process.bulk.delete.error.body": "Il processo con l'ID {{processId}} non può essere eliminato. I restanti processi saranno eliminati.", // "process.bulk.delete.success": "{{count}} process(es) have been succesfully deleted", - // TODO New key - Add a translation - "process.bulk.delete.success": "{{count}} process(es) have been succesfully deleted", + "process.bulk.delete.success": "{{count}} processi sono stati eliminati con successo", // "audit.overview.title": "Audit Logs Overview", - "audit.overview.title": "Panoramica dei log di controllo", + "audit.overview.title": "Panoramica dei log di revisione", // "audit.overview.table.id" : "Audit ID", - // TODO New key - Add a translation - "audit.overview.table.id" : "Audit ID", + "audit.overview.table.id" : "ID della revisione", // "audit.overview.table.objectUUID" : "Object ID", - // TODO New key - Add a translation - "audit.overview.table.objectUUID" : "Object ID", + "audit.overview.table.objectUUID" : "ID dell'oggetto", // "audit.overview.table.objectType" : "Object Type", - // TODO New key - Add a translation - "audit.overview.table.objectType" : "Object Type", + "audit.overview.table.objectType" : "Tipo di oggetto", // "audit.overview.table.subjectUUID" : "Subject ID", - // TODO New key - Add a translation - "audit.overview.table.subjectUUID" : "Subject ID", + "audit.overview.table.subjectUUID" : "ID del soggetto", // "audit.overview.table.subjectType" : "Subject Type", - // TODO New key - Add a translation - "audit.overview.table.subjectType" : "Subject Type", + "audit.overview.table.subjectType" : "Tipo di soggetto", // "audit.overview.table.entityType" : "Audit Type", - // TODO New key - Add a translation - "audit.overview.table.entityType" : "Audit Type", + "audit.overview.table.entityType" : "Tipo di revisione", // "audit.overview.table.eperson" : "EPerson", - // TODO New key - Add a translation "audit.overview.table.eperson" : "EPerson", // "audit.overview.table.timestamp" : "Time", - // TODO New key - Add a translation - "audit.overview.table.timestamp" : "Time", + "audit.overview.table.timestamp" : "Orario", // "audit.overview.breadcrumbs": "Audit Logs Overview", - "audit.overview.breadcrumbs": "Panoramica dei log di controllo", + "audit.overview.breadcrumbs": "Panoramica dei log di revisione", // "audit.object.overview.title": "Subject Audit Logs Overview", - "audit.object.overview.title": "Cenni generali dei log di controllo dell'oggetto", + "audit.object.overview.title": "Cenni generali dei log di revisione dell'oggetto", // "audit.detail.title" : "Audit Detail", - // TODO New key - Add a translation - "audit.detail.title" : "Audit Detail", + "audit.detail.title" : "Dettagli della revisione", // "audit.detail.id" : "Audit Id", - // TODO New key - Add a translation - "audit.detail.id" : "Audit Id", + "audit.detail.id" : "ID della revisione", // "audit.detail.subjectUUID" : "Subject ID", - // TODO New key - Add a translation - "audit.detail.subjectUUID" : "Subject ID", + "audit.detail.subjectUUID" : "ID del soggetto", // "audit.detail.subjectType" : "Subject Type", - // TODO New key - Add a translation - "audit.detail.subjectType" : "Subject Type", + "audit.detail.subjectType" : "Tipo di soggetto", // "audit.detail.eventType" : "Audit Type", - // TODO New key - Add a translation - "audit.detail.eventType" : "Audit Type", + "audit.detail.eventType" : "Tipo di revisione", // "audit.detail.eperson" : "EPerson", - // TODO New key - Add a translation "audit.detail.eperson" : "EPerson", // "audit.detail.timeStamp" : "Time", - // TODO New key - Add a translation - "audit.detail.timeStamp" : "Time", + "audit.detail.timeStamp" : "Orario", // "audit.detail.back" : "All Audit Logs", - // TODO New key - Add a translation - "audit.detail.back" : "All Audit Logs", + "audit.detail.back" : "Tutti i log di revisione", // "audit.detail.back.subject": "Subject Audit Logs", - "audit.detail.back.subject": "Log di controllo del soggetto", + "audit.detail.back.subject": "Log di revisione del soggetto", // "patent.listelement.badge": "Patent", "patent.listelement.badge": "Brevetto", @@ -7152,64 +6801,49 @@ "profile.breadcrumbs": "Aggiorna profilo", // "profile.card.access-token": "Personal access token", - // TODO New key - Add a translation - "profile.card.access-token": "Personal access token", + "profile.card.access-token": "Token di accesso personale", // "profile.card.access-token.copy": "Copy token", - // TODO New key - Add a translation - "profile.card.access-token.copy": "Copy token", + "profile.card.access-token.copy": "Copia token", // "profile.card.access-token.copy-info": "Make sure to copy your personal access token now. You won’t be able to see it again!", - // TODO New key - Add a translation - "profile.card.access-token.copy-info": "Make sure to copy your personal access token now. You won’t be able to see it again!", + "profile.card.access-token.copy-info": "Assicurati di copiare il tuo token di accesso personale. Non potrai più farlo in seguito!", // "profile.card.access-token.info": "Tokens you have generated that can be used to access the DSpace-CRIS API.", - // TODO New key - Add a translation - "profile.card.access-token.info": "Tokens you have generated that can be used to access the DSpace-CRIS API.", + "profile.card.access-token.info": "I token che hai generato che possono essere utilizzati per accedere all'API di DSpace-CRIS", // "profile.card.access-token.create.error": "An error occurred while generating the token, please try again later.", - // TODO New key - Add a translation - "profile.card.access-token.create.error": "An error occurred while generating the token, please try again later.", + "profile.card.access-token.create.error": "Si è verificato un errore durante la generazione del token, si prega di riprovare più tardi.", // "profile.card.access-token.create-warning.title": "Generate a new personal access token", - // TODO New key - Add a translation - "profile.card.access-token.create-warning.title": "Generate a new personal access token", + "profile.card.access-token.create-warning.title": "Genera un nuovo token di accesso personale", // "profile.card.access-token.create-warning.msg": "Are you sure you want to generate a new personal access token?\nThis will revoke access for the existing personal access token. This action cannot be undone.", - // TODO New key - Add a translation - "profile.card.access-token.create-warning.msg": "Are you sure you want to generate a new personal access token?\nThis will revoke access for the existing personal access token. This action cannot be undone.", + "profile.card.access-token.create-warning.msg": "Sei sicuro di voler generare un nuovo toke di accesso personale? In questo modo si revoca l'accesso al token esistente. Questa azione non potrà essere annullata.", // "profile.card.access-token.delete-warning.title": "Revoke the new personal access token", - // TODO New key - Add a translation - "profile.card.access-token.delete-warning.title": "Revoke the new personal access token", + "profile.card.access-token.delete-warning.title": "Revoca del nuovo token di accesso personale", // "profile.card.access-token.delete-warning.msg": "Are you sure you want to revoke access for the personal access token? This action cannot be undone.", - // TODO New key - Add a translation - "profile.card.access-token.delete-warning.msg": "Are you sure you want to revoke access for the personal access token? This action cannot be undone.", + "profile.card.access-token.delete-warning.msg": "Sei sicuro di voler revocare l'accesso al token? Questa azione non potrà essere annullata.", // "profile.card.access-token.confirm": "Confirm", - // TODO New key - Add a translation - "profile.card.access-token.confirm": "Confirm", + "profile.card.access-token.confirm": "Conferma", // "profile.card.access-token.cancel": "Cancel", - // TODO New key - Add a translation - "profile.card.access-token.cancel": "Cancel", + "profile.card.access-token.cancel": "Annulla", // "profile.card.access-token.create": "Generate new token", - // TODO New key - Add a translation - "profile.card.access-token.create": "Generate new token", + "profile.card.access-token.create": "Genera un nuovo token", // "profile.card.access-token.delete": "Revoke token", - // TODO New key - Add a translation - "profile.card.access-token.delete": "Revoke token", + "profile.card.access-token.delete": "Revoca il token", // "profile.card.access-token.no-token-generated": "You don't have personal access token. Generate a personal access token for quick access to the DSpace-CRIS API.", - // TODO New key - Add a translation - "profile.card.access-token.no-token-generated": "You don't have personal access token. Generate a personal access token for quick access to the DSpace-CRIS API.", + "profile.card.access-token.no-token-generated": "Non sei in possesso di un token di accesso personale. Genera un token per accedere velocemente all'API di DSpace-CRIS.", // "profile.card.access-token.token-generated": "You have already generated a personal access token.", - // TODO New key - Add a translation - "profile.card.access-token.token-generated": "You have already generated a personal access token.", + "profile.card.access-token.token-generated": "Hai già generato un token di accesso personale.", // "profile.card.identify": "Identify", "profile.card.identify": "Identificare", @@ -7224,8 +6858,7 @@ "profile.groups.head": "Gruppi di autorizzazione a cui appartieni", // "profile.special.groups.head": "Authorization special groups you belong to", - // TODO New key - Add a translation - "profile.special.groups.head": "Authorization special groups you belong to", + "profile.special.groups.head": "Gruppi speciali di autorizzazione a cui appartieni", // "profile.head": "Update Profile", "profile.head": "Aggiorna profilo", @@ -7267,8 +6900,7 @@ "profile.security.form.error.matching-passwords": "Le password non corrispondono.", // "profile.security.form.info": "Optionally, you can enter a new password in the box below, and confirm it by typing it again into the second box.", - // TODO Source message changed - Revise the translation - "profile.security.form.info": "Facoltativamente, è possibile inserire una nuova password nella casella qui sotto e confermarla digitandola nuovamente nella seconda casella. Dovrebbe essere lungo almeno sei caratteri.", + "profile.security.form.info": "Facoltativamente, è possibile inserire una nuova password nella casella qui sotto e confermarla digitandola nuovamente nella seconda casella.", // "profile.security.form.label.password": "Password", "profile.security.form.label.password": "Password", @@ -7277,8 +6909,7 @@ "profile.security.form.label.passwordrepeat": "Ridigitare per confermare", // "profile.security.form.label.current-password": "Current password", - // TODO New key - Add a translation - "profile.security.form.label.current-password": "Current password", + "profile.security.form.label.current-password": "Password corrente", // "profile.security.form.notifications.success.content": "Your changes to the password were saved.", "profile.security.form.notifications.success.content": "Le modifiche apportate alla password sono state salvate.", @@ -7290,15 +6921,13 @@ "profile.security.form.notifications.error.title": "Errore durante la modifica delle password", // "profile.security.form.notifications.error.change-failed": "An error occurred while trying to change the password. Please check if the current password is correct.", - // TODO New key - Add a translation - "profile.security.form.notifications.error.change-failed": "An error occurred while trying to change the password. Please check if the current password is correct.", + "profile.security.form.notifications.error.change-failed": "Si è verificato un errore durante la modifica della password. Assicurati che la password corrente sia corretta.", // "profile.security.form.notifications.error.not-same": "The provided passwords are not the same.", "profile.security.form.notifications.error.not-same": "Le password fornite non sono le stesse.", // "profile.security.form.notifications.error.general": "Please fill required fields of security form.", - // TODO New key - Add a translation - "profile.security.form.notifications.error.general": "Please fill required fields of security form.", + "profile.security.form.notifications.error.general": "Si prega di inserire i campi richiesti nel modulo di sicurezza.", // "profile.title": "Update Profile", "profile.title": "Aggiorna profilo", @@ -7335,15 +6964,13 @@ "project.page.status": "Parole chiave", // "project.page.titleprefix": "Research Project: ", - // TODO New key - Add a translation - "project.page.titleprefix": "Research Project: ", + "project.page.titleprefix": "Progetto di ricerca: ", // "project.search.results.head": "Project Search Results", - "project.search.results.head": "Progetto di ricerca: ", + "project.search.results.head": "Risultati della ricerca per progetti", // "project-relationships.search.results.head": "Project Search Results", - // TODO New key - Add a translation - "project-relationships.search.results.head": "Project Search Results", + "project-relationships.search.results.head": "Risultati della ricerca per progetti", @@ -7366,18 +6993,16 @@ "publication.page.publisher": "Editore", // "publication.page.titleprefix": "Publication: ", - // TODO New key - Add a translation - "publication.page.titleprefix": "Publication: ", + "publication.page.titleprefix": "Pubblicazione: ", // "publication.page.volume-title": "Volume Title", "publication.page.volume-title": "Titolo volume", // "publication.search.results.head": "Publication Search Results", - "publication.search.results.head": "Risultati della ricerca di pubblicazioni", + "publication.search.results.head": "Risultati della ricerca per pubblicazioni", // "publication-relationships.search.results.head": "Publication Search Results", - // TODO New key - Add a translation - "publication-relationships.search.results.head": "Publication Search Results", + "publication-relationships.search.results.head": "Risultati della ricerca per pubblicazioni", // "publication.search.title": "Publication Search", "publication.search.title": "Ricerca pubblicazione", @@ -7436,8 +7061,7 @@ "register-page.create-profile.security.header": "Sicurezza", // "register-page.create-profile.security.info": "Please enter a password in the box below, and confirm it by typing it again into the second box.", - // TODO Source message changed - Revise the translation - "register-page.create-profile.security.info": "Inserisci una password nella casella qui sotto e confermala digitandola nuovamente nella seconda casella. Dovrebbe essere lungo almeno sei caratteri.", + "register-page.create-profile.security.info": "Inserisci una password nella casella qui sotto e confermala digitandola nuovamente nella seconda casella.", // "register-page.create-profile.security.label.password": "Password *", "register-page.create-profile.security.label.password": "Password *", @@ -7485,12 +7109,10 @@ "register-page.registration.email.error.required": "Inserire un indirizzo e-mail", // "register-page.registration.email.error.not-email-form": "Please fill in a valid email address.", - // TODO New key - Add a translation - "register-page.registration.email.error.not-email-form": "Please fill in a valid email address.", + "register-page.registration.email.error.not-email-form": "Inserisci un indirizzo e-mail valido.", // "register-page.registration.email.error.not-valid-domain": "Use email with allowed domains: {{ domains }}", - // TODO New key - Add a translation - "register-page.registration.email.error.not-valid-domain": "Use email with allowed domains: {{ domains }}", + "register-page.registration.email.error.not-valid-domain": "Utilizza una e-mail con un dominio valido: {{ domains }}", // "register-page.registration.email.hint": "This address will be verified and used as your login name.", "register-page.registration.email.hint": "Questo indirizzo verrà verificato e utilizzato come nome di accesso.", @@ -7508,39 +7130,30 @@ "register-page.registration.error.head": "Errore durante il tentativo di registrazione dell'e-mail", // "register-page.registration.error.content": "An error occured when registering the following email address: {{ email }}", - // TODO New key - Add a translation - "register-page.registration.error.content": "An error occured when registering the following email address: {{ email }}", + "register-page.registration.error.content": "Si è verificato un errore durante la registrazione del seguente indirizzo e-mail: {{ email }}", // "register-page.registration.error.recaptcha": "Error when trying to authenticate with recaptcha", - // TODO New key - Add a translation - "register-page.registration.error.recaptcha": "Error when trying to authenticate with recaptcha", + "register-page.registration.error.recaptcha": "Errore durante l'autenticazione con reCaptcha", // "register-page.registration.google-recaptcha.must-accept-cookies": "In order to register you must accept the Registration and Password recovery (Google reCaptcha) cookies.", - // TODO New key - Add a translation - "register-page.registration.google-recaptcha.must-accept-cookies": "In order to register you must accept the Registration and Password recovery (Google reCaptcha) cookies.", + "register-page.registration.google-recaptcha.must-accept-cookies": "Per registrarsi è obbligatorio accettare i cookie Registration and Password recovery (Google reCaptcha).", // "register-page.registration.error.maildomain": "This email address is not on the list of domains who can register. Allowed domains are {{ domains }}", - // TODO New key - Add a translation - "register-page.registration.error.maildomain": "This email address is not on the list of domains who can register. Allowed domains are {{ domains }}", + "register-page.registration.error.maildomain": "Questo indirizzo e-mail non è nella lista dei dominii che possono essere registrati. I domini validi sono {{ domains }}", // "register-page.registration.google-recaptcha.open-cookie-settings": "Open cookie settings", - // TODO New key - Add a translation - "register-page.registration.google-recaptcha.open-cookie-settings": "Open cookie settings", + "register-page.registration.google-recaptcha.open-cookie-settings": "Aprire le impostazioni dei cookie", // "register-page.registration.google-recaptcha.notification.title": "Google reCaptcha", - // TODO New key - Add a translation "register-page.registration.google-recaptcha.notification.title": "Google reCaptcha", // "register-page.registration.google-recaptcha.notification.message.error": "An error occurred during reCaptcha verification", - // TODO New key - Add a translation - "register-page.registration.google-recaptcha.notification.message.error": "An error occurred during reCaptcha verification", + "register-page.registration.google-recaptcha.notification.message.error": "Si è verificato un errore durante la verifica reCaptcha", // "register-page.registration.google-recaptcha.notification.message.expired": "Verification expired. Please verify again.", - // TODO New key - Add a translation - "register-page.registration.google-recaptcha.notification.message.expired": "Verification expired. Please verify again.", + "register-page.registration.google-recaptcha.notification.message.expired": "Verifica scaduta. Si prega di verificare di nuovo.", // "register-page.registration.info.maildomain": "Accounts can be registered for mail addresses of the domains", - // TODO New key - Add a translation - "register-page.registration.info.maildomain": "Accounts can be registered for mail addresses of the domains", + "register-page.registration.info.maildomain": "Gli account possono essere registrati per gli indirizzi e-mail dei dominii", // "relationships.add.error.relationship-type.content": "No suitable match could be found for relationship type {{ type }} between the two items", "relationships.add.error.relationship-type.content": "Non è stata trovata alcuna corrispondenza adatta per il tipo di relazione {{ type }} tra i due item", @@ -7621,60 +7234,58 @@ "resource-policies.add.button": "Aggiungi", // "resource-policies.add.for.": "Add a new policy", - "resource-policies.add.for.": "Aggiungi un nuovo criterio", + "resource-policies.add.for.": "Aggiungi una nuova policy", // "resource-policies.add.for.bitstream": "Add a new Bitstream policy", - "resource-policies.add.for.bitstream": "Aggiungi un nuovo criterio Bitstream", + "resource-policies.add.for.bitstream": "Aggiungi una nuova policy di Bitstream", // "resource-policies.add.for.bundle": "Add a new Bundle policy", - "resource-policies.add.for.bundle": "Aggiungi un nuovo criterio Bundle", + "resource-policies.add.for.bundle": "Aggiungi una nuova policy di Bundle", // "resource-policies.add.for.item": "Add a new Item policy", - "resource-policies.add.for.item": "Aggiungi un nuovo criterio item", + "resource-policies.add.for.item": "Aggiungi una nuova policy di Item", // "resource-policies.add.for.community": "Add a new Community policy", - "resource-policies.add.for.community": "Aggiungere un nuovo criterio comunitario", + "resource-policies.add.for.community": "Aggiungi una nuova policy di community", // "resource-policies.add.for.collection": "Add a new Collection policy", - "resource-policies.add.for.collection": "Aggiungere un nuovo criterio di collezione", + "resource-policies.add.for.collection": "Aggiungi una nuova policy di collezione", // "resource-policies.create.page.heading": "Create new resource policy for ", - "resource-policies.create.page.heading": "Creare nuovi criteri di risorsa per ", + "resource-policies.create.page.heading": "Creare una nuova policy di risorsa per ", // "resource-policies.create.page.failure.content": "An error occurred while creating the resource policy.", - "resource-policies.create.page.failure.content": "Si è verificato un errore durante la creazione del criterio della risorsa.", + "resource-policies.create.page.failure.content": "Si è verificato un errore durante la creazione della policy di risorsa.", // "resource-policies.create.page.success.content": "Operation successful", "resource-policies.create.page.success.content": "Operazione riuscita", // "resource-policies.create.page.title": "Create new resource policy", - "resource-policies.create.page.title": "Creare nuovi criteri risorse", + "resource-policies.create.page.title": "Creare nuove policy di risorsa", // "resource-policies.delete.btn": "Delete selected", "resource-policies.delete.btn": "Elimina selezionato", // "resource-policies.delete.btn.title": "Delete selected resource policies", - "resource-policies.delete.btn.title": "Elimina criteri risorse selezionati", + "resource-policies.delete.btn.title": "Elimina le policy di risorsa selezionate", // "resource-policies.delete.failure.content": "An error occurred while deleting selected resource policies.", - "resource-policies.delete.failure.content": "Si è verificato un errore durante l'eliminazione dei criteri delle risorse selezionati.", + "resource-policies.delete.failure.content": "Si è verificato un errore durante l'eliminazione delle policy di risorsa selezionate.", // "resource-policies.delete.success.content": "Operation successful", "resource-policies.delete.success.content": "Operazione riuscita", // "resource-policies.edit.page.heading": "Edit resource policy ", - "resource-policies.edit.page.heading": "Modifica criterio risorse", + "resource-policies.edit.page.heading": "Modifica policy di risorsa", // "resource-policies.edit.page.failure.content": "An error occurred while editing the resource policy.", - "resource-policies.edit.page.failure.content": "Si è verificato un errore durante la modifica del criterio delle risorse.", + "resource-policies.edit.page.failure.content": "Si è verificato un errore durante la modifica della policy di risorsa.", // "resource-policies.edit.page.target-failure.content": "An error occurred while editing the target (ePerson or group) of the resource policy.", - // TODO New key - Add a translation - "resource-policies.edit.page.target-failure.content": "An error occurred while editing the target (ePerson or group) of the resource policy.", + "resource-policies.edit.page.target-failure.content": "Si è verificato un errore durante la modifica dell'obiettivo (ePerson o gruppo) della policy di risorsa.", // "resource-policies.edit.page.other-failure.content": "An error occurred while editing the resource policy. The target (ePerson or group) has been successfully updated.", - // TODO New key - Add a translation - "resource-policies.edit.page.other-failure.content": "An error occurred while editing the resource policy. The target (ePerson or group) has been successfully updated.", + "resource-policies.edit.page.other-failure.content": "Si è verificato un errore durante la modifica della policy di risorsa. L'obiettio (ePerson o gruppo) è stato aggiornato con successo.", // "resource-policies.edit.page.success.content": "Operation successful", "resource-policies.edit.page.success.content": "Operazione riuscita", @@ -7710,20 +7321,16 @@ "resource-policies.form.eperson-group-list.table.headers.name": "Nome", // "resource-policies.form.eperson-group-list.modal.header": "Cannot change type", - // TODO New key - Add a translation - "resource-policies.form.eperson-group-list.modal.header": "Cannot change type", + "resource-policies.form.eperson-group-list.modal.header": "Impossibile modificare il tipo", // "resource-policies.form.eperson-group-list.modal.text1.toGroup": "It is not possible to replace an ePerson with a group.", - // TODO New key - Add a translation - "resource-policies.form.eperson-group-list.modal.text1.toGroup": "It is not possible to replace an ePerson with a group.", + "resource-policies.form.eperson-group-list.modal.text1.toGroup": "Impossibile sostituire una ePerson con un gruppo.", // "resource-policies.form.eperson-group-list.modal.text1.toEPerson": "It is not possible to replace a group with an ePerson.", - // TODO New key - Add a translation - "resource-policies.form.eperson-group-list.modal.text1.toEPerson": "It is not possible to replace a group with an ePerson.", + "resource-policies.form.eperson-group-list.modal.text1.toEPerson": "Impossibile sostituire un gruppo con una ePerson.", // "resource-policies.form.eperson-group-list.modal.text2": "Delete the current resource policy and create a new one with the desired type.", - // TODO New key - Add a translation - "resource-policies.form.eperson-group-list.modal.text2": "Delete the current resource policy and create a new one with the desired type.", + "resource-policies.form.eperson-group-list.modal.text2": "Elimina la policy di risorsa corrente e creane una nuova con il tipo desiderato.", // "resource-policies.form.eperson-group-list.modal.close": "Ok", "resource-policies.form.eperson-group-list.modal.close": "Ok", @@ -7825,7 +7432,6 @@ "search.filters.applied.f.dateSubmitted": "Data di invio", // "search.filters.applied.f.discoverable": "Non-discoverable", - // TODO Source message changed - Revise the translation "search.filters.applied.f.discoverable": "Privato", // "search.filters.applied.f.entityType": "Item Type", @@ -7859,23 +7465,22 @@ "search.filters.applied.f.projectorgunits": "Organizzazione", // "search.filters.applied.f.subject": "Subject", - "search.filters.applied.f.subject": "Oggetto", + "search.filters.applied.f.subject": "Soggetto", // "search.filters.applied.f.submitter": "Submitter", - "search.filters.applied.f.submitter": "Mittente", + "search.filters.applied.f.submitter": "Submitter", // "search.filters.applied.f.jobTitle": "Job Title", - "search.filters.applied.f.jobTitle": "Titolo di lavoro", + "search.filters.applied.f.jobTitle": "Ruolo", // "search.filters.applied.f.birthDate.max": "End birth date", - "search.filters.applied.f.birthDate.max": "Data di fine nascita", + "search.filters.applied.f.birthDate.max": "Data di nascita finale", // "search.filters.applied.f.birthDate.min": "Start birth date", "search.filters.applied.f.birthDate.min": "Data di nascita iniziale", // "search.filters.applied.f.supervisedBy": "Supervised by", - // TODO New key - Add a translation - "search.filters.applied.f.supervisedBy": "Supervised by", + "search.filters.applied.f.supervisedBy": "Supervisionato da", // "search.filters.applied.f.withdrawn": "Withdrawn", "search.filters.applied.f.withdrawn": "Ritirato", @@ -7899,32 +7504,25 @@ "search.filters.applied.charts.defaultConfiguration.title": "Output di ricerca", // "search.filters.applied.charts.chart.bar.dateIssued.year.tab" : "Date Issued", - // TODO New key - Add a translation - "search.filters.applied.charts.chart.bar.dateIssued.year.tab" : "Date Issued", + "search.filters.applied.charts.chart.bar.dateIssued.year.tab" : "Data di inserimento", // "search.filters.applied.charts.chart.pie.more-value" : "More", - // TODO New key - Add a translation - "search.filters.applied.charts.chart.pie.more-value" : "More", + "search.filters.applied.charts.chart.pie.more-value" : "Più", // "search.filters.applied.charts.chart.pie.itemtype_filter.tab" : "Item Type", - // TODO New key - Add a translation - "search.filters.applied.charts.chart.pie.itemtype_filter.tab" : "Item Type", + "search.filters.applied.charts.chart.pie.itemtype_filter.tab" : "Tipo di item", // "search.filters.applied.charts.graphitemtype.tab" : "Visualization by Type", - // TODO New key - Add a translation - "search.filters.applied.charts.graphitemtype.tab" : "Visualization by Type", + "search.filters.applied.charts.graphitemtype.tab" : "Visualizzazione per tipo", // "search.filters.applied.charts.graphpubldate.tab" : "Visualization by Date", - // TODO New key - Add a translation - "search.filters.applied.charts.graphpubldate.tab" : "Visualization by Date", + "search.filters.applied.charts.graphpubldate.tab" : "Visualizzazione per data", // "search.filters.applied.charts.graphpubldateltr.tab" : "Visualization Demo LTR", - // TODO New key - Add a translation - "search.filters.applied.charts.graphpubldateltr.tab" : "Visualization Demo LTR", + "search.filters.applied.charts.graphpubldateltr.tab" : "Visualizzazione Demo LTR", // "search.filters.applied.charts.graphpubldatetrl.tab" : "Visualization Demo RTL", - // TODO New key - Add a translation - "search.filters.applied.charts.graphpubldatetrl.tab" : "Visualization Demo RTL", + "search.filters.applied.charts.graphpubldatetrl.tab" : "Visualizzazione Demo RTL", // "search.filters.applied.charts.default.title": "Search Output", "search.filters.applied.charts.default.title": "Risultati della ricerca", @@ -7936,15 +7534,13 @@ "search.filters.applied.charts.RELATION.Project.fundings.title": "Fondi", // "search.filters.applied.charts.RELATION.Project.researchoutputs.title": "Research Output", - // TODO New key - Add a translation - "search.filters.applied.charts.RELATION.Project.researchoutputs.title": "Research Output", + "search.filters.applied.charts.RELATION.Project.researchoutputs.title": "Output della ricerca", // "search.filters.applied.charts.no.data.found": "No data found", "search.filters.applied.charts.no.data.found": "Nessun dato trovato", // "search.filters.applied.charts.show.hide": "Show/Hide", - // TODO New key - Add a translation - "search.filters.applied.charts.show.hide": "Show/Hide", + "search.filters.applied.charts.show.hide": "Mostra/Nascondi", @@ -7965,12 +7561,10 @@ "search.filters.filter.birthDate.placeholder": "Data di nascita", // "search.filters.filter.birthDate.min.label": "From", - // TODO New key - Add a translation - "search.filters.filter.birthDate.min.label": "From", + "search.filters.filter.birthDate.min.label": "Da", // "search.filters.filter.birthDate.max.label": "To", - // TODO New key - Add a translation - "search.filters.filter.birthDate.max.label": "To", + "search.filters.filter.birthDate.max.label": "A", // "search.filters.filter.birthDate.label": "Search birth date", "search.filters.filter.birthDate.label": "Cerca data di nascita", @@ -8039,18 +7633,16 @@ "search.filters.filter.dateSubmitted.label": "Data di ricerca inviata", // "search.filters.filter.discoverable.head": "Non-discoverable", - // TODO Source message changed - Revise the translation "search.filters.filter.discoverable.head": "Privato", // "search.filters.filter.editor.head": "Editor", - "search.filters.filter.editor.head": "Editor", + "search.filters.filter.editor.head": "Editore", // "search.filters.filter.editor.placeholder": "Editor", - "search.filters.filter.editor.placeholder": "Editor", + "search.filters.filter.editor.placeholder": "Editore", // "search.filters.filter.editor.label": "Search editor", - // TODO New key - Add a translation - "search.filters.filter.editor.label": "Search editor", + "search.filters.filter.editor.label": "Cerca editore", // "search.filters.filter.withdrawn.head": "Withdrawn", "search.filters.filter.withdrawn.head": "Ritirato", @@ -8062,7 +7654,7 @@ "search.filters.filter.entityType.placeholder": "Tipo di item", // "search.filters.filter.entityType.label": "Search item type", - "search.filters.filter.entityType.label": "Tipo di item di ricerca", + "search.filters.filter.entityType.label": "Cerca tipo di item", // "search.filters.filter.expand": "Expand filter", "search.filters.filter.expand": "Espandi filtro", @@ -8086,8 +7678,7 @@ "search.filters.filter.funding.placeholder": "Finanziamento", // "search.filters.filter.funding.label": "Search funding", - // TODO New key - Add a translation - "search.filters.filter.funding.label": "Search funding", + "search.filters.filter.funding.label": "Cerca finanziamento", // "search.filters.filter.has_content_in_original_bundle.head": "Has files", "search.filters.filter.has_content_in_original_bundle.head": "Ha file", @@ -8135,33 +7726,28 @@ "search.filters.filter.language.placeholder": "Lingue", // "search.filters.filter.projectOrgUnits.head" : "Organizations", - // TODO New key - Add a translation - "search.filters.filter.projectOrgUnits.head" : "Organizations", + "search.filters.filter.projectOrgUnits.head" : "Strutture", // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", - // TODO New key - Add a translation - "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", + "search.filters.filter.projectOrgUnits.placeholder" : "Strutture", // "search.filters.filter.personOrgUnits.head" : "Organizations", - // TODO New key - Add a translation - "search.filters.filter.personOrgUnits.head" : "Organizations", + "search.filters.filter.personOrgUnits.head" : "Strutture", // "search.filters.filter.personOrgUnits.label" : "Search organizations", - // TODO New key - Add a translation - "search.filters.filter.personOrgUnits.label" : "Search organizations", + "search.filters.filter.personOrgUnits.label" : "Cerca strutture", // "search.filters.filter.personOrgUnits.placeholder" : "Organizations", - // TODO New key - Add a translation - "search.filters.filter.personOrgUnits.placeholder" : "Organizations", + "search.filters.filter.personOrgUnits.placeholder" : "Strutture", // "search.filters.filter.namedresourcetype.head": "Status", - "search.filters.filter.namedresourcetype.head": "Status", + "search.filters.filter.namedresourcetype.head": "Stato", // "search.filters.filter.namedresourcetype.placeholder": "Status", - "search.filters.filter.namedresourcetype.placeholder": "Status", + "search.filters.filter.namedresourcetype.placeholder": "Stato", // "search.filters.filter.namedresourcetype.label": "Search status", - "search.filters.filter.namedresourcetype.label": "Status Ricerca", + "search.filters.filter.namedresourcetype.label": "Cerca stato", // "search.filters.filter.objectpeople.head": "People", "search.filters.filter.objectpeople.head": "Ricercatori", @@ -8179,8 +7765,7 @@ "search.filters.filter.organization.placeholder": "Struttura", // "search.filters.filter.organization.label": "Search organization", - // TODO New key - Add a translation - "search.filters.filter.organization.label": "Search organization", + "search.filters.filter.organization.label": "Cerca struttura", // "search.filters.filter.organizationAddressCountry.head": "Country", "search.filters.filter.organizationAddressCountry.head": "Paese", @@ -8243,20 +7828,16 @@ "search.filters.filter.submitter.label": "Mittente della ricerca", // "search.filters.filter.show-tree": "Browse {{ name }} tree", - // TODO New key - Add a translation - "search.filters.filter.show-tree": "Browse {{ name }} tree", + "search.filters.filter.show-tree": "Sfoglia l'albero di {{ name }}", // "search.filters.filter.supervisedBy.head": "Supervised By", - // TODO New key - Add a translation - "search.filters.filter.supervisedBy.head": "Supervised By", + "search.filters.filter.supervisedBy.head": "Supervisionato da", // "search.filters.filter.supervisedBy.placeholder": "Supervised By", - // TODO New key - Add a translation - "search.filters.filter.supervisedBy.placeholder": "Supervised By", + "search.filters.filter.supervisedBy.placeholder": "Supervisionato da", // "search.filters.filter.supervisedBy.label": "Search Supervised By", - // TODO New key - Add a translation - "search.filters.filter.supervisedBy.label": "Search Supervised By", + "search.filters.filter.supervisedBy.label": "Cerca supervisionato da", // "search.filters.filter.types.head": "Type", "search.filters.filter.types.head": "Tipo", @@ -8325,8 +7906,7 @@ "default.search.results.head": "Risultati della ricerca", // "default-relationships.search.results.head": "Search Results", - // TODO New key - Add a translation - "default-relationships.search.results.head": "Search Results", + "default-relationships.search.results.head": "Risultati della ricerca", // "defaultConfiguration.search.results.head": "Search Results", "defaultConfiguration.search.results.head": "Risultati della ricerca", @@ -8353,12 +7933,10 @@ "search.results.empty": "La tua ricerca non ha prodotto risultati.", // "search.results.view-result": "View", - // TODO New key - Add a translation - "search.results.view-result": "View", + "search.results.view-result": "Vedi", // "search.results.response.500": "An error occurred during query execution, please try again later", - // TODO New key - Add a translation - "search.results.response.500": "An error occurred during query execution, please try again later", + "search.results.response.500": "Si è verificato un errore durante l'esecuzione, si prega di riprovare più tardi", // "search.sidebar.close": "Back to results", @@ -8383,16 +7961,13 @@ "search.sidebar.settings.title": "Impostazioni", // "search.sidebar.show.hide": "Show/Hide", - // TODO New key - Add a translation - "search.sidebar.show.hide": "Show/Hide", + "search.sidebar.show.hide": "Mostra/Nascondi", // "search.sidebar.show.hide.filters.tooltip": "Show/Hide filters sidebar", - // TODO New key - Add a translation - "search.sidebar.show.hide.filters.tooltip": "Show/Hide filters sidebar", + "search.sidebar.show.hide.filters.tooltip": "Mostra/Nascondi i filtri della barra laterale", // "search.sidebar.show.hide.charts.tooltip": "Show/Hide charts", - // TODO New key - Add a translation - "search.sidebar.show.hide.charts.tooltip": "Show/Hide charts", + "search.sidebar.show.hide.charts.tooltip": "Mostra/Nascondi grafici", @@ -8408,40 +7983,34 @@ // "sorting.ASC": "Ascending", - "sorting.ASC": "Ascendente", + "sorting.ASC": "Crescente", // "sorting.DESC": "Descending", - "sorting.DESC": "Discendente", + "sorting.DESC": "Decrescente", // "sorting.dc.title.ASC": "Title Ascending", - "sorting.dc.title.ASC": "Titolo ascendente", + "sorting.dc.title.ASC": "Titolo crescente", // "sorting.dc.title.DESC": "Title Descending", "sorting.dc.title.DESC": "Titolo decrescente", // "sorting.metric.view.ASC" : "Views Ascending", - // TODO New key - Add a translation - "sorting.metric.view.ASC" : "Views Ascending", + "sorting.metric.view.ASC" : "Vista in ordine crescente", // "sorting.metric.view.DESC" : "Views Descending", - // TODO New key - Add a translation - "sorting.metric.view.DESC" : "Views Descending", + "sorting.metric.view.DESC" : "Vista in ordine decrescente", // "sorting.metric.download.ASC" : "Downloads Ascending", - // TODO New key - Add a translation - "sorting.metric.download.ASC" : "Downloads Ascending", + "sorting.metric.download.ASC" : "Download in ordine crescente", // "sorting.metric.download.DESC" : "Downloads Descending", - // TODO New key - Add a translation - "sorting.metric.download.DESC" : "Downloads Descending", + "sorting.metric.download.DESC" : "Download in ordine decrescente", // "sorting.metric.scopus.citation.ASC" : "Citations Ascending", - // TODO New key - Add a translation - "sorting.metric.scopus.citation.ASC" : "Citations Ascending", + "sorting.metric.scopus.citation.ASC" : "Citazioni in ordine crescente", // "sorting.metric.scopus.citation.DESC" : "Citations Descending", - // TODO New key - Add a translation - "sorting.metric.scopus.citation.DESC" : "Citations Descending", + "sorting.metric.scopus.citation.DESC" : "Citazioni in ordine decrescente", // "sorting.score.ASC": "Least Relevant", "sorting.score.ASC": "Minor rilevanza", @@ -8474,36 +8043,28 @@ "sorting.ownerselection.DESC": "Rilevanza del proprietario decrescente", // "sorting.organization.legalName.ASC" : "Legal Name Ascending", - // TODO New key - Add a translation - "sorting.organization.legalName.ASC" : "Legal Name Ascending", + "sorting.organization.legalName.ASC" : "Nome legale in ordine crescente", // "sorting.organization.legalName.DESC" : "Legal Name Descending", - // TODO New key - Add a translation - "sorting.organization.legalName.DESC" : "Legal Name Descending", + "sorting.organization.legalName.DESC" : "Nome legale in ordine decrescente", // "sorting.organisation.address.addressCountry.ASC" : "Country Ascending", - // TODO New key - Add a translation - "sorting.organisation.address.addressCountry.ASC" : "Country Ascending", + "sorting.organisation.address.addressCountry.ASC" : "Paese in ordine crescente", // "sorting.organisation.address.addressCountry.DESC" : "Country Descending", - // TODO New key - Add a translation - "sorting.organisation.address.addressCountry.DESC" : "Country Descending", + "sorting.organisation.address.addressCountry.DESC" : "Paese in ordine decrescente", // "sorting.organisation.address.addressLocality.ASC" : "Locality Ascending", - // TODO New key - Add a translation - "sorting.organisation.address.addressLocality.ASC" : "Locality Ascending", + "sorting.organisation.address.addressLocality.ASC" : "Località in ordine crescente", // "sorting.organisation.address.addressLocality.DESC" : "Locality Descending", - // TODO New key - Add a translation - "sorting.organisation.address.addressLocality.DESC" : "Locality Descending", + "sorting.organisation.address.addressLocality.DESC" : "Località in ordine decrescente", // "sorting.organisation.address.foundingDate.ASC" : "Founding Date Ascending", - // TODO New key - Add a translation - "sorting.organisation.address.foundingDate.ASC" : "Founding Date Ascending", + "sorting.organisation.address.foundingDate.ASC" : "Data di fondazione in ordine crescente", // "sorting.organisation.address.foundingDate.DESC" : "Founding Date Descending", - // TODO New key - Add a translation - "sorting.organisation.address.foundingDate.DESC" : "Founding Date Descending", + "sorting.organisation.address.foundingDate.DESC" : "Data di fondazione in ordine decrescente", // "statistics.export.csv": "Export CSV", @@ -8534,39 +8095,31 @@ "statistics.table.no-data": "Nessun dato disponibile", // "statistics.table.mainReports.title.TotalVisits": "Total visits", - // TODO Source message changed - Revise the translation "statistics.table.mainReports.title.TotalVisits": "Visite totali", // "statistics.table.mainReports.title.TopItems": "Most viewed", - // TODO Source message changed - Revise the translation "statistics.table.mainReports.title.TopItems": "I più visti", // "statistics.table.mainReports.title.TotalVisitsPerMonth": "Total visits per month", - // TODO Source message changed - Revise the translation "statistics.table.mainReports.title.TotalVisitsPerMonth": "Visite mensili totali", // "statistics.table.mainReports.title.TotalDownloads": "File visits", - // TODO Source message changed - Revise the translation "statistics.table.mainReports.title.TotalDownloads": "Visite ai file", // "statistics.table.mainReports.title.TotalVisitsAndDownloads": "Items and file visits", - // TODO Source message changed - Revise the translation - "statistics.table.mainReports.title.TotalVisitsAndDownloads": "Visite di item e file", + "statistics.table.mainReports.title.TotalVisitsAndDownloads": "Visite ad item e file", // "statistics.table.mainReports.title.TopCountries": "Top country views", - // TODO Source message changed - Revise the translation - "statistics.table.mainReports.title.TopCountries": "Top Country Views", + "statistics.table.mainReports.title.TopCountries": "Paesi con più visite", // "statistics.table.mainReports.title.TopContinents": "Top region views", - // TODO Source message changed - Revise the translation - "statistics.table.mainReports.title.TopContinents": "Top Region Views", + "statistics.table.mainReports.title.TopContinents": "Regioni con più visite", // "statistics.table.mainReports.title.TopCategories": "Categories", "statistics.table.mainReports.title.TopCategories": "Categorie", // "statistics.table.mainReports.title.TopCities": "Top city views", - // TODO Source message changed - Revise the translation - "statistics.table.mainReports.title.TopCities": "Top City Views", + "statistics.table.mainReports.title.TopCities": "Città con più visite", // "statistics.table.downloadReports.title.TotalVisits": "Most downloaded", "statistics.table.downloadReports.title.TotalVisits": "I più scaricati", @@ -8575,15 +8128,12 @@ "statistics.table.downloadReports.title.TopItems": "I più scaricati", // "statistics.table.downloadReports.title.TotalVisitsPerMonth": "Total downloads per month", - // TODO Source message changed - Revise the translation "statistics.table.downloadReports.title.TotalVisitsPerMonth": "Download totali al mese", // "statistics.table.downloadReports.title.TotalDownloads": "Most downloaded", - // TODO Source message changed - Revise the translation "statistics.table.downloadReports.title.TotalDownloads": "I più scaricati", // "statistics.table.downloadReports.title.TotalDownloadsPerMonth": "Total downloads per month", - // TODO Source message changed - Revise the translation "statistics.table.downloadReports.title.TotalDownloadsPerMonth": "Download totali al mese", // "statistics.table.downloadReports.title.TopCountries": "Top country downloads", @@ -8599,186 +8149,145 @@ "statistics.table.downloadReports.title.TopCities": "Top city downloads", // "statistics.table.mainReports.header.views": "Views", - // TODO New key - Add a translation - "statistics.table.mainReports.header.views": "Views", + "statistics.table.mainReports.header.views": "Visualizzazioni", // "statistics.table.mainReports.header.bitstream": "File Visits", - // TODO New key - Add a translation - "statistics.table.mainReports.header.bitstream": "File Visits", + "statistics.table.mainReports.header.bitstream": "Visite al file", // "statistics.table.mainReports.header.continent": "Continent", - // TODO New key - Add a translation - "statistics.table.mainReports.header.continent": "Continent", + "statistics.table.mainReports.header.continent": "Continente", // "statistics.table.mainReports.header.country": "Country", - // TODO New key - Add a translation - "statistics.table.mainReports.header.country": "Country", + "statistics.table.mainReports.header.country": "Paese", // "statistics.table.mainReports.header.city": "City", - // TODO New key - Add a translation - "statistics.table.mainReports.header.city": "City", + "statistics.table.mainReports.header.city": "Città", // "statistics.table.mainReports.header.item": "Item", - // TODO New key - Add a translation "statistics.table.mainReports.header.item": "Item", // "statistics.table.downloadReports.header.views": "Downloads", - // TODO New key - Add a translation - "statistics.table.downloadReports.header.views": "Downloads", + "statistics.table.downloadReports.header.views": "Download", // "statistics.table.downloadReports.header.continent": "Continent", - // TODO New key - Add a translation - "statistics.table.downloadReports.header.continent": "Continent", + "statistics.table.downloadReports.header.continent": "Continente", // "statistics.table.downloadReports.header.country": "Country", - // TODO New key - Add a translation - "statistics.table.downloadReports.header.country": "Country", + "statistics.table.downloadReports.header.country": "Paese", // "statistics.table.downloadReports.header.city": "City", - // TODO New key - Add a translation - "statistics.table.downloadReports.header.city": "City", + "statistics.table.downloadReports.header.city": "Città", // "statistics.table.downloadReports.header.item": "Item", - // TODO New key - Add a translation "statistics.table.downloadReports.header.item": "Item", // "statistics.table.publicationsReports.title.TotalVisits": "Total visits", - // TODO Source message changed - Revise the translation "statistics.table.publicationsReports.title.TotalVisits": "Visite totali", // "statistics.table.publicationsReports.title.TopItems": "Most viewed", - // TODO Source message changed - Revise the translation "statistics.table.publicationsReports.title.TopItems": "I più visti", // "statistics.table.publicationsReports.title.TopCities": "Top cities", - // TODO Source message changed - Revise the translation - "statistics.table.publicationsReports.title.TopCities": "Migliori città", + "statistics.table.publicationsReports.title.TopCities": "Città con più visite", // "statistics.table.publicationsReports.title.TopCountries": "Top countries", - // TODO Source message changed - Revise the translation - "statistics.table.publicationsReports.title.TopCountries": "Migliori paesi", + "statistics.table.publicationsReports.title.TopCountries": "Paesi con più visite", // "statistics.table.publicationsReports.title.TotalVisitsPerMonth": "Total visits per month", - // TODO Source message changed - Revise the translation "statistics.table.publicationsReports.title.TotalVisitsPerMonth": "Visite totali al mese", // "statistics.table.publicationsReports.title.TotalDownloads": "Downloads", "statistics.table.publicationsReports.title.TotalDownloads": "Download", // "statistics.table.publicationsReports.title.TotalVisitsAndDownloads": "Items and file visits", - // TODO Source message changed - Revise the translation - "statistics.table.publicationsReports.title.TotalVisitsAndDownloads": "Visite di item e file", + "statistics.table.publicationsReports.title.TotalVisitsAndDownloads": "Visite ad item e file", // "statistics.table.projectsReports.title.TotalVisits": "Total visits", - // TODO Source message changed - Revise the translation "statistics.table.projectsReports.title.TotalVisits": "Visite totali", // "statistics.table.projectsReports.title.TopItems": "Most viewed", - // TODO Source message changed - Revise the translation "statistics.table.projectsReports.title.TopItems": "I più visti", // "statistics.table.projectsReports.title.TotalVisitsPerMonth": "Total visits per month", - // TODO Source message changed - Revise the translation "statistics.table.projectsReports.title.TotalVisitsPerMonth": "Visite totali al mese", // "statistics.table.projectsReports.title.TopCities": "Top cities", - // TODO Source message changed - Revise the translation "statistics.table.projectsReports.title.TopCities": "Migliori città", // "statistics.table.projectsReports.title.TopCountries": "Top countries", - // TODO Source message changed - Revise the translation "statistics.table.projectsReports.title.TopCountries": "Migliori paesi", // "statistics.table.projectsReports.title.TotalDownloads": "Downloads", "statistics.table.projectsReports.title.TotalDownloads": "Download", // "statistics.table.projectsReports.title.TotalVisitsAndDownloads": "Items and file visits", - // TODO Source message changed - Revise the translation - "statistics.table.projectsReports.title.TotalVisitsAndDownloads": "Visite di item e file", + "statistics.table.projectsReports.title.TotalVisitsAndDownloads": "Visite ad item e file", // "statistics.table.rppublicationsReports.title.TotalVisits": "Total visits", - // TODO Source message changed - Revise the translation "statistics.table.rppublicationsReports.title.TotalVisits": "Visite totali", // "statistics.table.rppublicationsReports.title.TopItems": "Most viewed", - // TODO Source message changed - Revise the translation "statistics.table.rppublicationsReports.title.TopItems": "I più visti", // "statistics.table.rppublicationsReports.title.TopCities": "Top cities", - // TODO Source message changed - Revise the translation "statistics.table.rppublicationsReports.title.TopCities": "Migliori città", // "statistics.table.rppublicationsReports.title.TopCountries": "Top countries", - // TODO Source message changed - Revise the translation "statistics.table.rppublicationsReports.title.TopCountries": "Migliori paesi", // "statistics.table.rppublicationsReports.title.TotalVisitsPerMonth": "Total visits per month", - // TODO Source message changed - Revise the translation "statistics.table.rppublicationsReports.title.TotalVisitsPerMonth": "Visite totali al mese", // "statistics.table.rppublicationsReports.title.TotalDownloads": "Downloads", "statistics.table.rppublicationsReports.title.TotalDownloads": "Download", // "statistics.table.rppublicationsReports.title.TotalVisitsAndDownloads": "Items and file visits", - // TODO Source message changed - Revise the translation - "statistics.table.rppublicationsReports.title.TotalVisitsAndDownloads": "Visite di item e file", + "statistics.table.rppublicationsReports.title.TotalVisitsAndDownloads": "Visite ad item e file", // "statistics.table.rpprojectsReports.title.TotalVisits": "Total visits", - // TODO Source message changed - Revise the translation "statistics.table.rpprojectsReports.title.TotalVisits": "Visite totali", // "statistics.table.rpprojectsReports.title.TopItems": "Most viewed", - // TODO Source message changed - Revise the translation "statistics.table.rpprojectsReports.title.TopItems": "I più visti", // "statistics.table.rpprojectsReports.title.TopCities": "Top cities", - // TODO Source message changed - Revise the translation "statistics.table.rpprojectsReports.title.TopCities": "Migliori città", // "statistics.table.rpprojectsReports.title.TopCountries": "Top countries", - // TODO Source message changed - Revise the translation "statistics.table.rpprojectsReports.title.TopCountries": "Migliori paesi", // "statistics.table.rpprojectsReports.title.TotalVisitsPerMonth": "Total visits per month", - // TODO Source message changed - Revise the translation "statistics.table.rpprojectsReports.title.TotalVisitsPerMonth": "Visite totali al mese", // "statistics.table.rpprojectsReports.title.TotalDownloads": "Downloads", "statistics.table.rpprojectsReports.title.TotalDownloads": "Download", // "statistics.table.rpprojectsReports.title.TotalVisitsAndDownloads": "Items and file Visits", - "statistics.table.rpprojectsReports.title.TotalVisitsAndDownloads": "Visite di item e file", + "statistics.table.rpprojectsReports.title.TotalVisitsAndDownloads": "Visite ad item e file", // "statistics.categories.title": "Statistics by category", - // TODO Source message changed - Revise the translation "statistics.categories.title": "Statistiche per categoria", // "statistics.categories.mainReports.tab": "Views reports", - // TODO Source message changed - Revise the translation "statistics.categories.mainReports.tab": "Visualizza report", // "statistics.categories.projectsReports.tab": "Project reports", - // TODO Source message changed - Revise the translation - "statistics.categories.projectsReports.tab": "Rapporti di progetto", + "statistics.categories.projectsReports.tab": "Report di progetto", // "statistics.categories.rpprojectsReports.tab": "Researchers projects reports", - // TODO Source message changed - Revise the translation - "statistics.categories.rpprojectsReports.tab": "Rapporti sui progetti dei ricercatori", + "statistics.categories.rpprojectsReports.tab": "Report sui progetti dei ricercatori", // "statistics.categories.publicationsReports.tab": "Publications reports", - // TODO Source message changed - Revise the translation - "statistics.categories.publicationsReports.tab": "Rapporti sulle pubblicazioni", + "statistics.categories.publicationsReports.tab": "Report sulle pubblicazioni", // "statistics.categories.rppublicationsReports.tab": "Researchers publication reports", - // TODO Source message changed - Revise the translation - "statistics.categories.rppublicationsReports.tab": "Rapporti di pubblicazione dei ricercatori", + "statistics.categories.rppublicationsReports.tab": "Report sulle pubblicazione dei ricercatori", // "statistics.categories.downloadReports.tab": "Downloads reports", - // TODO Source message changed - Revise the translation - "statistics.categories.downloadReports.tab": "Rapporti sui download", + "statistics.categories.downloadReports.tab": "Report sui download", // "statistics.reports.title": "Reports", - "statistics.reports.title": "Rapporti", + "statistics.reports.title": "Report", // "submission.edit.breadcrumbs": "Edit Submission", "submission.edit.breadcrumbs": "Modifica immissione", @@ -8904,7 +8413,6 @@ "submission.import-external.source.crossref": "CrossRef", // "submission.import-external.source.datacite": "DataCite", - // TODO New key - Add a translation "submission.import-external.source.datacite": "DataCite", // "submission.import-external.source.scielo": "SciELO", @@ -8980,15 +8488,13 @@ "submission.import-external.preview.title.Publication": "Anteprima pubblicazione", // "submission.import-external.preview.title.none": "Item Preview", - // TODO New key - Add a translation - "submission.import-external.preview.title.none": "Item Preview", + "submission.import-external.preview.title.none": "Anteprima item", // "submission.import-external.preview.title.Journal": "Journal Preview", "submission.import-external.preview.title.Journal": "Anteprima journal", // "submission.import-external.preview.title.OrgUnit": "Organizational Unit Preview", - // TODO Source message changed - Revise the translation - "submission.import-external.preview.title.OrgUnit": "Anteprima editore", + "submission.import-external.preview.title.OrgUnit": "Anteprima struttura", // "submission.import-external.preview.title.Person": "Person Preview", "submission.import-external.preview.title.Person": "Anteprima persona", @@ -9011,48 +8517,37 @@ // "submission.sections.correction.bitstream.operation.add": "Bitstream added", - // TODO New key - Add a translation - "submission.sections.correction.bitstream.operation.add": "Bitstream added", + "submission.sections.correction.bitstream.operation.add": "Bitstream aggiunto", // "submission.sections.correction.bitstream.operation.modify": "Bitstream modified", - // TODO New key - Add a translation - "submission.sections.correction.bitstream.operation.modify": "Bitstream modified", + "submission.sections.correction.bitstream.operation.modify": "Bitstream modificato", // "submission.sections.correction.bitstream.operation.remove": "Bitstream removed", - // TODO New key - Add a translation - "submission.sections.correction.bitstream.operation.remove": "Bitstream removed", + "submission.sections.correction.bitstream.operation.remove": "Bitstream rimosso", // "submission.sections.correction.column.current-value": "Current value", - // TODO New key - Add a translation - "submission.sections.correction.column.current-value": "Current value", + "submission.sections.correction.column.current-value": "Valore corrente", // "submission.sections.correction.column.file": "File", - // TODO New key - Add a translation "submission.sections.correction.column.file": "File", // "submission.sections.correction.column.file.info": "Here are the changes related the item's bitstream", - // TODO New key - Add a translation - "submission.sections.correction.column.file.info": "Here are the changes related the item's bitstream", + "submission.sections.correction.column.file.info": "Ecco le modifiche relative al bistream dell'item", // "submission.sections.correction.column.metadata": "Metadata", - // TODO New key - Add a translation - "submission.sections.correction.column.metadata": "Metadata", + "submission.sections.correction.column.metadata": "Metadati", // "submission.sections.correction.column.metadata.info": "Here are the changes related the item's metadata", - // TODO New key - Add a translation - "submission.sections.correction.column.metadata.info": "Here are the changes related the item's metadata", + "submission.sections.correction.column.metadata.info": "Ecco le modifiche relative ai metadati dell'item", // "submission.sections.correction.column.previous-value": "Previous value", - // TODO New key - Add a translation - "submission.sections.correction.column.previous-value": "Previous value", + "submission.sections.correction.column.previous-value": "Valore precedente", // "submission.sections.correction.column.policy": "Policy", - // TODO New key - Add a translation "submission.sections.correction.column.policy": "Policy", // "submission.sections.submit.progressbar.correction": "Correction details", - // TODO New key - Add a translation - "submission.sections.submit.progressbar.correction": "Correction details", + "submission.sections.submit.progressbar.correction": "Correzione dei dettagli", @@ -9108,12 +8603,10 @@ "submission.sections.detect-duplicate.not-duplicate-help": "Clicca qui se questo non è un duplicato del tuo articolo", // "submission.sections.detect-duplicate.submitter-decision": "Submitter decision:", - // TODO New key - Add a translation - "submission.sections.detect-duplicate.submitter-decision": "Submitter decision:", + "submission.sections.detect-duplicate.submitter-decision": "Decisione del submitter:", // "submission.sections.detect-duplicate.submitter-note": "Submitter note:", - // TODO New key - Add a translation - "submission.sections.detect-duplicate.submitter-note": "Submitter note:", + "submission.sections.detect-duplicate.submitter-note": "Nota del submitter:", @@ -9139,8 +8632,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-button-title.isProjectOfPublication": "Progetto", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.none": "Import remote item", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.none": "Import remote item", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.none": "Importa item remoti", // "submission.sections.describe.relationship-lookup.external-source.import-modal.isProjectOfPublication.added.new-entity": "New Entity Added!", "submission.sections.describe.relationship-lookup.external-source.import-modal.isProjectOfPublication.added.new-entity": "Nuova entità aggiunta!", @@ -9260,8 +8752,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.new-entity": "Importato e aggiunto con successo volume di journal esterno alla selezione", // "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Select a local match:", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Select a local match:", + "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Seleziona una corrispondenza locale:", // "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all", "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deseleziona tutto", @@ -9366,8 +8857,7 @@ "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfProject": "Finanziatore del progetto", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isPublicationOfAuthor": "Publication of the Author", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isPublicationOfAuthor": "Publication of the Author", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isPublicationOfAuthor": "Pubblicazioni dell'autore", // "submission.sections.describe.relationship-lookup.selection-tab.title.openAIREFunding": "Funding OpenAIRE API", "submission.sections.describe.relationship-lookup.selection-tab.title.openAIREFunding": "Finanziamento dell'API OpenAIRE", @@ -9436,11 +8926,10 @@ "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Unità organizzativa padre", // "submission.sections.describe.relationship-lookup.title.isPublicationOfAuthor": "Publication", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.title.isPublicationOfAuthor": "Publication", + "submission.sections.describe.relationship-lookup.title.isPublicationOfAuthor": "Pubblicazioni", // "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown", - "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown", + "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Attiva dropdown", // "submission.sections.describe.relationship-lookup.selection-tab.settings": "Settings", "submission.sections.describe.relationship-lookup.selection-tab.settings": "Impostazioni", @@ -9560,8 +9049,7 @@ "submission.sections.ccLicense.option.select": "Seleziona un'opzione...", // "submission.sections.ccLicense.link": "You’ve selected the following license:", - // TODO New key - Add a translation - "submission.sections.ccLicense.link": "You’ve selected the following license:", + "submission.sections.ccLicense.link": "Hai selezionato la seguente licenza:", // "submission.sections.ccLicense.confirmation": "I grant the license above", "submission.sections.ccLicense.confirmation": "Concedo la licenza di cui sopra", @@ -9570,14 +9058,13 @@ "submission.sections.general.add-more": "Aggiungi altro", // "submission.sections.general.cannot_deposit": "Deposit cannot be completed due to errors in the form.
Please fill out all required fields to complete the deposit.", - // TODO New key - Add a translation - "submission.sections.general.cannot_deposit": "Deposit cannot be completed due to errors in the form.
Please fill out all required fields to complete the deposit.", + "submission.sections.general.cannot_deposit": "L'immissione non può essere competata a causa di errori nel modulo.
Si prega di compilare tutti i campi obbligatori.", // "submission.sections.general.collection": "Collection", - "submission.sections.general.collection": "collezione", + "submission.sections.general.collection": "Collezione", // "submission.sections.general.deposit_error_notice": "There was an issue when submitting the item, please try again later.", - "submission.sections.general.deposit_error_notice": "Si è verificato un problema durante l'immissione dell'articolo, riprova più tardi.", + "submission.sections.general.deposit_error_notice": "Si è verificato un problema durante l'immissione dell'item, riprova più tardi.", // "submission.sections.general.invalid_state_error": "Cannot save current changes, mandatory fields are missing. Please resolve problems and save again later.", "submission.sections.general.invalid_state_error": "Impossibile salvare le modifiche correnti, mancano campi obbligatori. Risolvi i problemi e salva di nuovo in un secondo momento.", @@ -9619,28 +9106,22 @@ "submission.sections.general.sections_not_valid": "Ci sono sezioni incomplete.", // "submission.sections.identifiers.info": "The following identifiers will be created for your item:", - // TODO New key - Add a translation - "submission.sections.identifiers.info": "The following identifiers will be created for your item:", + "submission.sections.identifiers.info": "Per questo item saranno generati i seguenti identificativi:", // "submission.sections.identifiers.no_handle": "No handles have been minted for this item.", - // TODO New key - Add a translation - "submission.sections.identifiers.no_handle": "No handles have been minted for this item.", + "submission.sections.identifiers.no_handle": "Non sono stati generati handle per questo item.", // "submission.sections.identifiers.no_doi": "No DOIs have been minted for this item.", - // TODO New key - Add a translation - "submission.sections.identifiers.no_doi": "No DOIs have been minted for this item.", + "submission.sections.identifiers.no_doi": "Non sono stati generati DOI per questo item.", // "submission.sections.identifiers.handle_label": "Handle: ", - // TODO New key - Add a translation "submission.sections.identifiers.handle_label": "Handle: ", // "submission.sections.identifiers.doi_label": "DOI: ", - // TODO New key - Add a translation "submission.sections.identifiers.doi_label": "DOI: ", // "submission.sections.identifiers.otherIdentifiers_label": "Other identifiers: ", - // TODO New key - Add a translation - "submission.sections.identifiers.otherIdentifiers_label": "Other identifiers: ", + "submission.sections.identifiers.otherIdentifiers_label": "Altri identificativi: ", // "submission.sections.submit.progressbar.accessCondition": "Item access conditions", "submission.sections.submit.progressbar.accessCondition": "Condizioni di accesso all'item", @@ -9724,32 +9205,27 @@ "submission.sections.submit.progressbar.detect-duplicate": "Potenziali duplicati", // "submission.sections.submit.progressbar.identifiers": "Identifiers", - // TODO New key - Add a translation - "submission.sections.submit.progressbar.identifiers": "Identifiers", + "submission.sections.submit.progressbar.identifiers": "Identificativi", // "submission.sections.submit.progressbar.license": "Deposit license", "submission.sections.submit.progressbar.license": "Licenza di deposito", // "submission.sections.submit.progressbar.sherpapolicy": "Sherpa policies", - // TODO New key - Add a translation - "submission.sections.submit.progressbar.sherpapolicy": "Sherpa policies", + "submission.sections.submit.progressbar.sherpapolicy": "Policy di Sherpa", // "submission.sections.submit.progressbar.upload": "Upload files", "submission.sections.submit.progressbar.upload": "Carica file", // "submission.sections.submit.progressbar.sherpaPolicies": "Publisher open access policy information", - // TODO New key - Add a translation - "submission.sections.submit.progressbar.sherpaPolicies": "Publisher open access policy information", + "submission.sections.submit.progressbar.sherpaPolicies": "Informazioni sulla policy di open access dell'editore", // "submission.sections.submit.progressbar.correction-step": "Corrections", - // TODO New key - Add a translation - "submission.sections.submit.progressbar.correction-step": "Corrections", + "submission.sections.submit.progressbar.correction-step": "Correzioni", // "submission.sections.sherpa-policy.title-empty": "No publisher policy information available. If your work has an associated ISSN, please enter it above to see any related publisher open access policies.", - // TODO New key - Add a translation - "submission.sections.sherpa-policy.title-empty": "No publisher policy information available. If your work has an associated ISSN, please enter it above to see any related publisher open access policies.", + "submission.sections.sherpa-policy.title-empty": "Non sono disponibili informazioni sulle policy dell'editore. Se il lavoro ha un ISSN associato, si prega di inserirlo qui sopra per vedere le policy di open access dell'editore.", // "submission.sections.status.errors.title": "Errors", "submission.sections.status.errors.title": "Errori", @@ -9770,12 +9246,10 @@ "submission.sections.status.warnings.aria": "ha avvisi", // "submission.sections.status.info.title": "Additional Information", - // TODO New key - Add a translation - "submission.sections.status.info.title": "Additional Information", + "submission.sections.status.info.title": "Informazioni aggiuntive", // "submission.sections.status.info.aria": "Additional Information", - // TODO New key - Add a translation - "submission.sections.status.info.aria": "Additional Information", + "submission.sections.status.info.aria": "Informazioni aggiuntive", // "submission.sections.toggle.open": "Open section", "submission.sections.toggle.open": "Apri sezione", @@ -9790,7 +9264,6 @@ "submission.sections.toggle.aria.close": "Comprimi la sezione {{sectionHeader}}", // "submission.sections.upload.checksum": "Checksum", - // TODO New key - Add a translation "submission.sections.upload.checksum": "Checksum", // "submission.sections.upload.delete.confirm.cancel": "Cancel", @@ -9857,16 +9330,13 @@ "submission.sections.upload.form.until-placeholder": "Fino a quando", // "submission.sections.upload.header.policy.default.nolist": "Uploaded files in the {{collectionName}} collection will be accessible according to the following group(s):", - // TODO New key - Add a translation - "submission.sections.upload.header.policy.default.nolist": "Uploaded files in the {{collectionName}} collection will be accessible according to the following group(s):", + "submission.sections.upload.header.policy.default.nolist": "I file caricati nella collection {{collectionName}} saranno accessibili in base ai seguenti gruppi:", // "submission.sections.upload.header.policy.default.withlist": "Please note that uploaded files in the {{collectionName}} collection will be accessible, in addition to what is explicitly decided for the single file, with the following group(s):", - // TODO New key - Add a translation - "submission.sections.upload.header.policy.default.withlist": "Please note that uploaded files in the {{collectionName}} collection will be accessible, in addition to what is explicitly decided for the single file, with the following group(s):", + "submission.sections.upload.header.policy.default.withlist": "Si prega di notare che i file caricati nella collection {{collectionName}} saranno accessibili, in aggiunta a quanto esplicitamente deciso per il singolo file, con i seguenti gruppi:", // "submission.sections.upload.info": "Here you will find all the files currently in the item. You can update the file metadata and access conditions or upload additional files by dragging & dropping them anywhere on the page.", - // TODO Source message changed - Revise the translation - "submission.sections.upload.info": "Qui troverai tutti i file attualmente presenti nell'articolo. È possibile aggiornare i metadati dei file e le condizioni di accesso o upload di file aggiuntivi semplicemente trascinandoli e rilasciandoli ovunque nella pagina", + "submission.sections.upload.info": "Qui troverai tutti i file attualmente presenti nell'item. È possibile aggiornare i metadati dei file e le condizioni di accesso o caricare file aggiuntivi semplicemente trascinandoli e rilasciandoli ovunque nella pagina", // "submission.sections.upload.no-entry": "No", "submission.sections.upload.no-entry": "No", @@ -9938,121 +9408,92 @@ "submission.sections.accesses.form.until-placeholder": "Fino a quando", // "submission.sections.license.granted-label": "I confirm the license above", - // TODO New key - Add a translation - "submission.sections.license.granted-label": "I confirm the license above", + "submission.sections.license.granted-label": "Confermo la licenza di cui sopra", // "submission.sections.license.required": "You must accept the license", - // TODO New key - Add a translation - "submission.sections.license.required": "You must accept the license", + "submission.sections.license.required": "È necessario accettare la licenza", // "submission.sections.license.notgranted": "You must accept the license", - // TODO New key - Add a translation - "submission.sections.license.notgranted": "You must accept the license", + "submission.sections.license.notgranted": "È necessario accettare la licenza", // "submission.sections.sherpa.publication.information": "Publication information", - // TODO New key - Add a translation - "submission.sections.sherpa.publication.information": "Publication information", + "submission.sections.sherpa.publication.information": "Informazioni sulla pubblicazione", // "submission.sections.sherpa.publication.information.title": "Title", - // TODO New key - Add a translation - "submission.sections.sherpa.publication.information.title": "Title", + "submission.sections.sherpa.publication.information.title": "Titolo", // "submission.sections.sherpa.publication.information.issns": "ISSNs", - // TODO New key - Add a translation - "submission.sections.sherpa.publication.information.issns": "ISSNs", + "submission.sections.sherpa.publication.information.issns": "ISSN", // "submission.sections.sherpa.publication.information.url": "URL", - // TODO New key - Add a translation "submission.sections.sherpa.publication.information.url": "URL", // "submission.sections.sherpa.publication.information.publishers": "Publisher", - // TODO New key - Add a translation - "submission.sections.sherpa.publication.information.publishers": "Publisher", + "submission.sections.sherpa.publication.information.publishers": "Editore", // "submission.sections.sherpa.publication.information.romeoPub": "Romeo Pub", - // TODO New key - Add a translation "submission.sections.sherpa.publication.information.romeoPub": "Romeo Pub", // "submission.sections.sherpa.publication.information.zetoPub": "Zeto Pub", - // TODO New key - Add a translation "submission.sections.sherpa.publication.information.zetoPub": "Zeto Pub", // "submission.sections.sherpa.publisher.policy": "Publisher Policy", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy": "Publisher Policy", + "submission.sections.sherpa.publisher.policy": "Policy dell'editore", // "submission.sections.sherpa.publisher.policy.description": "The below information was found via Sherpa Romeo. Based on the policies of your publisher, it provides advice regarding whether an embargo may be necessary and/or which files you are allowed to upload. If you have questions, please contact your site administrator via the feedback form in the footer.", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.description": "The below information was found via Sherpa Romeo. Based on the policies of your publisher, it provides advice regarding whether an embargo may be necessary and/or which files you are allowed to upload. If you have questions, please contact your site administrator via the feedback form in the footer.", + "submission.sections.sherpa.publisher.policy.description": "Le informazioni riportate di seguito sono state reperite tramite Sherpa Romeo. In base alle policy del vostro editore, fornisce consigli sull'eventuale necessità di un embargo e/o su quali file è possibile caricare. In caso di domande, contattare l'amministratore del sito tramite il modulo di feedback nel piè di pagina.", // "submission.sections.sherpa.publisher.policy.openaccess": "Open Access pathways permitted by this journal's policy are listed below by article version. Click on a pathway for a more detailed view", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.openaccess": "Open Access pathways permitted by this journal's policy are listed below by article version. Click on a pathway for a more detailed view", + "submission.sections.sherpa.publisher.policy.openaccess": "I percorsi open access consentiti dalle policy di questa rivista sono elencati di seguito per versione dell'articolo. Clicca su un percorso per vederlo nel dettaglio", // "submission.sections.sherpa.publisher.policy.more.information": "For more information, please see the following links:", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.more.information": "For more information, please see the following links:", + "submission.sections.sherpa.publisher.policy.more.information": "Per maggiori informazioni si prega di consultare il seguente link:", // "submission.sections.sherpa.publisher.policy.version": "Version", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.version": "Version", + "submission.sections.sherpa.publisher.policy.version": "Versione", // "submission.sections.sherpa.publisher.policy.embargo": "Embargo", - // TODO New key - Add a translation "submission.sections.sherpa.publisher.policy.embargo": "Embargo", // "submission.sections.sherpa.publisher.policy.noembargo": "No Embargo", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.noembargo": "No Embargo", + "submission.sections.sherpa.publisher.policy.noembargo": "Nessun embargo", // "submission.sections.sherpa.publisher.policy.nolocation": "None", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.nolocation": "None", + "submission.sections.sherpa.publisher.policy.nolocation": "Nessuno", // "submission.sections.sherpa.publisher.policy.license": "License", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.license": "License", + "submission.sections.sherpa.publisher.policy.license": "Licenza", // "submission.sections.sherpa.publisher.policy.prerequisites": "Prerequisites", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.prerequisites": "Prerequisites", + "submission.sections.sherpa.publisher.policy.prerequisites": "Prerequisiti", // "submission.sections.sherpa.publisher.policy.location": "Location", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.location": "Location", + "submission.sections.sherpa.publisher.policy.location": "Località", // "submission.sections.sherpa.publisher.policy.conditions": "Conditions", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.conditions": "Conditions", + "submission.sections.sherpa.publisher.policy.conditions": "Condizioni", // "submission.sections.sherpa.publisher.policy.refresh": "Refresh", - // TODO New key - Add a translation - "submission.sections.sherpa.publisher.policy.refresh": "Refresh", + "submission.sections.sherpa.publisher.policy.refresh": "Ricarica", // "submission.sections.sherpa.record.information": "Record Information", - // TODO New key - Add a translation - "submission.sections.sherpa.record.information": "Record Information", + "submission.sections.sherpa.record.information": "Informazioni sulla registrazione", // "submission.sections.sherpa.record.information.id": "ID", - // TODO New key - Add a translation "submission.sections.sherpa.record.information.id": "ID", // "submission.sections.sherpa.record.information.date.created": "Date Created", - // TODO New key - Add a translation - "submission.sections.sherpa.record.information.date.created": "Date Created", + "submission.sections.sherpa.record.information.date.created": "Data di creazione", // "submission.sections.sherpa.record.information.date.modified": "Last Modified", - // TODO New key - Add a translation - "submission.sections.sherpa.record.information.date.modified": "Last Modified", + "submission.sections.sherpa.record.information.date.modified": "Ultima modifica", // "submission.sections.sherpa.record.information.uri": "URI", - // TODO New key - Add a translation "submission.sections.sherpa.record.information.uri": "URI", // "submission.sections.sherpa.error.message": "There was an error retrieving sherpa informations", - // TODO New key - Add a translation - "submission.sections.sherpa.error.message": "There was an error retrieving sherpa informations", + "submission.sections.sherpa.error.message": "Si è verificato un errore nel recuperare le informazioni da Sherpa", @@ -10084,20 +9525,16 @@ // "submission.workflow.generic.submit_select_reviewer": "Select Reviewer", - // TODO New key - Add a translation - "submission.workflow.generic.submit_select_reviewer": "Select Reviewer", + "submission.workflow.generic.submit_select_reviewer": "Seleziona revisore", // "submission.workflow.generic.submit_select_reviewer-help": "", - // TODO New key - Add a translation "submission.workflow.generic.submit_select_reviewer-help": "", // "submission.workflow.generic.submit_score": "Rate", - // TODO New key - Add a translation - "submission.workflow.generic.submit_score": "Rate", + "submission.workflow.generic.submit_score": "Valuta", // "submission.workflow.generic.submit_score-help": "", - // TODO New key - Add a translation "submission.workflow.generic.submit_score-help": "", @@ -10114,11 +9551,9 @@ "submission.workflow.tasks.claimed.edit_help": "Selezionare questa opzione per modificare i metadati dell'item.", // "submission.workflow.tasks.claimed.decline": "Decline", - // TODO New key - Add a translation - "submission.workflow.tasks.claimed.decline": "Decline", + "submission.workflow.tasks.claimed.decline": "Rifiuta", // "submission.workflow.tasks.claimed.decline_help": "", - // TODO New key - Add a translation "submission.workflow.tasks.claimed.decline_help": "", // "submission.workflow.tasks.claimed.reject.reason.info": "Please enter your reason for rejecting the submission into the box below, indicating whether the submitter may fix a problem and resubmit.", @@ -10208,80 +9643,61 @@ "subscriptions.frequency.W": "Settimanale", // "subscriptions.tooltip": "Subscribe", - // TODO New key - Add a translation - "subscriptions.tooltip": "Subscribe", + "subscriptions.tooltip": "Sottoscrivi", // "subscriptions.modal.title": "Subscriptions", - // TODO New key - Add a translation - "subscriptions.modal.title": "Subscriptions", + "subscriptions.modal.title": "Sottoscrizioni", // "subscriptions.modal.type-frequency": "Type and frequency", - // TODO New key - Add a translation - "subscriptions.modal.type-frequency": "Type and frequency", + "subscriptions.modal.type-frequency": "Tipo e frequenza", // "subscriptions.modal.close": "Close", - // TODO New key - Add a translation - "subscriptions.modal.close": "Close", + "subscriptions.modal.close": "Chiudi", // "subscriptions.modal.delete-info": "To remove this subscription, please visit the \"Subscriptions\" page under your user profile", - // TODO New key - Add a translation - "subscriptions.modal.delete-info": "To remove this subscription, please visit the \"Subscriptions\" page under your user profile", + "subscriptions.modal.delete-info": "Per rimuovere questa sottoscrizione si prega di visitare la pagina \"Sottoscrizioni\" nel proprio profilo utente", // "subscriptions.modal.new-subscription-form.type.content": "Content", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.type.content": "Content", + "subscriptions.modal.new-subscription-form.type.content": "Contenuto", // "subscriptions.modal.new-subscription-form.type.content+statistics": "Content and Statistics", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.type.content+statistics": "Content and Statistics", + "subscriptions.modal.new-subscription-form.type.content+statistics": "Contenuto e statistiche", // "subscriptions.modal.new-subscription-form.type.statistics": "Statistics", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.type.statistics": "Statistics", + "subscriptions.modal.new-subscription-form.type.statistics": "Statistiche", // "subscriptions.modal.new-subscription-form.frequency.D": "Daily", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.frequency.D": "Daily", + "subscriptions.modal.new-subscription-form.frequency.D": "Giornaliero", // "subscriptions.modal.new-subscription-form.frequency.W": "Weekly", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.frequency.W": "Weekly", + "subscriptions.modal.new-subscription-form.frequency.W": "Settimanale", // "subscriptions.modal.new-subscription-form.frequency.M": "Monthly", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.frequency.M": "Monthly", + "subscriptions.modal.new-subscription-form.frequency.M": "Mensile", // "subscriptions.modal.new-subscription-form.submit": "Submit", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.submit": "Submit", + "subscriptions.modal.new-subscription-form.submit": "Invia", // "subscriptions.modal.new-subscription-form.processing": "Processing...", - // TODO New key - Add a translation - "subscriptions.modal.new-subscription-form.processing": "Processing...", + "subscriptions.modal.new-subscription-form.processing": "Elaborazione...", // "subscriptions.modal.create.success": "Subscribed to {{ type }} successfully.", - // TODO New key - Add a translation - "subscriptions.modal.create.success": "Subscribed to {{ type }} successfully.", + "subscriptions.modal.create.success": "Sottoscrzione a {{ type }} avvenuta con successo.", // "subscriptions.modal.delete.success": "Subscription deleted successfully", - // TODO New key - Add a translation - "subscriptions.modal.delete.success": "Subscription deleted successfully", + "subscriptions.modal.delete.success": "Sottoscrizione eliminata con successo", // "subscriptions.modal.update.success": "Subscription to {{ type }} updated successfully", - // TODO New key - Add a translation - "subscriptions.modal.update.success": "Subscription to {{ type }} updated successfully", + "subscriptions.modal.update.success": "Sottoscrizione a {{ type }} aggiornata con successo", // "subscriptions.modal.create.error": "An error occurs during the subscription creation", - // TODO New key - Add a translation - "subscriptions.modal.create.error": "An error occurs during the subscription creation", + "subscriptions.modal.create.error": "Si è verificato un errore durante la creazione della sottoscrizione", // "subscriptions.modal.delete.error": "An error occurs during the subscription delete", - // TODO New key - Add a translation - "subscriptions.modal.delete.error": "An error occurs during the subscription delete", + "subscriptions.modal.delete.error": "Si è verificato un errore durante l'eliminazione della sottoscrizione", // "subscriptions.modal.update.error": "An error occurs during the subscription update", - // TODO New key - Add a translation - "subscriptions.modal.update.error": "An error occurs during the subscription update", + "subscriptions.modal.update.error": "Si è verificato un errore durante l'aggiornamento della sottoscrizione", // "subscriptions.table.dso": "Subject", "subscriptions.table.dso": "Oggetto", @@ -10296,24 +9712,19 @@ "subscriptions.table.action": "Azione", // "subscriptions.table.edit": "Edit", - // TODO New key - Add a translation - "subscriptions.table.edit": "Edit", + "subscriptions.table.edit": "Modifica", // "subscriptions.table.delete": "Delete", - // TODO New key - Add a translation - "subscriptions.table.delete": "Delete", + "subscriptions.table.delete": "Elimina", // "subscriptions.table.not-available": "Not available", - // TODO New key - Add a translation - "subscriptions.table.not-available": "Not available", + "subscriptions.table.not-available": "Non disponibile", // "subscriptions.table.not-available-message": "The subscribed item has been deleted, or you don't currently have the permission to view it", - // TODO New key - Add a translation - "subscriptions.table.not-available-message": "The subscribed item has been deleted, or you don't currently have the permission to view it", + "subscriptions.table.not-available-message": "L'elemento sottoscritto è stato cancellato o non si ha l'autorizzazione per visualizzarlo.", // "subscriptions.table.empty.message": "You do not have any subscriptions at this time. To subscribe to email updates for a Community or Collection, use the subscription button on the object's page.", - // TODO Source message changed - Revise the translation - "subscriptions.table.empty.message": "Non hai ancora sottoscritto alcuna notifica. Per sottoscrivere la notifica relativa a un oggetto, usa il menu contestuale nella pagina di dettaglio dell'oggetto", + "subscriptions.table.empty.message": "Al momento non ci sono sottoscrizioni. Per ricevere aggiornamenti via e-mail di una Community o di una Collection, utilizzare il pulsante di sottoscrizione sulla pagina dell'oggetto", // "thumbnail.default.alt": "Thumbnail Image", @@ -10443,8 +9854,7 @@ "vocabulary-treeview.tree.description.srsc": "Categorie di argomenti di ricerca", // "vocabulary-treeview.info": "Select a subject to add as search filter", - // TODO New key - Add a translation - "vocabulary-treeview.info": "Select a subject to add as search filter", + "vocabulary-treeview.info": "Seleziona un soggetto da aggiungere come filtro di ricerca", // "uploader.browse": "browse", "uploader.browse": "sfoglia", @@ -10459,8 +9869,7 @@ "uploader.or": ", oppure ", // "uploader.processing": "Processing uploaded file(s)... (it's now safe to close this page)", - // TODO Source message changed - Revise the translation - "uploader.processing": "Elaborazione", + "uploader.processing": "Elaborazione dei file caricati... (è ora possibile chiudere questa pagina)", // "uploader.queue-length": "Queue length", "uploader.queue-length": "Lunghezza coda", @@ -10477,12 +9886,10 @@ // "otherworkspace.search.results.head": "Workspace submissions", - // TODO New key - Add a translation - "otherworkspace.search.results.head": "Workspace submissions", + "otherworkspace.search.results.head": "Inserimenti in workspace", // "supervisedWorkspace.search.results.head": "Supervised Items", - // TODO New key - Add a translation - "supervisedWorkspace.search.results.head": "Supervised Items", + "supervisedWorkspace.search.results.head": "Item supervisionati", // "workspace.search.results.head": "Your submissions", "workspace.search.results.head": "I tuoi invii", @@ -10494,8 +9901,7 @@ "workflow.search.results.head": "Task del workflow", // "supervision.search.results.head": "Workflow and Workspace tasks", - // TODO New key - Add a translation - "supervision.search.results.head": "Workflow and Workspace tasks", + "supervision.search.results.head": "Task del workflow e del workspace", @@ -10565,74 +9971,57 @@ // "workflow-item.advanced.title": "Advanced workflow", - // TODO New key - Add a translation - "workflow-item.advanced.title": "Advanced workflow", + "workflow-item.advanced.title": "Workflow avanzato", // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", + "workflow-item.selectrevieweraction.notification.success.title": "Revisore selezionato", // "workflow-item.selectrevieweraction.notification.success.content": "The reviewer for this workflow item has been successfully selected", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.notification.success.content": "The reviewer for this workflow item has been successfully selected", + "workflow-item.selectrevieweraction.notification.success.content": "Il revisore per questo item nel workflow è stato selezionato con successo", // "workflow-item.selectrevieweraction.notification.error.title": "Something went wrong", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.notification.error.title": "Something went wrong", + "workflow-item.selectrevieweraction.notification.error.title": "Qualcosa è andato storto", // "workflow-item.selectrevieweraction.notification.error.content": "Couldn't select the reviewer for this workflow item", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.notification.error.content": "Couldn't select the reviewer for this workflow item", + "workflow-item.selectrevieweraction.notification.error.content": "Non è stato possibile selezionare il revisore per questo item nel workflow", // "workflow-item.selectrevieweraction.title": "Select Reviewer", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.title": "Select Reviewer", + "workflow-item.selectrevieweraction.title": "Seleziona revisore", // "workflow-item.selectrevieweraction.header": "Select Reviewer", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.header": "Select Reviewer", + "workflow-item.selectrevieweraction.header": "Seleziona revisore", // "workflow-item.selectrevieweraction.button.cancel": "Cancel", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.button.cancel": "Cancel", + "workflow-item.selectrevieweraction.button.cancel": "Annulla", // "workflow-item.selectrevieweraction.button.confirm": "Confirm", - // TODO New key - Add a translation - "workflow-item.selectrevieweraction.button.confirm": "Confirm", + "workflow-item.selectrevieweraction.button.confirm": "Conferma", // "workflow-item.scorereviewaction.notification.success.title": "Rating review", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.notification.success.title": "Rating review", + "workflow-item.scorereviewaction.notification.success.title": "Valuta revisione", // "workflow-item.scorereviewaction.notification.success.content": "The rating for this item workflow item has been successfully submitted", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.notification.success.content": "The rating for this item workflow item has been successfully submitted", + "workflow-item.scorereviewaction.notification.success.content": "La valutazione per il workflow di questo item è stata inserita con successo", // "workflow-item.scorereviewaction.notification.error.title": "Something went wrong", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.notification.error.title": "Something went wrong", + "workflow-item.scorereviewaction.notification.error.title": "Qualcosa è andato storto", // "workflow-item.scorereviewaction.notification.error.content": "Couldn't rate this item", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.notification.error.content": "Couldn't rate this item", + "workflow-item.scorereviewaction.notification.error.content": "Non è stato possibile valutare questo item", // "workflow-item.scorereviewaction.title": "Rate this item", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.title": "Rate this item", + "workflow-item.scorereviewaction.title": "Valuta questo item", // "workflow-item.scorereviewaction.header": "Rate this item", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.header": "Rate this item", + "workflow-item.scorereviewaction.header": "Valuta questo item", // "workflow-item.scorereviewaction.button.cancel": "Cancel", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.button.cancel": "Cancel", + "workflow-item.scorereviewaction.button.cancel": "Annulla", // "workflow-item.scorereviewaction.button.confirm": "Confirm", - // TODO New key - Add a translation - "workflow-item.scorereviewaction.button.confirm": "Confirm", + "workflow-item.scorereviewaction.button.confirm": "Conferma", // "idle-modal.header": "Session will expire soon", "idle-modal.header": "La sessione scadrà presto", @@ -10914,7 +10303,6 @@ "person.page.orcid.sync-queue.send.validation-error.title.required": "Il titolo è obbligatorio", // "person.page.orcid.sync-queue.send.validation-error.type.required": "The dc.type is required", - // TODO Source message changed - Revise the translation "person.page.orcid.sync-queue.send.validation-error.type.required": "Il tipo è obbligatorio", // "person.page.orcid.sync-queue.send.validation-error.start-date.required": "The start date is required", @@ -10942,7 +10330,6 @@ "person.page.orcid.sync-queue.send.validation-error.organization.city-required": "L'indirizzo dell'organizzazione da inviare richiede una città", // "person.page.orcid.sync-queue.send.validation-error.organization.country-required": "The address of the organization to be sent requires a valid 2 digits ISO 3166 country", - // TODO Source message changed - Revise the translation "person.page.orcid.sync-queue.send.validation-error.organization.country-required": "L'indirizzo dell'organizzazione da inviare richiede un paese (inserire 2 cifre secondo l'ISO 3166)", // "person.page.orcid.sync-queue.send.validation-error.disambiguated-organization.required": "An identifier to disambiguate organizations is required. Supported ids are GRID, Ringgold, Legal Entity identifiers (LEIs) and Crossref Funder Registry identifiers", @@ -10967,8 +10354,7 @@ "person.page.orcid.synchronization-mode.label": "Sincronizzazione", // "person.page.orcid.synchronization-mode-message": "Please select how you would like synchronization to ORCID to occur. The options include \"Manual\" (you must send your data to ORCID manually), or \"Batch\" (the system will send your data to ORCID via a scheduled script).", - // TODO Source message changed - Revise the translation - "person.page.orcid.synchronization-mode-message": "Abilitare la modalità di sincronizzazione \"manuale\" per disabilitare la sincronizzazione batch in modo da dover inviare manualmente i dati al Registro ORCID", + "person.page.orcid.synchronization-mode-message": "Selezionare la modalità di sincronizzazione con ORCID. Le opzioni includono 'Manuale' (sarà necessario inviare i dati a ORCID manualmente) o 'Batch' (il sistema invierà i dati a ORCID tramite uno script programmato).", // "person.page.orcid.synchronization-mode-funding-message": "Select whether to send your linked Project entities to your ORCID record's list of funding information.", "person.page.orcid.synchronization-mode-funding-message": "Scegli se sincronizzare i tuoi Progetti con la lista delle informazioni dei progetti sul profilo ORCID.", @@ -11015,8 +10401,7 @@ // "person.orcid.registry.auth": "ORCID Authorizations", "person.orcid.registry.auth": "Autorizzazioni ORCID", // "home.recent-submissions.head": "Recent Submissions", - // TODO New key - Add a translation - "home.recent-submissions.head": "Recent Submissions", + "home.recent-submissions.head": "Immissioni recenti", // "relation.rp.researchoutputs.search.results.head": "Related Publications", "relation.rp.researchoutputs.search.results.head": "Pubblicazioni correlate", @@ -11049,173 +10434,134 @@ "invitation.ignore-btn": "Ignora", // "authority-confidence.search-label":"Search", - // TODO Source message changed - Revise the translation "authority-confidence.search-label": "Cerca", // "curation-task.task.hocr.label": "Extract text (HOCR) from images", - "curation-task.task.hocr.label": "Estrai testo (HOCR) dalle immagini", + "curation-task.task.hocr.label": "Estrarre testo (HOCR) dalle immagini", // "curation-task.task.ocrfilter.label": "Consolidate hOCR for fulltext indexing", - "curation-task.task.ocrfilter.label": "Consolida hOCR per l'indicizzazione fulltext", + "curation-task.task.ocrfilter.label": "Consolidare hOCR per l'indicizzazione fulltext", // "curation-task.task.pushocr.label": "Send OCR to the Annotation Server", - "curation-task.task.pushocr.label": "Invia OCR all'Annotation Server", + "curation-task.task.pushocr.label": "Inviare OCR all'Annotation Server", // "curation-task.task.ocrclean.label": "Remove all metadata bundles and uploaded ocr created by a previous upload to the ocr server", - // TODO Source message changed - Revise the translation - "curation-task.task.ocrclean.label": "Rimuovi tutti i metadata bundles and tutti gli OCR caricati in precedenza sul server OCR", + "curation-task.task.ocrclean.label": "Rimuovi tutti i bundles dei metadati e tutti gli OCR generati da precedenti caricamenti sul server OCR", // "curation-task.task.pushocr.force.label": "Clean OCR from the Annotation Server and send again", - // TODO New key - Add a translation - "curation-task.task.pushocr.force.label": "Clean OCR from the Annotation Server and send again", + "curation-task.task.pushocr.force.label": "Ripulisci l'OCR dall'Annotation Server e invia di nuovo", // "curation-task.task.iiifuploader.label": "Upload images to the IIIF Image Server (photo gallery)", - // TODO New key - Add a translation - "curation-task.task.iiifuploader.label": "Upload images to the IIIF Image Server (photo gallery)", + "curation-task.task.iiifuploader.label": "Caricare le immagini sull'Image Server IIIF (galleria di foto)", // "curation-task.task.iiifuploader.primary.label": "Upload images to the IIIF Image Server (digitized book)", - // TODO New key - Add a translation - "curation-task.task.iiifuploader.primary.label": "Upload images to the IIIF Image Server (digitized book)", + "curation-task.task.iiifuploader.primary.label": "Caricare le immagini sull'Image Server IIIF (libro digitalizzato)", // "curation-task.task.iiifforbidfiledownload.label": "Forbid file download for files uploaded on image server", - // TODO New key - Add a translation - "curation-task.task.iiifforbidfiledownload.label": "Forbid file download for files uploaded on image server", + "curation-task.task.iiifforbidfiledownload.label": "Impedire il download dei file caricati sull'Image Server", // "curation-task.task.iiifallowfiledownload.label": "Allow file download for files uploaded on image server", - // TODO New key - Add a translation - "curation-task.task.iiifallowfiledownload.label": "Allow file download for files uploaded on image server", + "curation-task.task.iiifallowfiledownload.label": "Consentire il download dei file caricati sull'Image Server", // "curation-task.task.rawtoaccess.label": "Create access image for RAW Types", "curation-task.task.rawtoaccess.label": "Creare un'immagine di accesso per i tipi RAW", // "curation-task.task.iiifpdfmultipages.label": "Create multipages PDF from IIIF images", - // TODO New key - Add a translation - "curation-task.task.iiifpdfmultipages.label": "Create multipages PDF from IIIF images", + "curation-task.task.iiifpdfmultipages.label": "Creare un PDF multipagina dalle immagini IIIF", // "curation-task.task.iiifclean.label": "Remove all metadata bundles created by a previous upload to the image server", - // TODO New key - Add a translation - "curation-task.task.iiifclean.label": "Remove all metadata bundles created by a previous upload to the image server", + "curation-task.task.iiifclean.label": "Rimuovere tutti i bundle di metadati generati da precedenti caricamenti sull'Image Server", // "curation-task.task.pdftoimagecmyk.label": "Extract images from PDF (CMYK)", - // TODO New key - Add a translation - "curation-task.task.pdftoimagecmyk.label": "Extract images from PDF (CMYK)", + "curation-task.task.pdftoimagecmyk.label": "Estrarre le immagini da PDF (CMYK)", // "curation-task.task.pdftoimagergb.label": "Extract images from PDF (RGB)", - // TODO New key - Add a translation - "curation-task.task.pdftoimagergb.label": "Extract images from PDF (RGB)", + "curation-task.task.pdftoimagergb.label": "Estrarre le immagini da PDF (RGB)", // "curation-task.task.scannedpdf.label": "Extract images from a Scanned PDF", - // TODO New key - Add a translation - "curation-task.task.scannedpdf.label": "Extract images from a Scanned PDF", + "curation-task.task.scannedpdf.label": "Estrarre le immagini da un PDF scannerizzato", // "curation-task.task.rawimagesinpdf.label": "Extract raw images inside the PDF (no OCR)", - // TODO New key - Add a translation - "curation-task.task.rawimagesinpdf.label": "Extract raw images inside the PDF (no OCR)", + "curation-task.task.rawimagesinpdf.label": "Estrarre le immagini grezze dal PDF (no OCR)", // "curation-task.task.undopdfiiif.label": "Undo PDF 2 IIIF Images", - // TODO New key - Add a translation - "curation-task.task.undopdfiiif.label": "Undo PDF 2 IIIF Images", + "curation-task.task.undopdfiiif.label": "Annullare PDF 2 IIIF Images", // "curation-task.task.rebuildpdftoc.label": "Rebuild the PDF ToC", - // TODO New key - Add a translation - "curation-task.task.rebuildpdftoc.label": "Rebuild the PDF ToC", + "curation-task.task.rebuildpdftoc.label": "Ricostruire il PDF ToC", // "listable-notification-object.default-message": "This object couldn't be retrieved", - // TODO New key - Add a translation - "listable-notification-object.default-message": "This object couldn't be retrieved", + "listable-notification-object.default-message": "Questo oggetto non può essere recuperato", // "system-wide-alert-banner.retrieval.error": "Something went wrong retrieving the system-wide alert banner", - // TODO New key - Add a translation - "system-wide-alert-banner.retrieval.error": "Something went wrong retrieving the system-wide alert banner", + "system-wide-alert-banner.retrieval.error": "Qualcosa è andato storto nel recupero del banner di allarme di sistema", // "system-wide-alert-banner.countdown.prefix": "In", - // TODO New key - Add a translation - "system-wide-alert-banner.countdown.prefix": "In", + "system-wide-alert-banner.countdown.prefix": "Tra", // "system-wide-alert-banner.countdown.days": "{{days}} day(s),", - // TODO New key - Add a translation - "system-wide-alert-banner.countdown.days": "{{days}} day(s),", + "system-wide-alert-banner.countdown.days": "{{days}} giorni,", // "system-wide-alert-banner.countdown.hours": "{{hours}} hour(s) and", - // TODO New key - Add a translation - "system-wide-alert-banner.countdown.hours": "{{hours}} hour(s) and", + "system-wide-alert-banner.countdown.hours": "{{hours}} ore e", // "system-wide-alert-banner.countdown.minutes": "{{minutes}} minute(s):", - // TODO New key - Add a translation - "system-wide-alert-banner.countdown.minutes": "{{minutes}} minute(s):", + "system-wide-alert-banner.countdown.minutes": "{{minutes}} minuti:", // "menu.section.system-wide-alert": "System-wide Alert", - // TODO New key - Add a translation - "menu.section.system-wide-alert": "System-wide Alert", + "menu.section.system-wide-alert": "Allarme di sistema", // "system-wide-alert.form.header": "System-wide Alert", - // TODO New key - Add a translation - "system-wide-alert.form.header": "System-wide Alert", + "system-wide-alert.form.header": "Allarme di sistema", // "system-wide-alert-form.retrieval.error": "Something went wrong retrieving the system-wide alert", - // TODO New key - Add a translation - "system-wide-alert-form.retrieval.error": "Something went wrong retrieving the system-wide alert", + "system-wide-alert-form.retrieval.error": "Qualcosa è andato storto nel recupero dell'allarme di sistema", // "system-wide-alert.form.cancel": "Cancel", - // TODO New key - Add a translation - "system-wide-alert.form.cancel": "Cancel", + "system-wide-alert.form.cancel": "Annulla", // "system-wide-alert.form.save": "Save", - // TODO New key - Add a translation - "system-wide-alert.form.save": "Save", + "system-wide-alert.form.save": "Salva", // "system-wide-alert.form.label.active": "ACTIVE", - // TODO New key - Add a translation - "system-wide-alert.form.label.active": "ACTIVE", + "system-wide-alert.form.label.active": "ATTIVO", // "system-wide-alert.form.label.inactive": "INACTIVE", - // TODO New key - Add a translation - "system-wide-alert.form.label.inactive": "INACTIVE", + "system-wide-alert.form.label.inactive": "DISATTIVO", // "system-wide-alert.form.error.message": "The system wide alert must have a message", - // TODO New key - Add a translation - "system-wide-alert.form.error.message": "The system wide alert must have a message", + "system-wide-alert.form.error.message": "L'allarme di sistema deve avere un messaggio", // "system-wide-alert.form.label.message": "Alert message", - // TODO New key - Add a translation - "system-wide-alert.form.label.message": "Alert message", + "system-wide-alert.form.label.message": "Messaggio di allarme", // "system-wide-alert.form.label.countdownTo.enable": "Enable a countdown timer", - // TODO New key - Add a translation - "system-wide-alert.form.label.countdownTo.enable": "Enable a countdown timer", + "system-wide-alert.form.label.countdownTo.enable": "Attiva un conto alla rovescia", // "system-wide-alert.form.label.countdownTo.hint": "Hint: Set a countdown timer. When enabled, a date can be set in the future and the system-wide alert banner will perform a countdown to the set date. When this timer ends, it will disappear from the alert. The server will NOT be automatically stopped.", - // TODO New key - Add a translation - "system-wide-alert.form.label.countdownTo.hint": "Hint: Set a countdown timer. When enabled, a date can be set in the future and the system-wide alert banner will perform a countdown to the set date. When this timer ends, it will disappear from the alert. The server will NOT be automatically stopped.", + "system-wide-alert.form.label.countdownTo.hint": "Suggerimento: Imposta un conto alla rovescia. Se abilitato, è possibile impostare una data futura e il banner di allarme di sistema eseguirà un conto alla rovescia fino alla data impostata. Quando il timer terminerà, l'avviso scomparirà. Il server NON verrà arrestato automaticamente.", // "system-wide-alert.form.label.preview": "System-wide alert preview", - // TODO New key - Add a translation - "system-wide-alert.form.label.preview": "System-wide alert preview", + "system-wide-alert.form.label.preview": "Anteprima dell'allarme di sistema", // "system-wide-alert.form.update.success": "The system-wide alert was successfully updated", - // TODO New key - Add a translation - "system-wide-alert.form.update.success": "The system-wide alert was successfully updated", + "system-wide-alert.form.update.success": "L'allarme di sistema è stato aggiornato con successo", // "system-wide-alert.form.update.error": "Something went wrong when updating the system-wide alert", - // TODO New key - Add a translation - "system-wide-alert.form.update.error": "Something went wrong when updating the system-wide alert", + "system-wide-alert.form.update.error": "Qualcosa è andato storto durante l'aggiornamento dell'allarme di sistema", // "system-wide-alert.form.create.success": "The system-wide alert was successfully created", - // TODO New key - Add a translation - "system-wide-alert.form.create.success": "The system-wide alert was successfully created", + "system-wide-alert.form.create.success": "L'allarme di sistema è stato creato con successo", // "system-wide-alert.form.create.error": "Something went wrong when creating the system-wide alert", - // TODO New key - Add a translation - "system-wide-alert.form.create.error": "Something went wrong when creating the system-wide alert", + "system-wide-alert.form.create.error": "Qualcosa è andato storto nella creazione dell'allarme di sistema", // "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // TODO New key - Add a translation - "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", + "admin.system-wide-alert.breadcrumbs": "Allarmi di sistema", // "admin.system-wide-alert.title": "System-wide Alerts", - // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "Allarmi di sistema", } From 5d704c610d8f009b3e5e0ff0efcc1e478bc6bb43 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 25 Sep 2023 18:10:26 +0200 Subject: [PATCH 118/195] [CST-10703] initial commit (views & unit tests) --- src/app/app-routing.module.ts | 5 + src/app/core/core.module.ts | 4 +- .../core/data/eperson-registration.service.ts | 55 ++++++++++- .../external-login-page-routing.module.ts | 25 +++++ .../external-login-page.component.html | 3 + .../external-login-page.component.scss | 0 .../external-login-page.component.spec.ts | 25 +++++ .../external-login-page.component.ts | 43 +++++++++ .../external-login-page.module.ts | 24 +++++ .../themed-external-login-page.component.ts | 25 +++++ .../confirm-email.component.html | 37 ++++++++ .../confirm-email.component.scss | 0 .../confirm-email.component.spec.ts | 25 +++++ .../confirm-email/confirm-email.component.ts | 41 +++++++++ .../confirmation-sent.component.html | 5 + .../confirmation-sent.component.scss | 0 .../confirmation-sent.component.spec.ts | 55 +++++++++++ .../confirmation-sent.component.ts | 9 ++ .../email-validated.component.html | 9 ++ .../email-validated.component.scss | 0 .../email-validated.component.spec.ts | 79 ++++++++++++++++ .../email-validated.component.ts | 11 +++ .../provide-email.component.html | 38 ++++++++ .../provide-email.component.scss | 0 .../provide-email.component.spec.ts | 43 +++++++++ .../provide-email/provide-email.component.ts | 47 ++++++++++ .../external-log-in.methods-decorator.ts | 29 ++++++ .../external-log-in.component.html | 29 ++++++ .../external-log-in.component.scss | 0 .../external-log-in.component.spec.ts | 89 ++++++++++++++++++ .../external-log-in.component.ts | 91 +++++++++++++++++++ .../external-login-method-entry.component.ts | 22 +++++ .../models/registration-data.mock.model.ts | 31 +++++++ .../models/registration-data.model.ts | 70 ++++++++++++++ .../models/registration-data.resource-type.ts | 9 ++ .../orcid-confirmation.component.html | 35 +++++++ .../orcid-confirmation.component.scss | 0 .../orcid-confirmation.component.spec.ts | 74 +++++++++++++++ .../orcid-confirmation.component.ts | 60 ++++++++++++ .../container/log-in-container.component.html | 1 - src/app/shared/log-in/log-in.component.html | 8 +- src/app/shared/log-in/log-in.component.ts | 11 ++- src/app/shared/shared.module.ts | 16 +++- src/assets/i18n/en.json5 | 28 +++++- 44 files changed, 1198 insertions(+), 13 deletions(-) create mode 100644 src/app/external-login-page/external-login-page-routing.module.ts create mode 100644 src/app/external-login-page/external-login-page.component.html create mode 100644 src/app/external-login-page/external-login-page.component.scss create mode 100644 src/app/external-login-page/external-login-page.component.spec.ts create mode 100644 src/app/external-login-page/external-login-page.component.ts create mode 100644 src/app/external-login-page/external-login-page.module.ts create mode 100644 src/app/external-login-page/themed-external-login-page.component.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.html create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.scss create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.spec.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts create mode 100644 src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts create mode 100644 src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts create mode 100644 src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html create mode 100644 src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.scss create mode 100644 src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts create mode 100644 src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts create mode 100644 src/app/shared/external-log-in-complete/external-login-method-entry.component.ts create mode 100644 src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts create mode 100644 src/app/shared/external-log-in-complete/models/registration-data.model.ts create mode 100644 src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts create mode 100644 src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html create mode 100644 src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.scss create mode 100644 src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts create mode 100644 src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d585d737437..82af5149d06 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -171,6 +171,11 @@ import { RedirectService } from './redirect/redirect.service'; loadChildren: () => import('./login-page/login-page.module') .then((m) => m.LoginPageModule) }, + { + path: 'external-login', + loadChildren: () => import('./external-login-page/external-login-page.module') + .then((m) => m.ExternalLoginPageModule) + }, { path: 'logout', loadChildren: () => import('./logout-page/logout-page.module') diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index f57eb72e9c5..d22aadc8a55 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -232,6 +232,7 @@ import { import { ProductDatasetSchemaType } from './metadata/schema-json-ld/schema-types/product/product-dataset-schema-type'; import { PersonSchemaType } from './metadata/schema-json-ld/schema-types/Person/person-schema-type'; import {ItemRequest} from './shared/item-request.model'; +import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -470,7 +471,8 @@ export const models = WorkflowOwnerStatistics, LoginStatistics, Metric, - ItemRequest + ItemRequest, + RegistrationData ]; @NgModule({ diff --git a/src/app/core/data/eperson-registration.service.ts b/src/app/core/data/eperson-registration.service.ts index 1ec6f7eb299..e1b77977d6a 100644 --- a/src/app/core/data/eperson-registration.service.ts +++ b/src/app/core/data/eperson-registration.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { RequestService } from './request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { GetRequest, PostRequest } from './request.models'; +import { GetRequest, PatchRequest, PostRequest } from './request.models'; import { Observable } from 'rxjs'; import { filter, find, map } from 'rxjs/operators'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; @@ -15,14 +15,14 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpHeaders } from '@angular/common/http'; import { HttpParams } from '@angular/common/http'; - +import { Operation } from 'fast-json-patch'; @Injectable({ providedIn: 'root', }) /** * Service that will register a new email address and request a token */ -export class EpersonRegistrationService { +export class EpersonRegistrationService{ protected linkPath = 'registrations'; protected searchByTokenPath = '/search/findByToken?token='; @@ -32,7 +32,6 @@ export class EpersonRegistrationService { protected rdbService: RemoteDataBuildService, protected halService: HALEndpointService, ) { - } /** @@ -142,4 +141,52 @@ export class EpersonRegistrationService { }); return this.rdbService.buildSingle(href$); } + + /** + * Patch the registration object to update the email address + * @param value provided by the user during the registration confirmation process + * @param registrationId The id of the registration object + * @param token The token of the registration object + * @param updateValue Flag to indicate if the email should be updated or added + * @returns Remote Data state of the patch request + */ + patchUpdateRegistration(value: string, field: string, registrationId: string, token: string, updateValue: boolean) { + const requestId = this.requestService.generateRequestId(); + + const href$ = this.getRegistrationEndpoint().pipe( + find((href: string) => hasValue(href)), + map((href: string) => `${href}/${registrationId}?token=${token}`), + ); + + href$.subscribe((href: string) => { + const operations = this.generateOperations(value, field, updateValue); + const patchRequest = new PatchRequest(requestId, href, operations); + this.requestService.send(patchRequest); + }); + + return this.rdbService.buildFromRequestUUID(requestId); + } + + /** + * Custom method to generate the operations to be performed on the registration object + * @param value provided by the user during the registration confirmation process + * @param updateValue Flag to indicate if the email should be updated or added + * @returns Operations to be performed on the registration object + */ + private generateOperations(value: string, field: string, updateValue: boolean): Operation[] { + let operations = []; + if (hasValue(value) && updateValue) { + operations = [...operations, { + op: 'replace', path: `/${field}`, value: value + }]; + } + + if (hasValue(value) && !updateValue) { + operations = [...operations, { + op: 'add', path: `/${field}`, value: value + }]; + } + + return operations; + } } diff --git a/src/app/external-login-page/external-login-page-routing.module.ts b/src/app/external-login-page/external-login-page-routing.module.ts new file mode 100644 index 00000000000..4e64d267de8 --- /dev/null +++ b/src/app/external-login-page/external-login-page-routing.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service'; +import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; + +const routes: Routes = [ + { + path: '', + pathMatch: 'full', + component: ThemedExternalLoginPageComponent, + // resolve: { breadcrumb: I18nBreadcrumbResolver }, + // data: { breadcrumbKey: 'external-login', title: 'login.title' }, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [ + I18nBreadcrumbResolver, + I18nBreadcrumbsService + ] +}) +export class ExternalLoginPageRoutingModule { } diff --git a/src/app/external-login-page/external-login-page.component.html b/src/app/external-login-page/external-login-page.component.html new file mode 100644 index 00000000000..d2daa24a561 --- /dev/null +++ b/src/app/external-login-page/external-login-page.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/external-login-page/external-login-page.component.scss b/src/app/external-login-page/external-login-page.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/external-login-page/external-login-page.component.spec.ts b/src/app/external-login-page/external-login-page.component.spec.ts new file mode 100644 index 00000000000..56f0d81d189 --- /dev/null +++ b/src/app/external-login-page/external-login-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExternalLoginPageComponent } from './external-login-page.component'; + +describe('ExternalLoginPageComponent', () => { + let component: ExternalLoginPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ExternalLoginPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExternalLoginPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts new file mode 100644 index 00000000000..94ab4424b86 --- /dev/null +++ b/src/app/external-login-page/external-login-page.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { hasValue } from '../shared/empty.util'; +import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; +import { RemoteData } from '../core/data/remote-data'; +import { Registration } from '../core/shared/registration.model'; +import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; +import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; + +@Component({ + templateUrl: './external-login-page.component.html', + styleUrls: ['./external-login-page.component.scss'] +}) +export class ExternalLoginPageComponent implements OnInit { + + public token: string; + + public registrationData: RegistrationData = mockRegistrationDataModel; + + constructor( + private epersonRegistrationService: EpersonRegistrationService, + private router: Router, + ) { + this.token = this.router.parseUrl(this.router.url).queryParams.token; + } + + ngOnInit(): void { + // TODO: call the method getTokenSearchEndpoint (eperson-registration.service.ts ) protected searchByTokenPath = '/search/findByToken?token='; + // token will be provided by the url (returned by REST API) + console.log('ExternalLoginPageComponent ngOnInit'); + if (hasValue(this.token)) { + this.epersonRegistrationService.searchByToken(this.token).subscribe((registration: RemoteData + ) => { + console.log('ExternalLoginPageComponent ngOnInit registration', registration); + if (registration.hasSucceeded) { + this.registrationData = Object.assign(new RegistrationData(), registration.payload); + console.log('ExternalLoginPageComponent ngOnInit registrationData', this.registrationData); + } + }); + } + } + +} diff --git a/src/app/external-login-page/external-login-page.module.ts b/src/app/external-login-page/external-login-page.module.ts new file mode 100644 index 00000000000..697ae7e1a8e --- /dev/null +++ b/src/app/external-login-page/external-login-page.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ExternalLoginPageRoutingModule } from './external-login-page-routing.module'; +import { ExternalLoginPageComponent } from './external-login-page.component'; +import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; +import { SharedModule } from '../shared/shared.module'; + +const COMPONENTS = [ + ExternalLoginPageComponent, + ThemedExternalLoginPageComponent, +]; + +@NgModule({ + declarations: [ + ...COMPONENTS + ], + imports: [ + CommonModule, + ExternalLoginPageRoutingModule, + SharedModule + ] +}) +export class ExternalLoginPageModule { } diff --git a/src/app/external-login-page/themed-external-login-page.component.ts b/src/app/external-login-page/themed-external-login-page.component.ts new file mode 100644 index 00000000000..975b9a2f55e --- /dev/null +++ b/src/app/external-login-page/themed-external-login-page.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { ThemedComponent } from '../shared/theme-support/themed.component'; +import { ExternalLoginPageComponent } from './external-login-page.component'; + +/** + * Themed wrapper for ExternalLoginPageComponent + */ +@Component({ + selector: 'ds-themed-external-login-page', + styleUrls: [], + templateUrl: './../shared/theme-support/themed.component.html' +}) +export class ThemedExternalLoginPageComponent extends ThemedComponent { + protected getComponentName(): string { + return 'ExternalLoginPageComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../themes/${themeName}/app/external-login-page/external-login-page.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./external-login-page.component`); + } +} diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html new file mode 100644 index 00000000000..97d9869f411 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html @@ -0,0 +1,37 @@ +

+ {{ "external-login.confirm-email.header" | translate }} +

+ +
+
+ +
+ {{ "external-login.confirmation.email-required" | translate }} +
+
+ {{ "external-login.confirmation.email-invalid" | translate }} +
+
+ + +
diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts new file mode 100644 index 00000000000..8e426f27fc0 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfirmEmailComponent } from './confirm-email.component'; + +describe('ConfirmEmailComponent', () => { + let component: ConfirmEmailComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ConfirmEmailComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfirmEmailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts new file mode 100644 index 00000000000..0b177db851d --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -0,0 +1,41 @@ +import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; +import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; + +@Component({ + selector: 'ds-confirm-email', + templateUrl: './confirm-email.component.html', + styleUrls: ['./confirm-email.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ConfirmEmailComponent { + + emailForm: FormGroup; + + @Input() registrationId: string; + + @Input() token: string; + + constructor( + private formBuilder: FormBuilder, + private epersonRegistrationService: EpersonRegistrationService, + ) { + this.emailForm = this.formBuilder.group({ + email: ['', [Validators.required, Validators.email]] + }); + } + + submitForm() { + this.emailForm.markAllAsTouched(); + if (this.emailForm.valid) { + const email = this.emailForm.get('email').value; + console.log('Email submitted:', email); + this.epersonRegistrationService.patchUpdateRegistration(email, 'email', this.registrationId, this.token, true).pipe( + getFirstCompletedRemoteData() + ).subscribe((update) => { + console.log('Email update:', update); + }); + } + } +} diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html new file mode 100644 index 00000000000..f81ebf1a170 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html @@ -0,0 +1,5 @@ +

+ {{ "external-login.confirm-email-sent.header" | translate }} +

+ +

diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts new file mode 100644 index 00000000000..5106220db62 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts @@ -0,0 +1,55 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfirmationSentComponent } from './confirmation-sent.component'; +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; + +describe('ConfirmationSentComponent', () => { + let component: ConfirmationSentComponent; + let fixture: ComponentFixture; + let compiledTemplate: HTMLElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ConfirmationSentComponent ], + providers: [ + { provide: TranslateService, useClass: {} }, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfirmationSentComponent); + component = fixture.componentInstance; + compiledTemplate = fixture.nativeElement; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should render translated header', () => { + const headerElement = compiledTemplate.querySelector('h4'); + expect(headerElement.textContent).toContain('Mocked Header Translation'); + }); + + it('should render translated info paragraph', () => { + const infoParagraphElement = compiledTemplate.querySelector('p'); + expect(infoParagraphElement.innerHTML).toContain('Mocked Info Translation'); + }); + +}); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts new file mode 100644 index 00000000000..78a0ef81fed --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts @@ -0,0 +1,9 @@ +import { Component, ChangeDetectionStrategy } from '@angular/core'; + +@Component({ + selector: 'ds-confirmation-sent', + templateUrl: './confirmation-sent.component.html', + styleUrls: ['./confirmation-sent.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ConfirmationSentComponent { } diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.html b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.html new file mode 100644 index 00000000000..3fa31b0bd53 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.html @@ -0,0 +1,9 @@ +

+ {{ "external-login.validated-email.header" | translate }} +

+ +

+ +
+ +
diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.scss b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.spec.ts new file mode 100644 index 00000000000..e961e6fb913 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.spec.ts @@ -0,0 +1,79 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EmailValidatedComponent } from './email-validated.component'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; +import { BehaviorSubject } from 'rxjs'; +import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock'; + +describe('EmailValidatedComponent', () => { + let component: EmailValidatedComponent; + let fixture: ComponentFixture; + let compiledTemplate: HTMLElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ EmailValidatedComponent ], + providers: [ + { provide: TranslateService, useValue: getMockTranslateService() }, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EmailValidatedComponent); + component = fixture.componentInstance; + compiledTemplate = fixture.nativeElement; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should render translated header', () => { + const headerElement = compiledTemplate.querySelector('h4'); + expect(headerElement.textContent).toContain('Mocked Header Translation'); + }); + + it('should render translated info paragraph', () => { + const infoParagraphElement = compiledTemplate.querySelector('p'); + expect(infoParagraphElement.innerHTML).toContain('Mocked Info Translation'); + }); + + it('should render ds-log-in component', () => { + const dsLogInComponent = compiledTemplate.querySelector('ds-log-in'); + expect(dsLogInComponent).toBeTruthy(); + }); + +}); + +// Mock the TranslateService +class MockTranslateService { + private translationSubject = new BehaviorSubject({}); + + get(key: string) { + const translations = { + 'external-login.validated-email.header': 'Mocked Header Translation', + 'external-login.validated-email.info': 'Mocked Info Translation', + }; + + this.translationSubject.next(translations); + + // Return an Observable that mimics TranslateService's behavior + return this.translationSubject.asObservable(); + } +} diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts new file mode 100644 index 00000000000..c74cbcfd489 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts @@ -0,0 +1,11 @@ +import { Component, ChangeDetectionStrategy } from '@angular/core'; + +@Component({ + selector: 'ds-email-validated', + templateUrl: './email-validated.component.html', + styleUrls: ['./email-validated.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EmailValidatedComponent { + +} diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html new file mode 100644 index 00000000000..72c8eb7c88c --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html @@ -0,0 +1,38 @@ +

+ {{ "external-login.provide-email.header" | translate }} +

+ +
+
+ + +
+ {{ "external-login.confirmation.email-required" | translate }} +
+
+ {{ "external-login.confirmation.email-invalid" | translate }} +
+
+ + +
diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts new file mode 100644 index 00000000000..03e28c687d3 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts @@ -0,0 +1,43 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProvideEmailComponent } from './provide-email.component'; +import { FormBuilder } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; + +describe('ProvideEmailComponent', () => { + let component: ProvideEmailComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProvideEmailComponent ], + providers: [ + FormBuilder, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ProvideEmailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts new file mode 100644 index 00000000000..1d504c869f4 --- /dev/null +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts @@ -0,0 +1,47 @@ +import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; +import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; + +@Component({ + selector: 'ds-provide-email', + templateUrl: './provide-email.component.html', + styleUrls: ['./provide-email.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProvideEmailComponent { + emailForm: FormGroup; + + @Input() registrationId: string; + + @Input() token: string; + + constructor( + private formBuilder: FormBuilder, + private epersonRegistrationService: EpersonRegistrationService + ) { + this.emailForm = this.formBuilder.group({ + email: ['', [Validators.required, Validators.email]], + }); + } + + submitForm() { + this.emailForm.markAllAsTouched(); + if (this.emailForm.valid) { + const email = this.emailForm.get('email').value; + console.log('Email submitted:', email); + this.epersonRegistrationService + .patchUpdateRegistration( + email, + 'email', + this.registrationId, + this.token, + true + ) + .pipe(getFirstCompletedRemoteData()) + .subscribe((update) => { + console.log('Email update:', update); + }); + } + } +} diff --git a/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts b/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts new file mode 100644 index 00000000000..933edf24640 --- /dev/null +++ b/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts @@ -0,0 +1,29 @@ +import { AuthMethodType } from '../../core/auth/models/auth.method-type'; + +/** + * Map to store the external login confirmation component for the given auth method type + */ +const authMethodsMap = new Map(); +/** + * Decorator to register the external login confirmation component for the given auth method type + * @param authMethodType the type of the external login method + */ +export function renderExternalLoginConfirmationFor( + authMethodType: AuthMethodType +) { + return function decorator(objectElement: any) { + if (!objectElement) { + return; + } + authMethodsMap.set(authMethodType, objectElement); + }; +} +/** + * Get the external login confirmation component for the given auth method type + * @param authMethodType the type of the external login method + */ +export function getExternalLoginConfirmationType( + authMethodType: AuthMethodType +) { + return authMethodsMap.get(authMethodType); +} diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html new file mode 100644 index 00000000000..e9b5f27f572 --- /dev/null +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html @@ -0,0 +1,29 @@ +
+

{{ 'external-login.confirmation.header' | translate}}

+
+
+ + +
+
+ {{ informationText }} +
+
+
+ + + + + + +
+
+

or

+
+
+ +
+
diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.scss b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts new file mode 100644 index 00000000000..9268ebeb817 --- /dev/null +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts @@ -0,0 +1,89 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExternalLogInComponent } from './external-log-in.component'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { CommonModule } from '@angular/common'; +import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; +import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter, Injector } from '@angular/core'; +import { mockRegistrationDataModel } from '../models/registration-data.mock.model'; +import { By } from '@angular/platform-browser'; +import { of as observableOf } from 'rxjs'; +import { FormBuilder } from '@angular/forms'; + +describe('ExternalLogInComponent', () => { + let component: ExternalLogInComponent; + let fixture: ComponentFixture; + let compiledTemplate: HTMLElement; + const translateServiceStub = { + get: () => observableOf('Mocked Translation Text'), + instant: (key: any) => 'Mocked Translation Text', + onLangChange: new EventEmitter(), + onTranslationChange: new EventEmitter(), + onDefaultLangChange: new EventEmitter() + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ExternalLogInComponent], + providers: [ + { provide: TranslateService, useValue: translateServiceStub }, + { provide: Injector, useValue: {} }, + FormBuilder + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExternalLogInComponent); + component = fixture.componentInstance; + component.registrationData = mockRegistrationDataModel; + compiledTemplate = fixture.nativeElement; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should set registrationType and informationText correctly when email is present', () => { + expect(component.registrationType).toBe('orcid'); + expect(component.informationText).toContain('orcid'); + }); + + it('should render the template to confirm email when registrationData has email', () => { + const selector = compiledTemplate.querySelector('ds-confirm-email'); + expect(selector).toBeTruthy(); + }); + + it('should render the template with provide email component when registrationData email is null', () => { + component.registrationData.email = null; + fixture.detectChanges(); + const provideEmailComponent = compiledTemplate.querySelector('ds-provide-email'); + expect(provideEmailComponent).toBeTruthy(); + }); + + it('should render the template with log-in component', () => { + const logInComponent = compiledTemplate.querySelector('ds-log-in'); + expect(logInComponent).toBeTruthy(); + }); + + it('should render the template with the translated informationText', () => { + component.informationText = 'Mocked Translation Text'; + fixture.detectChanges(); + const infoText = fixture.debugElement.query(By.css('[data-test="info-text"]')); + expect(infoText.nativeElement.innerHTML).toContain('Mocked Translation Text'); + }); +}); + + diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts new file mode 100644 index 00000000000..091f3750398 --- /dev/null +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts @@ -0,0 +1,91 @@ +import { Component, OnInit, ChangeDetectionStrategy, Input, Injector } from '@angular/core'; +import { getExternalLoginConfirmationType } from '../external-log-in.methods-decorator'; +import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; +import { RegistrationData } from '../models/registration-data.model'; +import { hasValue } from '../../empty.util'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'ds-external-log-in', + templateUrl: './external-log-in.component.html', + styleUrls: ['./external-log-in.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ExternalLogInComponent implements OnInit { + /** + * The type of registration type to be confirmed + */ + registrationType: AuthMethodType = AuthMethodType.Orcid; + /** + * The registration data object + */ + @Input() registrationData: RegistrationData; + /** + * The token to be used to confirm the registration + * @memberof ExternalLogInComponent + */ + @Input() token: string; + /** + * The information text to be displayed, + * depending on the registration type and the presence of an email + * @memberof ExternalLogInComponent + */ + public informationText = ''; + /** + * Injector to inject a registration data to the component with the @Input registrationType + * @type {Injector} + */ + public objectInjector: Injector; + + constructor( + private injector: Injector, + private translate: TranslateService, + ) { + } + + /** + * Provide the registration data object to the objectInjector. + * Generate the information text to be displayed. + */ + ngOnInit(): void { + this.objectInjector = Injector.create({ + providers: [ + { provide: 'registrationDataProvider', useFactory: () => (this.registrationData), deps: [] }, + ], + parent: this.injector + }); + this.registrationType = this.registrationData?.registrationType ?? null; + this.informationText = hasValue(this.registrationData?.email) + ? this.generateInformationTextWhenEmail(this.registrationType) + : this.generateInformationTextWhenNOEmail(this.registrationType); + } + + /** + * Generate the information text to be displayed when the user has no email + * @param authMethod the registration type + */ + private generateInformationTextWhenNOEmail(authMethod: string): string { + if (authMethod) { + const authMethodUppercase = authMethod.toUpperCase(); + return this.translate.instant('external-login.noEmail.informationText', { authMethod: authMethodUppercase }); + } + } + + /** + * Generate the information text to be displayed when the user has an email + * @param authMethod the registration type + */ + private generateInformationTextWhenEmail(authMethod: string): string { + if (authMethod) { + const authMethodUppercase = authMethod.toUpperCase(); + return this.translate.instant('external-login.haveEmail.informationText', { authMethod: authMethodUppercase }); + } + } + + /** + * Get the registration type to be rendered + */ + getExternalLoginConfirmationType() { + return getExternalLoginConfirmationType(this.registrationType); + } +} diff --git a/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts b/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts new file mode 100644 index 00000000000..5d03342e0f4 --- /dev/null +++ b/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts @@ -0,0 +1,22 @@ +import { Component, Inject } from '@angular/core'; +import { RegistrationData } from './models/registration-data.model'; + +/** + * This component renders a form to complete the registration process + */ +@Component({ + template: '' +}) +export abstract class ExternalLoginMethodEntryComponent { + + /** + * The registration data object + */ + public registratioData: RegistrationData; + + constructor( + @Inject('registrationDataProvider') protected injectedRegistrationDataObject: RegistrationData, + ) { + this.registratioData = injectedRegistrationDataObject; + } +} diff --git a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts new file mode 100644 index 00000000000..3fdf739e27e --- /dev/null +++ b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts @@ -0,0 +1,31 @@ +import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; +import { RegistrationData } from './registration-data.model'; +import { MetadataValue } from 'src/app/core/shared/metadata.models'; + +export const mockRegistrationDataModel: RegistrationData = Object.assign( new RegistrationData(), { + id: '3', + email: 'user@institution.edu', + user: '028dcbb8-0da2-4122-a0ea-254be49ca107', + registrationType: AuthMethodType.Orcid, + netId: '<:orcid>', + registrationMetadata: { + 'eperson.firstname': [ + Object.assign(new MetadataValue(), { + value: 'Power', + language: null, + authority: '', + confidence: -1, + place: -1, + }) + ], + 'eperson.lastname': [ + Object.assign(new MetadataValue(), { + value: 'User', + language: null, + authority: '', + confidence: -1, + place: -1 + }) + ] + } +}); diff --git a/src/app/shared/external-log-in-complete/models/registration-data.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.model.ts new file mode 100644 index 00000000000..4d58c2a13bc --- /dev/null +++ b/src/app/shared/external-log-in-complete/models/registration-data.model.ts @@ -0,0 +1,70 @@ +import { CacheableObject } from '../../../core/cache/cacheable-object.model'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { REGISTRATION_DATA } from './registration-data.resource-type'; +import { autoserialize, deserialize } from 'cerialize'; +import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; +import { ResourceType } from '../../../core/shared/resource-type'; +import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; +import { MetadataMap } from '../../../core/shared/metadata.models'; +import { HALLink } from '../../../core/shared/hal-link.model'; + +/** + * Object that represents the authenticated status of a user + */ +@typedObject +export class RegistrationData implements CacheableObject { + + static type = REGISTRATION_DATA; + + /** + * The unique identifier of this registration data + */ + @autoserialize + id: string; + + /** + * The type for this RegistrationData + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + + /** + * The registered email address + */ + @autoserialize + email: string; + + /** + * The registered user identifier + */ + @autoserialize + user: string; + + /** + * The registration type (e.g. orcid, shibboleth, etc.) + */ + @autoserialize + registrationType?: AuthMethodType; + + /** + * The netId of the user (e.g. for ORCID - <:orcid>) + */ + @autoserialize + netId?: string; + + + /** + * The metadata involved during the registration process + */ + @autoserialize + registrationMetadata?: MetadataMap; + + /** + * The {@link HALLink}s for this RegistrationData + */ + @deserialize + _links: { + self: HALLink; + }; +} diff --git a/src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts b/src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts new file mode 100644 index 00000000000..2a387c3ca38 --- /dev/null +++ b/src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from '../../../core/shared/resource-type'; + +/** + * The resource type for RegistrationData + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const REGISTRATION_DATA = new ResourceType('registration'); diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html new file mode 100644 index 00000000000..4c81c7fdb15 --- /dev/null +++ b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html @@ -0,0 +1,35 @@ + diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.scss b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts new file mode 100644 index 00000000000..3c856e6f283 --- /dev/null +++ b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts @@ -0,0 +1,74 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OrcidConfirmationComponent } from './orcid-confirmation.component'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { mockRegistrationDataModel } from '../../models/registration-data.mock.model'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { BrowserOnlyMockPipe } from '../../../../shared/testing/browser-only-mock.pipe'; + +describe('OrcidConfirmationComponent', () => { + let component: OrcidConfirmationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + OrcidConfirmationComponent, + BrowserOnlyMockPipe, + ], + providers: [ + FormBuilder, + { provide: 'registrationDataProvider', useValue: mockRegistrationDataModel }, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(OrcidConfirmationComponent); + component = fixture.componentInstance; + component.registratioData = mockRegistrationDataModel; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize the form with disabled fields', () => { + expect(component.form).toBeInstanceOf(FormGroup); + expect(component.form.controls.netId.disabled).toBeTrue(); + expect(component.form.controls.firstname.disabled).toBeTrue(); + expect(component.form.controls.lastname.disabled).toBeTrue(); + expect(component.form.controls.email.disabled).toBeTrue(); + }); + + + it('should initialize the form with null email as an empty string', () => { + component.registratioData.email = null; + fixture.detectChanges(); + component.ngOnInit(); + const emailFormControl = component.form.get('email'); + expect(emailFormControl.value).toBe(''); + }); + + it('should not render email input when email is null', () => { + component.registratioData.email = null; + fixture.detectChanges(); + const emailInput = fixture.nativeElement.querySelector('input[type="email"]'); + expect(emailInput).toBeFalsy(); + }); +}); diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts new file mode 100644 index 00000000000..0c93b668ec7 --- /dev/null +++ b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts @@ -0,0 +1,60 @@ +import { Component, OnInit, ChangeDetectionStrategy, Inject } from '@angular/core'; +import { renderExternalLoginConfirmationFor } from '../../external-log-in.methods-decorator'; +import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { RegistrationData } from '../../models/registration-data.model'; +import { ExternalLoginMethodEntryComponent } from '../../external-login-method-entry.component'; + +@Component({ + selector: 'ds-orcid-confirmation', + templateUrl: './orcid-confirmation.component.html', + styleUrls: ['./orcid-confirmation.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +@renderExternalLoginConfirmationFor(AuthMethodType.Orcid) +export class OrcidConfirmationComponent extends ExternalLoginMethodEntryComponent implements OnInit { + + /** + * The form containing the user's data + */ + public form: FormGroup; + + /** + * @param injectedRegistrationDataObject RegistrationData object provided + * @param formBuilder To build the form + */ + constructor( + @Inject('registrationDataProvider') protected injectedRegistrationDataObject: RegistrationData, + private formBuilder: FormBuilder + ) { + super(injectedRegistrationDataObject); + } + + /** + * Initialize the form with disabled fields + */ + ngOnInit(): void { + this.form = this.formBuilder.group({ + netId: [{ value: this.registratioData.netId, disabled: true }], + firstname: [{ value: this.getFirstname(), disabled: true }], + lastname: [{ value: this.getLastname(), disabled: true }], + email: [{ value: this.registratioData?.email || '', disabled: true }], // email can be null + }); + } + + /** + * Get the firstname of the user from the registration metadata + * @returns the firstname of the user + */ + private getFirstname(): string { + return this.registratioData.registrationMetadata?.['eperson.firstname']?.[0]?.value || ''; + } + + /** + * Get the lastname of the user from the registration metadata + * @returns the lastname of the user + */ + private getLastname(): string { + return this.registratioData.registrationMetadata?.['eperson.lastname']?.[0]?.value || ''; + } +} diff --git a/src/app/shared/log-in/container/log-in-container.component.html b/src/app/shared/log-in/container/log-in-container.component.html index bef6f43b667..3b6ea5d054c 100644 --- a/src/app/shared/log-in/container/log-in-container.component.html +++ b/src/app/shared/log-in/container/log-in-container.component.html @@ -2,4 +2,3 @@ *ngComponentOutlet="getAuthMethodContent(); injector: objectInjector;"> - diff --git a/src/app/shared/log-in/log-in.component.html b/src/app/shared/log-in/log-in.component.html index 36f7034f4d7..11d306c1bc4 100644 --- a/src/app/shared/log-in/log-in.component.html +++ b/src/app/shared/log-in/log-in.component.html @@ -7,7 +7,9 @@ - - {{"login.form.new-user" | translate}} - {{"login.form.forgot-password" | translate}} + + + {{"login.form.new-user" | translate}} + {{"login.form.forgot-password" | translate}} +
diff --git a/src/app/shared/log-in/log-in.component.ts b/src/app/shared/log-in/log-in.component.ts index 94175f0f6c3..1fe595c1ed2 100644 --- a/src/app/shared/log-in/log-in.component.ts +++ b/src/app/shared/log-in/log-in.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { Observable, Subscription } from 'rxjs'; +import { Observable, Subscription, take } from 'rxjs'; import { select, Store } from '@ngrx/store'; import uniqBy from 'lodash/uniqBy'; @@ -18,6 +18,7 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { CoreState } from '../../core/core-state.model'; import { AuthMethodType } from '../../core/auth/models/auth.method-type'; +import de from 'date-fns/esm/locale/de/index.js'; /** * /users/sign-in @@ -36,6 +37,10 @@ export class LogInComponent implements OnInit, OnDestroy { */ @Input() isStandalonePage: boolean; + @Input() excludedAuthMethod: AuthMethodType; + + @Input() showRegisterLink = true; + /** * The list of authentication methods available * @type {AuthMethod[]} @@ -77,6 +82,10 @@ export class LogInComponent implements OnInit, OnDestroy { ).subscribe(methods => { // ignore the ip authentication method when it's returned by the backend this.authMethods = uniqBy(methods.filter(a => a.authMethodType !== AuthMethodType.Ip), 'authMethodType'); + // exclude the given auth method in case there is one + if (hasValue(this.excludedAuthMethod)) { + this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType !== this.excludedAuthMethod); + } }); // set loading diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 4f07cfdeefd..3ad876a114e 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -332,6 +332,12 @@ import { EntityIconDirective } from './entity-icon/entity-icon.directive'; import { AdditionalMetadataComponent } from './object-list/search-result-list-element/additional-metadata/additional-metadata.component'; +import { ExternalLogInComponent } from './external-log-in-complete/external-log-in/external-log-in.component'; +import { OrcidConfirmationComponent } from './external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component'; +import { ProvideEmailComponent } from './external-log-in-complete/email-confirmation/provide-email/provide-email.component'; +import { ConfirmEmailComponent } from './external-log-in-complete/email-confirmation/confirm-email/confirm-email.component'; +import { ConfirmationSentComponent } from './external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component'; +import { EmailValidatedComponent } from './external-log-in-complete/email-confirmation/email-validated/email-validated.component'; const MODULES = [ CommonModule, @@ -469,7 +475,8 @@ const COMPONENTS = [ MetadataLinkViewComponent, ExportExcelSelectorComponent, ThemedBrowseMostElementsComponent, - SearchChartBarHorizontalComponent + SearchChartBarHorizontalComponent, + ExternalLogInComponent, ]; const ENTRY_COMPONENTS = [ @@ -543,7 +550,8 @@ const ENTRY_COMPONENTS = [ ThemedBrowseMostElementsComponent, SearchChartBarHorizontalComponent, RelationshipsListComponent, - AdditionalMetadataComponent + AdditionalMetadataComponent, + OrcidConfirmationComponent, ]; const PROVIDERS = [ @@ -583,6 +591,10 @@ const DIRECTIVES = [ ...COMPONENTS, ...ENTRY_COMPONENTS, ...DIRECTIVES, + ProvideEmailComponent, + ConfirmEmailComponent, + ConfirmationSentComponent, + EmailValidatedComponent, ], providers: [ ...PROVIDERS diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 0b4dcd84bd4..faddb3a2b0e 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7167,5 +7167,31 @@ "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - "admin.system-wide-alert.title": "System-wide Alerts" + "admin.system-wide-alert.title": "System-wide Alerts", + + "external-login.confirmation.header": "Information needed to complete the login process", + + "external-login.noEmail.informationText": "The information received from {{authMethod}} are not sufficient to complete the login process. Please provide the missing information below, or login via a different method to associate your {{authMethod}} to an existing account.", + + "external-login.haveEmail.informationText": "It seems that you have not yet an account in this system. If this is the case, please confirm the data received from {{authMethod}} and a new account will be created for you. Otherwise, if you already have an account in the system, please update the email address to match the one already in use in the system or login via a different method to associate your {{authMethod}} to your existing account.", + + "external-login.confirm-email.header": "Confirm or update email", + + "external-login.confirmation.email-required": "Email is required.", + + "external-login.confirmation.email-invalid": "Invalid email format.", + + "external-login.confirm.button.label": "Confirm this email", + + "external-login.confirm-email-sent.header": "Confirmation email sent", + + "external-login.confirm-email-sent.info": " We have sent an emait to the provided address to validate your input.
Please follow the instructions in the email to complete the login process.", + + "external-login.validated-email.header": "Email validated", + + "external-login.validated-email.info": "Your email has been validated.
You can now login in the system with your prefered authentication method.", + + "external-login.provide-email.header": "Provide email", + + "external-login.provide-email.button.label": "Send Verification link", } From c754c35870cb807c10cf604f86b5b24a3a46f5f2 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 25 Sep 2023 23:51:48 +0200 Subject: [PATCH 119/195] [DURACOM-185] Fix pointer on language dropdown menu --- src/app/shared/lang-switch/lang-switch.component.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/shared/lang-switch/lang-switch.component.scss b/src/app/shared/lang-switch/lang-switch.component.scss index 7b593a9bb59..ea099435a8e 100644 --- a/src/app/shared/lang-switch/lang-switch.component.scss +++ b/src/app/shared/lang-switch/lang-switch.component.scss @@ -9,3 +9,7 @@ color: var(--ds-header-icon-color-hover); } } + +.dropdown-item { + cursor: pointer; +} From 87296bf7bbdb1f1e9a7be8595538c5ff8c5ba5c6 Mon Sep 17 00:00:00 2001 From: Nikita Krivonosov Date: Mon, 25 Sep 2023 19:21:51 +0200 Subject: [PATCH 120/195] [DSC-1079] Pagination on item edit page uses the same page number for each bundle --- .../epeople-registry.component.spec.ts | 5 ++++- .../epeople-registry/epeople-registry.component.ts | 4 +++- .../eperson-form/eperson-form.component.spec.ts | 3 +++ .../eperson-form/eperson-form.component.ts | 4 +++- .../members-list/members-list.component.spec.ts | 3 +++ .../group-form/members-list/members-list.component.ts | 8 +++++--- .../subgroup-list/subgroups-list.component.spec.ts | 3 +++ .../subgroup-list/subgroups-list.component.ts | 8 +++++--- .../group-registry/groups-registry.component.spec.ts | 5 ++++- .../group-registry/groups-registry.component.ts | 4 +++- .../bitstream-formats.component.spec.ts | 11 ++++++++--- .../bitstream-formats/bitstream-formats.component.ts | 4 +++- .../metadata-registry.component.spec.ts | 3 +++ .../metadata-registry/metadata-registry.component.ts | 4 +++- .../metadata-schema/metadata-schema.component.spec.ts | 5 ++++- .../metadata-schema/metadata-schema.component.ts | 4 +++- .../object-audit-overview.component.ts | 6 ++++-- src/app/core/data/lookup-relation.service.spec.ts | 6 +++++- src/app/core/data/lookup-relation.service.ts | 6 ++++-- .../core/data/version-history-data.service.spec.ts | 5 +++++ src/app/core/data/version-history-data.service.ts | 4 +++- src/app/core/shared/search/search.service.spec.ts | 3 +++ .../recent-item-list.component.spec.ts | 3 +++ .../recent-item-list/recent-item-list.component.ts | 4 +++- ...ted-drag-and-drop-bitstream-list.component.spec.ts | 5 ++++- ...aginated-drag-and-drop-bitstream-list.component.ts | 6 ++++-- .../file-section/full-file-section.component.spec.ts | 3 +++ .../file-section/full-file-section.component.ts | 6 ++++-- .../orcid-queue/orcid-queue.component.spec.ts | 3 +++ .../orcid-page/orcid-queue/orcid-queue.component.ts | 4 +++- .../versions/item-versions.component.spec.ts | 3 +++ src/app/item-page/versions/item-versions.component.ts | 4 +++- .../my-dspace-configuration.service.spec.ts | 4 +++- .../my-dspace-page/my-dspace-configuration.service.ts | 6 ++++-- .../events/openaire-broker-events.component.spec.ts | 3 +++ .../broker/events/openaire-broker-events.component.ts | 6 ++++-- .../topics/openaire-broker-topics.component.spec.ts | 3 +++ .../broker/topics/openaire-broker-topics.component.ts | 5 ++++- .../suggestion-targets.component.ts | 7 +++++-- .../overview/process-overview.component.spec.ts | 5 ++++- .../overview/process-overview.component.ts | 6 ++++-- .../counters-section/counters-section.component.ts | 7 +++++-- ...ternal-source-entry-import-modal.component.spec.ts | 5 ++++- .../external-source-entry-import-modal.component.ts | 6 ++++-- ...act-paginated-drag-and-drop-list.component.spec.ts | 11 ++++++++--- ...abstract-paginated-drag-and-drop-list.component.ts | 6 ++++-- .../item-export-list.component.spec.ts | 3 +++ .../item-export-list/item-export-list.component.ts | 6 ++++-- .../item-export/item-export.component.spec.ts | 5 ++++- .../item-export/item-export/item-export.component.ts | 6 ++++-- .../submission-import-external.component.spec.ts | 3 +++ .../submission-import-external.component.ts | 4 +++- .../subscriptions-page.component.spec.ts | 5 ++++- .../subscriptions-page.component.ts | 6 ++++-- .../suggestions-page.component.spec.ts | 3 +++ .../suggestions-page/suggestions-page.component.ts | 6 ++++-- .../reviewers-list/reviewers-list.component.spec.ts | 3 +++ .../reviewers-list/reviewers-list.component.ts | 6 ++++-- 58 files changed, 219 insertions(+), 66 deletions(-) diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts b/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts index c0d70fd0b25..c3a6fc8bbce 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts +++ b/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts @@ -27,6 +27,8 @@ import { RequestService } from '../../core/data/request.service'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { FindListOptions } from '../../core/data/find-list-options.model'; +import { UUIDService } from '../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; describe('EPeopleRegistryComponent', () => { let component: EPeopleRegistryComponent; @@ -138,7 +140,8 @@ describe('EPeopleRegistryComponent', () => { { provide: FormBuilderService, useValue: builderService }, { provide: Router, useValue: new RouterStub() }, { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.ts b/src/app/access-control/epeople-registry/epeople-registry.component.ts index 55233d8173d..f600fef0b23 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.ts +++ b/src/app/access-control/epeople-registry/epeople-registry.component.ts @@ -21,6 +21,7 @@ import { RequestService } from '../../core/data/request.service'; import { PageInfo } from '../../core/shared/page-info.model'; import { NoContent } from '../../core/shared/NoContent.model'; import { PaginationService } from '../../core/pagination/pagination.service'; +import { UUIDService } from '../../core/shared/uuid.service'; @Component({ selector: 'ds-epeople-registry', @@ -58,7 +59,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy { * Pagination config used to display the list of epeople */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'elp', + id: this.uuidService.generate(), pageSize: 5, currentPage: 1 }); @@ -93,6 +94,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy { private router: Router, private modalService: NgbModal, private paginationService: PaginationService, + private uuidService: UUIDService, public requestService: RequestService) { this.currentSearchQuery = ''; this.currentSearchScope = 'metadata'; diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts index bd87d924772..64217b1410f 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts @@ -31,6 +31,8 @@ import { PaginationServiceStub } from '../../../shared/testing/pagination-servic import { FindListOptions } from '../../../core/data/find-list-options.model'; import { ValidateEmailNotTaken } from './validators/email-taken.validator'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../shared/mocks/uuid.service.mock'; describe('EPersonFormComponent', () => { let component: EPersonFormComponent; @@ -207,6 +209,7 @@ describe('EPersonFormComponent', () => { { provide: PaginationService, useValue: paginationService }, { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring'])}, { provide: EpersonRegistrationService, useValue: epersonRegistrationService }, + { provide: UUIDService, useValue: getMockUUIDService() }, EPeopleRegistryComponent ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts index c4194473c3d..7e24d76d82b 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts @@ -37,6 +37,7 @@ import { ValidateEmailNotTaken } from './validators/email-taken.validator'; import { Registration } from '../../../core/shared/registration.model'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { TYPE_REQUEST_FORGOT } from '../../../register-email-form/register-email-form.component'; +import { UUIDService } from '../../../core/shared/uuid.service'; @Component({ selector: 'ds-eperson-form', @@ -150,7 +151,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { * Pagination config used to display the list of groups */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'gem', + id: this.uuidService.generate(), pageSize: 5, currentPage: 1 }); @@ -183,6 +184,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { private paginationService: PaginationService, public requestService: RequestService, private epersonRegistrationService: EpersonRegistrationService, + private uuidService: UUIDService ) { this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => { this.epersonInitial = eperson; diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts index b7536177cdf..e2ecd2e4548 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts @@ -28,6 +28,8 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio import { RouterMock } from '../../../../shared/mocks/router.mock'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { UUIDService } from '../../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../../shared/mocks/uuid.service.mock'; describe('MembersListComponent', () => { let component: MembersListComponent; @@ -135,6 +137,7 @@ describe('MembersListComponent', () => { { provide: FormBuilderService, useValue: builderService }, { provide: Router, useValue: new RouterMock() }, { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts index 58d252f0b4a..81fd511d4a6 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts @@ -27,6 +27,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { UUIDService } from '../../../../core/shared/uuid.service'; /** * Keys to keep track of specific subscriptions @@ -105,7 +106,7 @@ export class MembersListComponent implements OnInit, OnDestroy { * Pagination config used to display the list of EPeople that are result of EPeople search */ configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'sml', + id: this.uuidService.generate(), pageSize: 5, currentPage: 1 }); @@ -113,7 +114,7 @@ export class MembersListComponent implements OnInit, OnDestroy { * Pagination config used to display the list of EPerson Membes of active group being edited */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'ml', + id: this.uuidService.generate(), pageSize: 5, currentPage: 1 }); @@ -143,7 +144,8 @@ export class MembersListComponent implements OnInit, OnDestroy { protected notificationsService: NotificationsService, protected formBuilder: FormBuilder, protected paginationService: PaginationService, - private router: Router + private router: Router, + protected uuidService: UUIDService ) { this.currentSearchQuery = ''; this.currentSearchScope = 'metadata'; diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts index 1ca6c88c5f7..bd7e543a218 100644 --- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts @@ -37,6 +37,8 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio import { map } from 'rxjs/operators'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { UUIDService } from '../../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../../shared/mocks/uuid.service.mock'; describe('SubgroupsListComponent', () => { let component: SubgroupsListComponent; @@ -121,6 +123,7 @@ describe('SubgroupsListComponent', () => { { provide: FormBuilderService, useValue: builderService }, { provide: Router, useValue: routerStub }, { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts index 5f1700e07d5..cbe230c2676 100644 --- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts +++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts @@ -18,6 +18,7 @@ import { PaginationComponentOptions } from '../../../../shared/pagination/pagina import { NoContent } from '../../../../core/shared/NoContent.model'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { followLink } from '../../../../shared/utils/follow-link-config.model'; +import { UUIDService } from '../../../../core/shared/uuid.service'; /** * Keys to keep track of specific subscriptions @@ -58,7 +59,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy { * Pagination config used to display the list of groups that are result of groups search */ configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'ssgl', + id: this.uuidService.generate(), pageSize: 5, currentPage: 1 }); @@ -66,7 +67,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy { * Pagination config used to display the list of subgroups of currently active group being edited */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'sgl', + id: this.uuidService.generate(), pageSize: 5, currentPage: 1 }); @@ -88,7 +89,8 @@ export class SubgroupsListComponent implements OnInit, OnDestroy { private notificationsService: NotificationsService, private formBuilder: FormBuilder, private paginationService: PaginationService, - private router: Router) { + private router: Router, + private uuidService: UUIDService) { this.currentSearchQuery = ''; } diff --git a/src/app/access-control/group-registry/groups-registry.component.spec.ts b/src/app/access-control/group-registry/groups-registry.component.spec.ts index 239939e70d8..b421d950e1f 100644 --- a/src/app/access-control/group-registry/groups-registry.component.spec.ts +++ b/src/app/access-control/group-registry/groups-registry.component.spec.ts @@ -32,6 +32,8 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { NoContent } from '../../core/shared/NoContent.model'; +import { UUIDService } from '../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; describe('GroupRegistryComponent', () => { let component: GroupsRegistryComponent; @@ -179,7 +181,8 @@ describe('GroupRegistryComponent', () => { { provide: Router, useValue: new RouterMock() }, { provide: AuthorizationDataService, useValue: authorizationService }, { provide: PaginationService, useValue: paginationService }, - { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) } + { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/access-control/group-registry/groups-registry.component.ts b/src/app/access-control/group-registry/groups-registry.component.ts index 70c9b22852f..62c14309366 100644 --- a/src/app/access-control/group-registry/groups-registry.component.ts +++ b/src/app/access-control/group-registry/groups-registry.component.ts @@ -37,6 +37,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c import { NoContent } from '../../core/shared/NoContent.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { followLink } from '../../shared/utils/follow-link-config.model'; +import { UUIDService } from '../../core/shared/uuid.service'; @Component({ selector: 'ds-groups-registry', @@ -54,7 +55,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { * Pagination config used to display the list of groups */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'gl', + id: this.uuidService.generate(), pageSize: 5, currentPage: 1 }); @@ -104,6 +105,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { private router: Router, private authorizationService: AuthorizationDataService, private paginationService: PaginationService, + private uuidService: UUIDService, public requestService: RequestService) { this.currentSearchQuery = ''; this.searchForm = this.formBuilder.group(({ diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts index 8a44240b7e2..cda9a67fc47 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts @@ -26,6 +26,8 @@ import { import { createPaginatedList } from '../../../shared/testing/utils.test'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { UUIDService } from '../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../shared/mocks/uuid.service.mock'; describe('BitstreamFormatsComponent', () => { let comp: BitstreamFormatsComponent; @@ -108,7 +110,8 @@ describe('BitstreamFormatsComponent', () => { { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: NotificationsService, useValue: notificationsServiceStub }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ] }).compileComponents(); }; @@ -236,7 +239,8 @@ describe('BitstreamFormatsComponent', () => { { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: NotificationsService, useValue: notificationsServiceStub }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ] }).compileComponents(); } @@ -285,7 +289,8 @@ describe('BitstreamFormatsComponent', () => { { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: NotificationsService, useValue: notificationsServiceStub }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ] }).compileComponents(); } diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts index 162bf2bdb28..022193cdbee 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts @@ -13,6 +13,7 @@ import { NoContent } from '../../../core/shared/NoContent.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { FindListOptions } from '../../../core/data/find-list-options.model'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { UUIDService } from '../../../core/shared/uuid.service'; /** * This component renders a list of bitstream formats @@ -33,7 +34,7 @@ export class BitstreamFormatsComponent implements OnInit, OnDestroy { * Currently simply renders all bitstream formats */ pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'rbp', + id: this.uuidService.generate(), pageSize: 20, pageSizeOptions: [20, 40, 60, 80, 100] }); @@ -43,6 +44,7 @@ export class BitstreamFormatsComponent implements OnInit, OnDestroy { private translateService: TranslateService, private bitstreamFormatService: BitstreamFormatDataService, private paginationService: PaginationService, + private uuidService: UUIDService ) { } diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts index 36ecdaf3162..fecd0644988 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts @@ -23,6 +23,8 @@ import { PaginationServiceStub } from '../../../shared/testing/pagination-servic import { MetadataSchemaExportService } from '../../../shared/metadata-export/metadata-schema-export/metadata-schema-export.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../shared/mocks/uuid.service.mock'; describe('MetadataRegistryComponent', () => { let comp: MetadataRegistryComponent; @@ -79,6 +81,7 @@ describe('MetadataRegistryComponent', () => { { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: PaginationService, useValue: paginationService }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: UUIDService, useValue: getMockUUIDService() }, { provide: MetadataSchemaExportService, useValue: jasmine.createSpyObj('metadataSchemaExportService', { diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts index 0f052ba25ef..ab47a45a5ea 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts @@ -16,6 +16,7 @@ import { PaginationService } from '../../../core/pagination/pagination.service'; import { MetadataSchemaExportService } from '../../../shared/metadata-export/metadata-schema-export/metadata-schema-export.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; @Component({ selector: 'ds-metadata-registry', @@ -37,7 +38,7 @@ export class MetadataRegistryComponent { * Pagination config used to display the list of metadata schemas */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'rm', + id: this.uuidService.generate(), pageSize: 25 }); @@ -50,6 +51,7 @@ export class MetadataRegistryComponent { private notificationsService: NotificationsService, private paginationService: PaginationService, private translateService: TranslateService, + private uuidService: UUIDService, private readonly metadataSchemaExportService: MetadataSchemaExportService) { this.updateSchemas(); } diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts index 2b660a63634..c11ca6c7bb7 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts @@ -25,6 +25,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u import { VarDirective } from '../../../shared/utils/var.directive'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { UUIDService } from '../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../shared/mocks/uuid.service.mock'; describe('MetadataSchemaComponent', () => { let comp: MetadataSchemaComponent; @@ -139,7 +141,8 @@ describe('MetadataSchemaComponent', () => { { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: Router, useValue: new RouterStub() }, { provide: PaginationService, useValue: paginationService }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.ts b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.ts index d0827e6e4d5..0f4f4244b54 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.ts +++ b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.ts @@ -22,6 +22,7 @@ import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from import { toFindListOptions } from '../../../shared/pagination/pagination.utils'; import { NoContent } from '../../../core/shared/NoContent.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; @Component({ selector: 'ds-metadata-schema', @@ -47,7 +48,7 @@ export class MetadataSchemaComponent implements OnInit { * Pagination config used to display the list of metadata fields */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'rm', + id: this.uuidService.generate(), pageSize: 25, pageSizeOptions: [25, 50, 100, 200] }); @@ -62,6 +63,7 @@ export class MetadataSchemaComponent implements OnInit { private notificationsService: NotificationsService, private router: Router, private paginationService: PaginationService, + private uuidService: UUIDService, private translateService: TranslateService) { } diff --git a/src/app/audit-page/object-audit-overview/object-audit-overview.component.ts b/src/app/audit-page/object-audit-overview/object-audit-overview.component.ts index 58fd9615d07..c9512d3a804 100644 --- a/src/app/audit-page/object-audit-overview/object-audit-overview.component.ts +++ b/src/app/audit-page/object-audit-overview/object-audit-overview.component.ts @@ -18,6 +18,7 @@ import { AuthService } from '../../core/auth/auth.service'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { redirectOn4xx } from '../../core/shared/authorized.operators'; +import { UUIDService } from '../../core/shared/uuid.service'; /** * Component displaying a list of all audit about a object in a paginated table @@ -53,7 +54,7 @@ export class ObjectAuditOverviewComponent implements OnInit { * The current pagination configuration for the page */ pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'oop', + id: this.uuidService.generate(), pageSize: 10 }); @@ -68,7 +69,8 @@ export class ObjectAuditOverviewComponent implements OnInit { protected auditService: AuditDataService, protected itemService: ItemDataService, protected authorizationService: AuthorizationDataService, - protected paginationService: PaginationService) { + protected paginationService: PaginationService, + protected uuidService: UUIDService) { } ngOnInit(): void { diff --git a/src/app/core/data/lookup-relation.service.spec.ts b/src/app/core/data/lookup-relation.service.spec.ts index 58598b9870d..8dd99118c49 100644 --- a/src/app/core/data/lookup-relation.service.spec.ts +++ b/src/app/core/data/lookup-relation.service.spec.ts @@ -13,12 +13,15 @@ import { skip, take } from 'rxjs/operators'; import { ExternalSource } from '../shared/external-source.model'; import { RequestService } from './request.service'; import { of as observableOf } from 'rxjs'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; +import { UUIDService } from '../shared/uuid.service'; describe('LookupRelationService', () => { let service: LookupRelationService; let externalSourceService: ExternalSourceDataService; let searchService: SearchService; let requestService: RequestService; + let uuidService: UUIDService; const totalExternal = 8; const optionsWithQuery = new PaginatedSearchOptions({ query: 'test-query' }); @@ -55,7 +58,8 @@ describe('LookupRelationService', () => { getEndpoint: observableOf(searchServiceEndpoint) }); requestService = jasmine.createSpyObj('requestService', ['removeByHrefSubstring']); - service = new LookupRelationService(externalSourceService, searchService, requestService); + uuidService = getMockUUIDService(); + service = new LookupRelationService(externalSourceService, searchService, requestService, uuidService); } beforeEach(() => { diff --git a/src/app/core/data/lookup-relation.service.ts b/src/app/core/data/lookup-relation.service.ts index 7a6bc2358b5..b35d8b26806 100644 --- a/src/app/core/data/lookup-relation.service.ts +++ b/src/app/core/data/lookup-relation.service.ts @@ -15,6 +15,7 @@ import { Injectable } from '@angular/core'; import { ExternalSource } from '../shared/external-source.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { RequestService } from './request.service'; +import { UUIDService } from '../shared/uuid.service'; /** * A service for retrieving local and external entries information during a relation lookup @@ -30,13 +31,14 @@ export class LookupRelationService { * Pagination options for retrieving exactly one result */ private singleResultOptions = Object.assign(new PaginationComponentOptions(), { - id: 'single-result-options', + id: this.uuidService.generate(), pageSize: 1 }); constructor(protected externalSourceService: ExternalSourceDataService, protected searchService: SearchService, - protected requestService: RequestService) { + protected requestService: RequestService, + protected uuidService: UUIDService) { } /** diff --git a/src/app/core/data/version-history-data.service.spec.ts b/src/app/core/data/version-history-data.service.spec.ts index 03bf6c8bcb7..afdc5de728c 100644 --- a/src/app/core/data/version-history-data.service.spec.ts +++ b/src/app/core/data/version-history-data.service.spec.ts @@ -14,6 +14,8 @@ import { createPaginatedList } from '../../shared/testing/utils.test'; import { Item } from '../shared/item.model'; import { of } from 'rxjs'; import SpyObj = jasmine.SpyObj; +import { UUIDService } from '../shared/uuid.service'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; const url = 'fake-url'; @@ -25,6 +27,7 @@ describe('VersionHistoryDataService', () => { let rdbService: RemoteDataBuildService; let objectCache: ObjectCacheService; let versionService: SpyObj; + let uuidService: UUIDService; let halService: any; const versionHistoryId = 'version-history-id'; @@ -109,6 +112,7 @@ describe('VersionHistoryDataService', () => { findListByHref: jasmine.createSpy('findListByHref'), getHistoryFromVersion: jasmine.createSpy('getHistoryFromVersion'), }); + uuidService = getMockUUIDService(); halService = new HALEndpointServiceStub(url); notificationsService = new NotificationsServiceStub(); @@ -118,6 +122,7 @@ describe('VersionHistoryDataService', () => { objectCache, halService, versionService, + uuidService ); } diff --git a/src/app/core/data/version-history-data.service.ts b/src/app/core/data/version-history-data.service.ts index b331c4f2e40..659feb9276d 100644 --- a/src/app/core/data/version-history-data.service.ts +++ b/src/app/core/data/version-history-data.service.ts @@ -25,6 +25,7 @@ import { sendRequest } from '../shared/request.operators'; import { RestRequest } from './rest-request.model'; import { IdentifiableDataService } from './base/identifiable-data.service'; import { dataService } from './base/data-service.decorator'; +import { UUIDService } from '../shared/uuid.service'; /** * Service responsible for handling requests related to the VersionHistory object @@ -40,6 +41,7 @@ export class VersionHistoryDataService extends IdentifiableDataService { { provide: PaginationService, useValue: {} }, { provide: SearchConfigurationService, useValue: searchConfigService }, { provide: Angulartics2, useValue: {} }, + { provide: UUIDService, useValue: getMockUUIDService() }, SearchService ], }); diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.spec.ts b/src/app/home-page/recent-item-list/recent-item-list.component.spec.ts index edcb4f84f83..7070e2f257d 100644 --- a/src/app/home-page/recent-item-list/recent-item-list.component.spec.ts +++ b/src/app/home-page/recent-item-list/recent-item-list.component.spec.ts @@ -14,6 +14,8 @@ import { of as observableOf } from 'rxjs'; import { APP_CONFIG } from '../../../config/app-config.interface'; import { environment } from '../../../environments/environment'; import { PLATFORM_ID } from '@angular/core'; +import { UUIDService } from '../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; describe('RecentItemListComponent', () => { let component: RecentItemListComponent; @@ -47,6 +49,7 @@ describe('RecentItemListComponent', () => { { provide: SearchConfigurationService, useValue: searchConfigServiceStub }, { provide: APP_CONFIG, useValue: environment }, { provide: PLATFORM_ID, useValue: 'browser' }, + { provide: UUIDService, useValue: getMockUUIDService() } ], }) .compileComponents(); diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.ts b/src/app/home-page/recent-item-list/recent-item-list.component.ts index 9a8535d970a..408b79b79bb 100644 --- a/src/app/home-page/recent-item-list/recent-item-list.component.ts +++ b/src/app/home-page/recent-item-list/recent-item-list.component.ts @@ -18,6 +18,7 @@ import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { isPlatformBrowser } from '@angular/common'; import { setPlaceHolderAttributes } from '../../shared/utils/object-list-utils'; import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; +import { UUIDService } from '../../core/shared/uuid.service'; @Component({ selector: 'ds-recent-item-list', @@ -47,12 +48,13 @@ export class RecentItemListComponent implements OnInit { private paginationService: PaginationService, public searchConfigurationService: SearchConfigurationService, protected elementRef: ElementRef, + protected uuidService: UUIDService, @Inject(APP_CONFIG) private appConfig: AppConfig, @Inject(PLATFORM_ID) private platformId: Object, ) { this.paginationConfig = Object.assign(new PaginationComponentOptions(), { - id: 'hp', + id: this.uuidService.generate(), pageSize: environment.homePage.recentSubmissions.pageSize, currentPage: 1, maxSize: 1 diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts index 7317eb93be0..58248aee56d 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts @@ -18,6 +18,8 @@ import { createPaginatedList } from '../../../../../shared/testing/utils.test'; import { RequestService } from '../../../../../core/data/request.service'; import { PaginationService } from '../../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../../shared/testing/pagination-service.stub'; +import { UUIDService } from '../../../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../../../shared/mocks/uuid.service.mock'; describe('PaginatedDragAndDropBitstreamListComponent', () => { let comp: PaginatedDragAndDropBitstreamListComponent; @@ -122,7 +124,8 @@ describe('PaginatedDragAndDropBitstreamListComponent', () => { { provide: BundleDataService, useValue: bundleService }, { provide: ObjectValuesPipe, useValue: objectValuesPipe }, { provide: RequestService, useValue: requestService }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [ NO_ERRORS_SCHEMA ] diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts index 2c81a4e2cb9..b0eb783dc02 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts @@ -12,6 +12,7 @@ import { ObjectValuesPipe } from '../../../../../shared/utils/object-values-pipe import { RequestService } from '../../../../../core/data/request.service'; import { PaginationService } from '../../../../../core/pagination/pagination.service'; import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model'; +import { UUIDService } from '../../../../../core/shared/uuid.service'; @Component({ selector: 'ds-paginated-drag-and-drop-bitstream-list', @@ -40,8 +41,9 @@ export class PaginatedDragAndDropBitstreamListComponent extends AbstractPaginate protected objectValuesPipe: ObjectValuesPipe, protected bundleService: BundleDataService, protected paginationService: PaginationService, - protected requestService: RequestService) { - super(objectUpdatesService, elRef, objectValuesPipe, paginationService); + protected requestService: RequestService, + protected uuidService: UUIDService) { + super(objectUpdatesService, elRef, objectValuesPipe, paginationService, uuidService); } ngOnInit() { diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts index 09ed981358a..b2e956efafb 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts @@ -20,6 +20,8 @@ import { PaginationService } from '../../../../core/pagination/pagination.servic import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; import { APP_CONFIG } from 'src/config/app-config.interface'; import { environment } from 'src/environments/environment'; +import { UUIDService } from '../../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../../shared/mocks/uuid.service.mock'; describe('FullFileSectionComponent', () => { let comp: FullFileSectionComponent; @@ -73,6 +75,7 @@ describe('FullFileSectionComponent', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: PaginationService, useValue: paginationService }, { provide: APP_CONFIG, useValue: environment }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts index 3be0d58c819..0c5a7fda0b9 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts @@ -15,6 +15,7 @@ import { TranslateService } from '@ngx-translate/core'; import { hasValue, isEmpty } from '../../../../shared/empty.util'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; +import { UUIDService } from '../../../../core/shared/uuid.service'; /** * This component renders the file section of the item @@ -36,13 +37,13 @@ export class FullFileSectionComponent extends FileSectionComponent implements On licenses$: Observable>>; originalOptions = Object.assign(new PaginationComponentOptions(), { - id: 'obo', + id: this.uuidService.generate(), currentPage: 1, pageSize: this.appConfig.item.bitstream.pageSize }); licenseOptions = Object.assign(new PaginationComponentOptions(), { - id: 'lbo', + id: this.uuidService.generate(), currentPage: 1, pageSize: this.appConfig.item.bitstream.pageSize }); @@ -52,6 +53,7 @@ export class FullFileSectionComponent extends FileSectionComponent implements On protected notificationsService: NotificationsService, protected translateService: TranslateService, protected paginationService: PaginationService, + protected uuidService: UUIDService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(bitstreamDataService, notificationsService, translateService, appConfig); diff --git a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.spec.ts b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.spec.ts index d0f52531d44..0a12d7ff2b0 100644 --- a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.spec.ts +++ b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.spec.ts @@ -17,6 +17,8 @@ import { PaginatedList } from '../../../core/data/paginated-list.model'; import { By } from '@angular/platform-browser'; import { Item } from '../../../core/shared/item.model'; import { OrcidAuthService } from '../../../core/orcid/orcid-auth.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../shared/mocks/uuid.service.mock'; describe('OrcidQueueComponent test suite', () => { let component: OrcidQueueComponent; @@ -124,6 +126,7 @@ describe('OrcidQueueComponent test suite', () => { { provide: OrcidHistoryDataService, useValue: {} }, { provide: PaginationService, useValue: new PaginationServiceStub() }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts index 4930e976758..9a52d41a69d 100644 --- a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts +++ b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts @@ -18,6 +18,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio import { AlertType } from '../../../shared/alert/aletr-type'; import { Item } from '../../../core/shared/item.model'; import { OrcidAuthService } from '../../../core/orcid/orcid-auth.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; @Component({ selector: 'ds-orcid-queue', @@ -35,7 +36,7 @@ export class OrcidQueueComponent implements OnInit, OnDestroy { * Pagination config used to display the list */ public paginationOptions: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'oqp', + id: this.uuidService.generate(), pageSize: 5 }); @@ -67,6 +68,7 @@ export class OrcidQueueComponent implements OnInit, OnDestroy { private paginationService: PaginationService, private notificationsService: NotificationsService, private orcidHistoryService: OrcidHistoryDataService, + private uuidService: UUIDService ) { } diff --git a/src/app/item-page/versions/item-versions.component.spec.ts b/src/app/item-page/versions/item-versions.component.spec.ts index 999176d996c..c3eff29f8bf 100644 --- a/src/app/item-page/versions/item-versions.component.spec.ts +++ b/src/app/item-page/versions/item-versions.component.spec.ts @@ -29,6 +29,8 @@ import { ConfigurationDataService } from '../../core/data/configuration-data.ser import { Router } from '@angular/router'; import { CommonModule } from '@angular/common'; import { ItemSharedModule } from '../item-shared.module'; +import { UUIDService } from '../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; describe('ItemVersionsComponent', () => { let component: ItemVersionsComponent; @@ -151,6 +153,7 @@ describe('ItemVersionsComponent', () => { {provide: WorkflowItemDataService, useValue: workflowItemDataServiceSpy}, {provide: ConfigurationDataService, useValue: configurationServiceSpy}, { provide: Router, useValue: routerSpy }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/item-page/versions/item-versions.component.ts b/src/app/item-page/versions/item-versions.component.ts index e0fc623f493..defe9fe7ddc 100644 --- a/src/app/item-page/versions/item-versions.component.ts +++ b/src/app/item-page/versions/item-versions.component.ts @@ -48,6 +48,7 @@ import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model' import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; import { WorkflowItemDataService } from '../../core/submission/workflowitem-data.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; +import { UUIDService } from '../../core/shared/uuid.service'; @Component({ selector: 'ds-item-versions', @@ -135,7 +136,7 @@ export class ItemVersionsComponent implements OnInit { * Start at page 1 and always use the set page size */ options = Object.assign(new PaginationComponentOptions(), { - id: 'ivo', + id: this.uuidService.generate(), currentPage: 1, pageSize: this.pageSize }); @@ -181,6 +182,7 @@ export class ItemVersionsComponent implements OnInit { private workspaceItemDataService: WorkspaceitemDataService, private workflowItemDataService: WorkflowItemDataService, private configurationService: ConfigurationDataService, + private uuidService: UUIDService ) { } diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts index 8b0608804df..4cacda957f9 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts @@ -13,6 +13,7 @@ import { PaginationServiceStub } from '../shared/testing/pagination-service.stub import { Context } from '../core/shared/context.model'; import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service.stub'; import { getMockRemoteDataBuildService } from '../shared/mocks/remote-data-build.service.mock'; +import { getMockUUIDService } from '../shared/mocks/uuid.service.mock'; describe('MyDSpaceConfigurationService', () => { let service: MyDSpaceConfigurationService; @@ -50,9 +51,10 @@ describe('MyDSpaceConfigurationService', () => { const halService: any = new HALEndpointServiceStub(''); const requestService: any = {}; const rdb: any = getMockRemoteDataBuildService(); + const uuidService = getMockUUIDService(); beforeEach(() => { - service = new MyDSpaceConfigurationService(roleService, spy, paginationService as any, activatedRoute, linkService, halService, requestService, rdb); + service = new MyDSpaceConfigurationService(roleService, spy, paginationService as any, activatedRoute, linkService, halService, requestService, rdb, uuidService); }); describe('when the scope is called', () => { diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.ts b/src/app/my-dspace-page/my-dspace-configuration.service.ts index 5d86b5fdb70..ce8f691a0ec 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.ts @@ -17,6 +17,7 @@ import { HALEndpointService } from '../core/shared/hal-endpoint.service'; import { RequestService } from '../core/data/request.service'; import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service'; import { Context } from '../core/shared/context.model'; +import { UUIDService } from '../core/shared/uuid.service'; export const MyDSpaceConfigurationToContextMap = new Map([ [MyDSpaceConfigurationValueType.Workspace, Context.Workspace], @@ -35,7 +36,7 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { * Default pagination settings */ protected defaultPagination = Object.assign(new PaginationComponentOptions(), { - id: 'mydspace-page', + id: this.uuidService.generate(), pageSize: 10, currentPage: 1 }); @@ -83,7 +84,8 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { protected linkService: LinkService, protected halService: HALEndpointService, protected requestService: RequestService, - protected rdb: RemoteDataBuildService) { + protected rdb: RemoteDataBuildService, + protected uuidService: UUIDService) { super(routeService, paginationService, route, linkService, halService, requestService, rdb); diff --git a/src/app/openaire/broker/events/openaire-broker-events.component.spec.ts b/src/app/openaire/broker/events/openaire-broker-events.component.spec.ts index c863e8e04d5..1aacc38fec6 100644 --- a/src/app/openaire/broker/events/openaire-broker-events.component.spec.ts +++ b/src/app/openaire/broker/events/openaire-broker-events.component.spec.ts @@ -40,6 +40,8 @@ import { FindListOptions } from '../../../core/data/find-list-options.model'; import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { UUIDService } from '../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../shared/mocks/uuid.service.mock'; describe('OpenaireBrokerEventsComponent test suite', () => { let fixture: ComponentFixture; @@ -116,6 +118,7 @@ describe('OpenaireBrokerEventsComponent test suite', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: TranslateService, useValue: getMockTranslateService() }, { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() }, OpenaireBrokerEventsComponent ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/openaire/broker/events/openaire-broker-events.component.ts b/src/app/openaire/broker/events/openaire-broker-events.component.ts index 74d0be2f7d1..1785f56fa62 100644 --- a/src/app/openaire/broker/events/openaire-broker-events.component.ts +++ b/src/app/openaire/broker/events/openaire-broker-events.component.ts @@ -29,6 +29,7 @@ import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { combineLatest } from 'rxjs/internal/observable/combineLatest'; import { Item } from '../../../core/shared/item.model'; +import { UUIDService } from '../../../core/shared/uuid.service'; /** * Component to display the OpenAIRE Broker event list. @@ -44,7 +45,7 @@ export class OpenaireBrokerEventsComponent implements OnInit { * @type {PaginationComponentOptions} */ public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'bep', + id: this.uuidService.generate(), currentPage: 1, pageSize: 10, pageSizeOptions: [5, 10, 20, 40, 60] @@ -124,7 +125,8 @@ export class OpenaireBrokerEventsComponent implements OnInit { private notificationsService: NotificationsService, private openaireBrokerEventRestService: OpenaireBrokerEventRestService, private paginationService: PaginationService, - private translateService: TranslateService + private translateService: TranslateService, + private uuidService: UUIDService ) { } diff --git a/src/app/openaire/broker/topics/openaire-broker-topics.component.spec.ts b/src/app/openaire/broker/topics/openaire-broker-topics.component.spec.ts index 00ea772ff9a..ad7d46eca33 100644 --- a/src/app/openaire/broker/topics/openaire-broker-topics.component.spec.ts +++ b/src/app/openaire/broker/topics/openaire-broker-topics.component.spec.ts @@ -15,6 +15,8 @@ import { OpenaireStateService } from '../../openaire-state.service'; import { cold } from 'jasmine-marbles'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; import { PaginationService } from '../../../core/pagination/pagination.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../shared/mocks/uuid.service.mock'; describe('OpenaireBrokerTopicsComponent test suite', () => { let fixture: ComponentFixture; @@ -43,6 +45,7 @@ describe('OpenaireBrokerTopicsComponent test suite', () => { { provide: OpenaireStateService, useValue: mockOpenaireStateService }, { provide: ActivatedRoute, useValue: { data: observableOf(activatedRouteParams), params: observableOf({}) } }, { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() }, OpenaireBrokerTopicsComponent ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/openaire/broker/topics/openaire-broker-topics.component.ts b/src/app/openaire/broker/topics/openaire-broker-topics.component.ts index 408e21d946f..90255658d90 100644 --- a/src/app/openaire/broker/topics/openaire-broker-topics.component.ts +++ b/src/app/openaire/broker/topics/openaire-broker-topics.component.ts @@ -10,6 +10,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio import { OpenaireStateService } from '../../openaire-state.service'; import { AdminNotificationsOpenaireTopicsPageParams } from '../../../admin/admin-notifications/admin-notifications-openaire-topics-page/admin-notifications-openaire-topics-page-resolver.service'; import { PaginationService } from '../../../core/pagination/pagination.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; /** * Component to display the OpenAIRE Broker topic list. @@ -25,7 +26,7 @@ export class OpenaireBrokerTopicsComponent implements OnInit { * @type {PaginationComponentOptions} */ public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'btp', + id: this.uuidService.generate(), pageSize: 10, pageSizeOptions: [5, 10, 20, 40, 60] }); @@ -52,10 +53,12 @@ export class OpenaireBrokerTopicsComponent implements OnInit { * Initialize the component variables. * @param {PaginationService} paginationService * @param {OpenaireStateService} openaireStateService + * @param {UuidService} uuidService */ constructor( private paginationService: PaginationService, private openaireStateService: OpenaireStateService, + private uuidService: UUIDService ) { } /** diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts index b9ed6c4e87b..0e0744bd6d8 100644 --- a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts @@ -11,6 +11,7 @@ import { SuggestionTargetsStateService } from './suggestion-targets.state.servic import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths'; import { SuggestionsService } from '../suggestions.service'; import { PaginationService } from '../../../core/pagination/pagination.service'; +import { UUIDService } from '../../../core/shared/uuid.service'; /** * Component to display the Suggestion Target list. @@ -32,7 +33,7 @@ export class SuggestionTargetsComponent implements OnInit { * @type {PaginationComponentOptions} */ public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'stp', + id: this.uuidService.generate(), pageSizeOptions: [5, 10, 20, 40, 60] }); @@ -56,12 +57,14 @@ export class SuggestionTargetsComponent implements OnInit { * @param {SuggestionTargetsStateService} suggestionTargetsStateService * @param {SuggestionsService} suggestionService * @param {Router} router + * @param {uuidService} uuidService */ constructor( private paginationService: PaginationService, private suggestionTargetsStateService: SuggestionTargetsStateService, private suggestionService: SuggestionsService, - private router: Router + private router: Router, + private uuidService: UUIDService ) { } diff --git a/src/app/process-page/overview/process-overview.component.spec.ts b/src/app/process-page/overview/process-overview.component.spec.ts index a06a16474ea..d6a9e0ae0c9 100644 --- a/src/app/process-page/overview/process-overview.component.spec.ts +++ b/src/app/process-page/overview/process-overview.component.spec.ts @@ -21,6 +21,8 @@ import { ProcessBulkDeleteService } from './process-bulk-delete.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ProcessStatus } from '../processes/process-status.model'; import { Process } from '../processes/process.model'; +import { UUIDService } from '../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; describe('ProcessOverviewComponent', () => { let component: ProcessOverviewComponent; @@ -146,7 +148,8 @@ describe('ProcessOverviewComponent', () => { { provide: ProcessBulkDeleteService, useValue: processBulkDeleteService }, { provide: NgbModal, useValue: modalService }, { provide: AuthorizationDataService, useValue: authorizationService }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/process-page/overview/process-overview.component.ts b/src/app/process-page/overview/process-overview.component.ts index 9f2fc4742f9..2bd6da5e5d2 100644 --- a/src/app/process-page/overview/process-overview.component.ts +++ b/src/app/process-page/overview/process-overview.component.ts @@ -18,6 +18,7 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; +import { UUIDService } from '../../core/shared/uuid.service'; @Component({ selector: 'ds-process-overview', @@ -44,7 +45,7 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { * The current pagination configuration for the page */ pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'po', + id: this.uuidService.generate(), pageSize: 20 }); @@ -65,7 +66,8 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { protected authorizationService: AuthorizationDataService, protected notificationService: NotificationsService, protected translateService: TranslateService, - public processBulkDeleteService: ProcessBulkDeleteService, + protected uuidService: UUIDService, + public processBulkDeleteService: ProcessBulkDeleteService ) { } diff --git a/src/app/shared/explore/section-component/counters-section/counters-section.component.ts b/src/app/shared/explore/section-component/counters-section/counters-section.component.ts index f4ed8adb04f..db50de91833 100644 --- a/src/app/shared/explore/section-component/counters-section/counters-section.component.ts +++ b/src/app/shared/explore/section-component/counters-section/counters-section.component.ts @@ -12,6 +12,7 @@ import { SectionComponent } from '../../../../core/layout/models/section.model'; import { SearchService } from '../../../../core/shared/search/search.service'; import { PaginatedSearchOptions } from '../../../search/models/paginated-search-options.model'; import { hasValue } from '../../../empty.util'; +import { UUIDService } from '../../../../core/shared/uuid.service'; @Component({ selector: 'ds-counters-section', @@ -30,13 +31,15 @@ export class CountersSectionComponent implements OnInit { isLoading$ = new BehaviorSubject(true); pagination: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'counters-pagination', + id: this.uuidService.generate(), pageSize: 1, currentPage: 1 }); - constructor(private searchService: SearchService, @Inject(NativeWindowService) protected _window: NativeWindowRef,) { + constructor(private searchService: SearchService, + private uuidService: UUIDService, + @Inject(NativeWindowService) protected _window: NativeWindowRef) { } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts index c8711b6b47d..9dd85011bee 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts @@ -15,6 +15,8 @@ import { ItemDataService } from '../../../../../../../core/data/item-data.servic import { NotificationsService } from '../../../../../../notifications/notifications.service'; import { createSuccessfulRemoteDataObject$ } from '../../../../../../remote-data.utils'; import { createPaginatedList } from '../../../../../../testing/utils.test'; +import { UUIDService } from '../../../../../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../../../../mocks/uuid.service.mock'; describe('DsDynamicLookupRelationExternalSourceTabComponent', () => { let component: ExternalSourceEntryImportModalComponent; @@ -74,7 +76,8 @@ describe('DsDynamicLookupRelationExternalSourceTabComponent', () => { { provide: SelectableListService, useValue: selectService }, { provide: NotificationsService, useValue: notificationsService }, { provide: ItemDataService, useValue: itemService }, - { provide: NgbActiveModal, useValue: modalStub } + { provide: NgbActiveModal, useValue: modalStub }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts index bf1f2f7e70c..b0eb0a6ae28 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts @@ -25,6 +25,7 @@ import { TranslateService } from '@ngx-translate/core'; import { ItemType } from '../../../../../../../core/shared/item-relationships/item-type.model'; import { SubmissionImportExternalCollectionComponent } from '../../../../../../../submission/import-external/import-external-collection/submission-import-external-collection.component'; import { CollectionListEntry } from '../../../../../../collection-dropdown/collection-dropdown.component'; +import { UUIDService } from '../../../../../../../core/shared/uuid.service'; /** * The possible types of import for the external entry @@ -155,12 +156,13 @@ export class ExternalSourceEntryImportModalComponent implements OnInit { private selectService: SelectableListService, private itemService: ItemDataService, private notificationsService: NotificationsService, - private translateService: TranslateService) { + private translateService: TranslateService, + private uuidService: UUIDService) { } ngOnInit(): void { this.uri = Metadata.first(this.externalSourceEntry.metadata, 'dc.identifier.uri'); - const pagination = Object.assign(new PaginationComponentOptions(), { id: 'external-entry-import', pageSize: 5 }); + const pagination = Object.assign(new PaginationComponentOptions(), { id: this.uuidService.generate(), pageSize: 5 }); this.searchOptions = Object.assign(new PaginatedSearchOptions({ query: this.externalSourceEntry.value, pagination: pagination })); this.localEntitiesRD$ = this.lookupRelationService.getLocalResults(this.relationship, this.searchOptions); } diff --git a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts index bac6b89583f..4bfd8670354 100644 --- a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts +++ b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts @@ -13,6 +13,8 @@ import { ObjectValuesPipe } from '../utils/object-values-pipe'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; import { FieldUpdates } from '../../core/data/object-updates/field-updates.model'; +import { UUIDService } from '../../core/shared/uuid.service'; +import { getMockUUIDService } from '../mocks/uuid.service.mock'; @Component({ selector: 'ds-mock-paginated-drag-drop-abstract', @@ -25,8 +27,9 @@ class MockAbstractPaginatedDragAndDropListComponent extends AbstractPaginatedDra protected objectValuesPipe: ObjectValuesPipe, protected mockUrl: string, protected paginationService: PaginationService, - protected mockObjectsRD$: Observable>>) { - super(objectUpdatesService, elRef, objectValuesPipe, paginationService); + protected mockObjectsRD$: Observable>>, + protected uuidService: UUIDService) { + super(objectUpdatesService, elRef, objectValuesPipe, paginationService, uuidService); } initializeObjectsRD(): void { @@ -43,6 +46,7 @@ describe('AbstractPaginatedDragAndDropListComponent', () => { let objectUpdatesService: ObjectUpdatesService; let elRef: ElementRef; let objectValuesPipe: ObjectValuesPipe; + let uuidService: UUIDService; const url = 'mock-abstract-paginated-drag-and-drop-list-component'; @@ -76,7 +80,8 @@ describe('AbstractPaginatedDragAndDropListComponent', () => { }); paginationService = new PaginationServiceStub(); objectsRD$ = new BehaviorSubject(objectsRD); - component = new MockAbstractPaginatedDragAndDropListComponent(objectUpdatesService, elRef, objectValuesPipe, url, paginationService, objectsRD$); + uuidService = getMockUUIDService(); + component = new MockAbstractPaginatedDragAndDropListComponent(objectUpdatesService, elRef, objectValuesPipe, url, paginationService, objectsRD$, uuidService); component.paginationComponent = paginationComponent; component.ngOnInit(); }); diff --git a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts index 8dba47566fa..c785959de5f 100644 --- a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts +++ b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts @@ -19,6 +19,7 @@ import { compareArraysUsing } from '../../item-page/simple/item-types/shared/ite import { PaginationService } from '../../core/pagination/pagination.service'; import { FieldUpdate } from '../../core/data/object-updates/field-update.model'; import { FieldUpdates } from '../../core/data/object-updates/field-updates.model'; +import { UUIDService } from '../../core/shared/uuid.service'; /** * Operator used for comparing {@link FieldUpdate}s by their field's UUID @@ -90,7 +91,7 @@ export abstract class AbstractPaginatedDragAndDropListComponent { let component: ItemExportListComponent; @@ -63,6 +65,7 @@ describe('ItemExportListComponent', () => { providers: [ { provide: PaginationService, useValue: paginationService }, { provide: SearchManager, useValue: mockSearchManager }, + { provide: UUIDService, useValue: getMockUUIDService() } ] }) .compileComponents(); diff --git a/src/app/shared/search/item-export/item-export/item-export-list/item-export-list.component.ts b/src/app/shared/search/item-export/item-export/item-export-list/item-export-list.component.ts index 784c3dccafd..e2d916638f1 100644 --- a/src/app/shared/search/item-export/item-export/item-export-list/item-export-list.component.ts +++ b/src/app/shared/search/item-export/item-export/item-export-list/item-export-list.component.ts @@ -13,6 +13,7 @@ import { Context } from '../../../../../core/shared/context.model'; import { PaginationService } from '../../../../../core/pagination/pagination.service'; import { fadeIn } from '../../../../animations/fade'; import { PaginatedSearchOptions } from '../../../models/paginated-search-options.model'; +import { UUIDService } from '../../../../../core/shared/uuid.service'; @Component({ selector: 'ds-item-export-list', @@ -53,7 +54,7 @@ export class ItemExportListComponent implements OnInit { * The initial pagination options */ initialPagination = Object.assign(new PaginationComponentOptions(), { - id: 'el', + id: this.uuidService.generate(), pageSize: 10 }); @@ -64,7 +65,8 @@ export class ItemExportListComponent implements OnInit { constructor( private paginationService: PaginationService, - private searchManager: SearchManager) { + private searchManager: SearchManager, + private uuidService: UUIDService) { } ngOnInit(): void { diff --git a/src/app/shared/search/item-export/item-export/item-export.component.spec.ts b/src/app/shared/search/item-export/item-export/item-export.component.spec.ts index e6675fba273..e692244147f 100644 --- a/src/app/shared/search/item-export/item-export/item-export.component.spec.ts +++ b/src/app/shared/search/item-export/item-export/item-export.component.spec.ts @@ -24,6 +24,8 @@ import { SearchManager } from '../../../../core/browse/search-manager'; import { SearchObjects } from '../../models/search-objects.model'; import { createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; import { PageInfo } from '../../../../core/shared/page-info.model'; +import { UUIDService } from '../../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../mocks/uuid.service.mock'; describe('ItemExportComponent', () => { let component: ItemExportComponent; @@ -125,7 +127,8 @@ describe('ItemExportComponent', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: SelectableListService, useValue: selectService }, { provide: Router, useValue: router }, - { provide: SearchManager, useValue: mockSearchManager } + { provide: SearchManager, useValue: mockSearchManager }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [ NO_ERRORS_SCHEMA diff --git a/src/app/shared/search/item-export/item-export/item-export.component.ts b/src/app/shared/search/item-export/item-export/item-export.component.ts index cfe4da4f058..fb44be6b1bf 100644 --- a/src/app/shared/search/item-export/item-export/item-export.component.ts +++ b/src/app/shared/search/item-export/item-export/item-export.component.ts @@ -26,6 +26,7 @@ import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { RemoteData } from '../../../../core/data/remote-data'; import { SearchObjects } from '../../models/search-objects.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { UUIDService } from '../../../../core/shared/uuid.service'; export enum ExportSelectionMode { All = 'all', @@ -105,7 +106,8 @@ export class ItemExportComponent implements OnInit, OnDestroy { protected translate: TranslateService, public activeModal: NgbActiveModal, private selectableListService: SelectableListService, - private searchManager: SearchManager,) { + private searchManager: SearchManager, + private uuidService: UUIDService) { } ngOnInit() { @@ -260,7 +262,7 @@ export class ItemExportComponent implements OnInit, OnDestroy { Object.assign(new PaginatedSearchOptions({}), this.searchOptions, { fixedFilter: `f.entityType=${this.itemType.label},equals`, pagination: Object.assign(new PaginationComponentOptions(), { - id: 'ex', + id: this.uuidService.generate(), pageSize: 1 }) }) diff --git a/src/app/submission/import-external/submission-import-external.component.spec.ts b/src/app/submission/import-external/submission-import-external.component.spec.ts index f6e1ceb1f94..fc6c3352c01 100644 --- a/src/app/submission/import-external/submission-import-external.component.spec.ts +++ b/src/app/submission/import-external/submission-import-external.component.spec.ts @@ -28,6 +28,8 @@ import { ExternalSourceEntry } from '../../core/shared/external-source-entry.mod import { SubmissionImportExternalPreviewComponent } from './import-external-preview/submission-import-external-preview.component'; import { By } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { UUIDService } from '../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; describe('SubmissionImportExternalComponent test suite', () => { let comp: SubmissionImportExternalComponent; @@ -64,6 +66,7 @@ describe('SubmissionImportExternalComponent test suite', () => { { provide: RouteService, useValue: routeServiceStub }, { provide: Router, useValue: new RouterStub() }, { provide: NgbModal, useValue: ngbModal }, + { provide: UUIDService, useValue: getMockUUIDService() }, SubmissionImportExternalComponent ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/submission/import-external/submission-import-external.component.ts b/src/app/submission/import-external/submission-import-external.component.ts index aeabe1eddf2..580b55a2d84 100644 --- a/src/app/submission/import-external/submission-import-external.component.ts +++ b/src/app/submission/import-external/submission-import-external.component.ts @@ -21,6 +21,7 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { getFinishedRemoteData } from '../../core/shared/operators'; import { NONE_ENTITY_TYPE } from '../../core/shared/item-relationships/item-type.resource-type'; +import { UUIDService } from '../../core/shared/uuid.service'; /** * This component allows to submit a new workspaceitem importing the data from an external source. @@ -71,7 +72,7 @@ export class SubmissionImportExternalComponent implements OnInit, OnDestroy { * The initial pagination options */ public initialPagination = Object.assign(new PaginationComponentOptions(), { - id: 'spc', + id: this.uuidService.generate(), pageSize: 10 }); /** @@ -104,6 +105,7 @@ export class SubmissionImportExternalComponent implements OnInit, OnDestroy { private routeService: RouteService, private router: Router, private modalService: NgbModal, + private uuidService: UUIDService ) { } diff --git a/src/app/subscriptions-page/subscriptions-page.component.spec.ts b/src/app/subscriptions-page/subscriptions-page.component.spec.ts index 4f443924281..d8af192eae9 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.spec.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.spec.ts @@ -26,6 +26,8 @@ import { SubscriptionViewComponent } from '../shared/subscriptions/subscription- import { PageInfo } from '../core/shared/page-info.model'; import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { buildPaginatedList } from '../core/data/paginated-list.model'; +import { UUIDService } from '../core/shared/uuid.service'; +import { getMockUUIDService } from '../shared/mocks/uuid.service.mock'; describe('SubscriptionsPageComponent', () => { let component: SubscriptionsPageComponent; @@ -71,7 +73,8 @@ describe('SubscriptionsPageComponent', () => { { provide: SubscriptionsDataService, useValue: subscriptionServiceStub }, { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, { provide: AuthService, useValue: authServiceStub }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }) diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts index 05c587ba12c..00180230b0e 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -14,6 +14,7 @@ import { EPerson } from '../core/eperson/models/eperson.model'; import { getAllCompletedRemoteData } from '../core/shared/operators'; import { RemoteData } from '../core/data/remote-data'; import { hasValue } from '../shared/empty.util'; +import { UUIDService } from '../core/shared/uuid.service'; @Component({ selector: 'ds-subscriptions-page', @@ -34,7 +35,7 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { * The current pagination configuration for the page */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'elp', + id: this.uuidService.generate(), pageSize: 10, currentPage: 1 }); @@ -57,7 +58,8 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { constructor( private paginationService: PaginationService, private authService: AuthService, - private subscriptionService: SubscriptionsDataService + private subscriptionService: SubscriptionsDataService, + private uuidService: UUIDService ) { } diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index ad518930d98..94f5f82e98c 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -31,6 +31,8 @@ import { TestScheduler } from 'rxjs/testing'; import { getTestScheduler } from 'jasmine-marbles'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; import { PaginationService } from '../core/pagination/pagination.service'; +import { UUIDService } from '../core/shared/uuid.service'; +import { getMockUUIDService } from '../shared/mocks/uuid.service.mock'; describe('SuggestionPageComponent', () => { let component: SuggestionsPageComponent; @@ -80,6 +82,7 @@ describe('SuggestionPageComponent', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: TranslateService, useValue: getMockTranslateService() }, { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() }, SuggestionsPageComponent ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index cde3a33d005..710b68a9585 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -26,6 +26,7 @@ import { PaginationService } from '../core/pagination/pagination.service'; import { FindListOptions } from '../core/data/find-list-options.model'; import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; import { redirectOn4xx } from '../core/shared/authorized.operators'; +import { UUIDService } from '../core/shared/uuid.service'; @Component({ selector: 'ds-suggestion-page', @@ -38,7 +39,7 @@ export class SuggestionsPageComponent implements OnInit { * The pagination configuration */ paginationOptions: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'sp', + id: this.uuidService.generate(), pageSizeOptions: [5, 10, 20, 40, 60] }); @@ -83,7 +84,8 @@ export class SuggestionsPageComponent implements OnInit { private suggestionService: SuggestionsService, private suggestionTargetsStateService: SuggestionTargetsStateService, private translateService: TranslateService, - private workspaceItemService: WorkspaceitemDataService + private workspaceItemService: WorkspaceitemDataService, + private uuidService: UUIDService ) { } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts index 7c8db782ce6..2fe31f6f906 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts @@ -32,6 +32,8 @@ import { RouterMock } from '../../../../shared/mocks/router.mock'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; +import { UUIDService } from '../../../../core/shared/uuid.service'; +import { getMockUUIDService } from '../../../../shared/mocks/uuid.service.mock'; describe('ReviewersListComponent', () => { let component: ReviewersListComponent; @@ -152,6 +154,7 @@ describe('ReviewersListComponent', () => { { provide: FormBuilderService, useValue: builderService }, { provide: Router, useValue: new RouterMock() }, { provide: PaginationService, useValue: paginationService }, + { provide: UUIDService, useValue: getMockUUIDService() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts index 7112a305432..656ade86d79 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts @@ -17,6 +17,7 @@ import { MembersListComponent, EPersonListActionConfig, } from '../../../../access-control/group-registry/group-form/members-list/members-list.component'; +import { UUIDService } from '../../../../core/shared/uuid.service'; /** * Keys to keep track of specific subscriptions @@ -57,8 +58,9 @@ export class ReviewersListComponent extends MembersListComponent implements OnIn notificationsService: NotificationsService, formBuilder: FormBuilder, paginationService: PaginationService, - router: Router) { - super(groupService, ePersonDataService, translateService, notificationsService, formBuilder, paginationService, router); + router: Router, + protected uuidService: UUIDService) { + super(groupService, ePersonDataService, translateService, notificationsService, formBuilder, paginationService, router, uuidService); } ngOnInit() { From d3c8b74b288f9e793b50b85c67bfda80b94254f5 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Mon, 6 Jun 2022 20:24:45 +0530 Subject: [PATCH 121/195] =?UTF-8?q?[DSC-435]=20Nascondere=20la=20sezione?= =?UTF-8?q?=20filtri=20quando=20=C3=A8=20vuota?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search-filter/search-filter.component.ts | 2 +- .../search-filters.component.html | 12 ++++++---- .../search-filters.component.ts | 23 +++++++++++++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts index d1d3bd729d4..5a64c043997 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts @@ -180,6 +180,6 @@ export class SearchFilterComponent implements OnInit { )); } }), - startWith(true)); + startWith(false)); } } diff --git a/src/app/shared/search/search-filters/search-filters.component.html b/src/app/shared/search/search-filters/search-filters.component.html index e392cd2663e..a0595c5eee7 100644 --- a/src/app/shared/search/search-filters/search-filters.component.html +++ b/src/app/shared/search/search-filters/search-filters.component.html @@ -1,7 +1,9 @@ -

{{"search.filters.head" | translate}}

-
-
- +
+

{{"search.filters.head" | translate}}

+
+
+ +
+ {{"search.filters.reset" | translate}}
- {{"search.filters.reset" | translate}} diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index 766939226dd..bdd9729c4d0 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; +import { AfterViewChecked, Component, Inject, Input, OnDestroy, OnInit, ViewChildren } from '@angular/core'; import { Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -23,7 +23,7 @@ import { hasValue } from '../../empty.util'; /** * This component represents the part of the search sidebar that contains filters. */ -export class SearchFiltersComponent implements OnInit, OnDestroy { +export class SearchFiltersComponent implements OnInit, AfterViewChecked, OnDestroy { /** * An observable containing configuration about which filters are shown and how they are shown */ @@ -55,6 +55,16 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { */ @Input() refreshFilters: BehaviorSubject; + /** + * List of element references to all filters + */ + @ViewChildren('searchFilter') searchFilter; + + /** + * counts for the active filters + */ + searchFilterCount = 0; + /** * Link to the search page */ @@ -101,6 +111,15 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { return config ? config.name : undefined; } + ngAfterViewChecked() { + this.searchFilterCount = 0; + this.searchFilter._results.forEach(element => { + if (element.nativeElement?.children[0]?.children.length > 0) { + this.searchFilterCount++; + } + }); + } + ngOnDestroy() { this.subs.forEach((sub) => { if (hasValue(sub)) { From 674d8c8d8e3547d937cc4acb28b6027a08ee36df Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Wed, 8 Jun 2022 19:40:34 +0530 Subject: [PATCH 122/195] [DSC-435] test cases --- .../search-filters.component.spec.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/app/shared/search/search-filters/search-filters.component.spec.ts b/src/app/shared/search/search-filters/search-filters.component.spec.ts index 310a8f33a8f..c3a876c661b 100644 --- a/src/app/shared/search/search-filters/search-filters.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filters.component.spec.ts @@ -10,6 +10,7 @@ import { SearchService } from '../../../core/shared/search/search.service'; import { of as observableOf } from 'rxjs'; import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; +import { By } from '@angular/platform-browser'; describe('SearchFiltersComponent', () => { let comp: SearchFiltersComponent; @@ -66,4 +67,18 @@ describe('SearchFiltersComponent', () => { }); }); + describe('when there are no filters', () => { + beforeEach(() => { + (comp as any).ngOnInit(); + fixture.detectChanges(); + }); + + it('should not render component', () => { + const menu = fixture.debugElement.query(By.css('div.d-none')); + expect(menu).not.toBeNull(); + expect(comp.searchFilterCount).toEqual(0); + }); + + }); + }); From cfa8e866dbe3deb5bb5d2dccd55af4fd97680c9e Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 26 Sep 2023 12:50:31 +0200 Subject: [PATCH 123/195] [DSC-435] Fixes --- .../search/search-filters/search-filters.component.html | 2 +- .../search-filters/search-filters.component.spec.ts | 2 +- .../search/search-filters/search-filters.component.ts | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/shared/search/search-filters/search-filters.component.html b/src/app/shared/search/search-filters/search-filters.component.html index a0595c5eee7..2909d31766e 100644 --- a/src/app/shared/search/search-filters/search-filters.component.html +++ b/src/app/shared/search/search-filters/search-filters.component.html @@ -1,4 +1,4 @@ -
+

{{"search.filters.head" | translate}}

diff --git a/src/app/shared/search/search-filters/search-filters.component.spec.ts b/src/app/shared/search/search-filters/search-filters.component.spec.ts index c3a876c661b..247df67ee53 100644 --- a/src/app/shared/search/search-filters/search-filters.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filters.component.spec.ts @@ -76,7 +76,7 @@ describe('SearchFiltersComponent', () => { it('should not render component', () => { const menu = fixture.debugElement.query(By.css('div.d-none')); expect(menu).not.toBeNull(); - expect(comp.searchFilterCount).toEqual(0); + expect(comp.availableFilters).toBeFalse(); }); }); diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index bdd9729c4d0..c20a7e37d0f 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -63,7 +63,7 @@ export class SearchFiltersComponent implements OnInit, AfterViewChecked, OnDestr /** * counts for the active filters */ - searchFilterCount = 0; + availableFilters = false; /** * Link to the search page @@ -112,11 +112,9 @@ export class SearchFiltersComponent implements OnInit, AfterViewChecked, OnDestr } ngAfterViewChecked() { - this.searchFilterCount = 0; + this.availableFilters = false; this.searchFilter._results.forEach(element => { - if (element.nativeElement?.children[0]?.children.length > 0) { - this.searchFilterCount++; - } + this.availableFilters = element.nativeElement?.children[0]?.children.length > 0; }); } From 64598f121ab216a863825181588b6dcaa55c3c08 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Tue, 26 Sep 2023 14:36:19 +0300 Subject: [PATCH 124/195] [DSC-1248] add new input 'configuration' to searchFormComponent --- .../shared/search-form/search-form.component.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 7ea51e4c1e0..1d29ea8ab37 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -42,8 +42,12 @@ export class SearchFormComponent implements OnInit { /** * The currently selected scope object's UUID */ - @Input() - scope = ''; + @Input() scope = ''; + + /** + * Discovery configuration to be used in search + */ + @Input() configuration: string; selectedScope: BehaviorSubject = new BehaviorSubject(undefined); @@ -99,8 +103,13 @@ export class SearchFormComponent implements OnInit { */ onSubmit(data: any) { if (isNotEmpty(this.scope)) { - data = Object.assign(data, { scope: this.scope }); + data = { ...data, scope: this.scope }; } + + if (isNotEmpty(this.configuration)) { + data = { ...data, configuration: this.configuration }; + } + this.updateSearch(data); this.submitSearch.emit(data); } From 1e4dcf25a8289980760ba8f81e597fa5ef00b4c9 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Tue, 26 Sep 2023 14:37:09 +0300 Subject: [PATCH 125/195] [DSC-1248] set new input 'configuration' to 'site' in search-section.component.html --- .../search-section/search-section.component.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/shared/explore/section-component/search-section/search-section.component.html b/src/app/shared/explore/section-component/search-section/search-section.component.html index 187454424f2..57cf6e66447 100644 --- a/src/app/shared/explore/section-component/search-section/search-section.component.html +++ b/src/app/shared/explore/section-component/search-section/search-section.component.html @@ -27,7 +27,9 @@

{{ 'explore.search-section.' + sectionId
- >
From ff59759c1d0ecbcb2af0d4ca28f4ddb9e8b31ffb Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Tue, 26 Sep 2023 14:38:53 +0300 Subject: [PATCH 126/195] [DSC-1248] update i18n labels --- src/assets/i18n/en.json5 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 5f5b1797b9a..415f397a872 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -5094,6 +5094,8 @@ "search.filters.applied.charts.default.title": "Search Output", + "search.filters.applied.charts.site.title": "Search Output", + "search.filters.applied.charts.RELATION.Person.researchoutputs.title": "Research Output", "search.filters.applied.charts.RELATION.Project.fundings.title": "Fundings", @@ -5353,6 +5355,8 @@ "default.search.results.head": "Search Results", + "site.search.results.head": "Search Results", + "default-relationships.search.results.head": "Search Results", "defaultConfiguration.search.results.head": "Search Results", From 2dba1c65cb8a5a03dde54b8c9b852c55414be30e Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 26 Sep 2023 17:40:59 +0200 Subject: [PATCH 127/195] [CST-10703] new wireframe implementation & validate email page --- src/app/app-routing.module.ts | 10 +++ ...-email-confirmation-page-routing.module.ts | 17 ++++ ...gin-email-confirmation-page.component.html | 3 + ...in-email-confirmation-page.component.scss} | 0 ...-email-confirmation-page.component.spec.ts | 25 ++++++ ...login-email-confirmation-page.component.ts | 8 ++ ...al-login-email-confirmation-page.module.ts | 19 ++++ .../external-login-page-routing.module.ts | 9 +- .../external-login-page.component.html | 1 + .../external-login-page.component.spec.ts | 9 +- .../email-validated.component.html | 2 +- .../email-validated.component.scss | 0 .../email-validated.component.spec.ts | 38 +++----- .../email-validated.component.ts | 21 +++++ ...al-login-validation-page-routing.module.ts | 17 ++++ ...ernal-login-validation-page.component.html | 8 ++ ...ernal-login-validation-page.component.scss | 0 ...al-login-validation-page.component.spec.ts | 25 ++++++ ...xternal-login-validation-page.component.ts | 21 +++++ .../external-login-validation-page.module.ts | 30 +++++++ .../helpers/compare-values.pipe.ts | 15 ++++ .../review-account-info.component.html | 45 ++++++++++ .../review-account-info.component.scss | 13 +++ .../review-account-info.component.spec.ts | 25 ++++++ .../review-account-info.component.ts | 87 +++++++++++++++++++ ...xternal-login-validation-page.component.ts | 25 ++++++ .../confirm-email.component.html | 2 +- .../confirm-email.component.spec.ts | 22 ++++- .../confirmation-sent.component.html | 2 +- .../confirmation-sent.component.spec.ts | 18 ++-- .../email-validated.component.ts | 11 --- .../provide-email.component.html | 2 +- .../provide-email.component.spec.ts | 2 + .../external-log-in.component.html | 40 ++++++--- .../external-log-in.component.spec.ts | 16 ++-- .../external-log-in.component.ts | 55 ++++++++++-- .../models/registration-data.mock.model.ts | 6 +- .../orcid-confirmation.component.spec.ts | 1 + src/app/shared/log-in/log-in.component.html | 4 +- src/app/shared/log-in/log-in.component.ts | 5 +- src/app/shared/shared.module.ts | 8 +- 41 files changed, 578 insertions(+), 89 deletions(-) create mode 100644 src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts create mode 100644 src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.html rename src/app/{shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.scss => external-login-email-confirmation-page/external-login-email-confirmation-page.component.scss} (100%) create mode 100644 src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts create mode 100644 src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.ts create mode 100644 src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts rename src/app/{shared/external-log-in-complete/email-confirmation => external-login-validation-page}/email-validated/email-validated.component.html (73%) create mode 100644 src/app/external-login-validation-page/email-validated/email-validated.component.scss rename src/app/{shared/external-log-in-complete/email-confirmation => external-login-validation-page}/email-validated/email-validated.component.spec.ts (61%) create mode 100644 src/app/external-login-validation-page/email-validated/email-validated.component.ts create mode 100644 src/app/external-login-validation-page/external-login-validation-page-routing.module.ts create mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.html create mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.scss create mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.spec.ts create mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.ts create mode 100644 src/app/external-login-validation-page/external-login-validation-page.module.ts create mode 100644 src/app/external-login-validation-page/helpers/compare-values.pipe.ts create mode 100644 src/app/external-login-validation-page/review-account-info/review-account-info.component.html create mode 100644 src/app/external-login-validation-page/review-account-info/review-account-info.component.scss create mode 100644 src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts create mode 100644 src/app/external-login-validation-page/review-account-info/review-account-info.component.ts create mode 100644 src/app/external-login-validation-page/themed-external-login-validation-page.component.ts delete mode 100644 src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 82af5149d06..d5f3a5aecdc 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -176,6 +176,16 @@ import { RedirectService } from './redirect/redirect.service'; loadChildren: () => import('./external-login-page/external-login-page.module') .then((m) => m.ExternalLoginPageModule) }, + { + path: 'validate-email', + loadChildren: () => import('./external-login-validation-page/external-login-validation-page.module') + .then((m) => m.ExternalLoginValidationPageModule) + }, + { + path: 'email-confirmation', + loadChildren: () => import('./external-login-email-confirmation-page/external-login-email-confirmation-page.module') + .then((m) => m.ExternalLoginEmailConfirmationPageModule) + }, { path: 'logout', loadChildren: () => import('./logout-page/logout-page.module') diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts new file mode 100644 index 00000000000..e242d9ea1a9 --- /dev/null +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; + +const routes: Routes = [ + { + path: '', + pathMatch: 'full', + component: ExternalLoginEmailConfirmationPageComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ExternalLoginEmailConfirmationPageRoutingModule { } diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.html b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.html new file mode 100644 index 00000000000..c1cf46032b4 --- /dev/null +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.scss b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.scss similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.scss rename to src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.scss diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts new file mode 100644 index 00000000000..cd646f25cf0 --- /dev/null +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; + +describe('ExternalLoginEmailConfirmationPageComponent', () => { + let component: ExternalLoginEmailConfirmationPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ExternalLoginEmailConfirmationPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExternalLoginEmailConfirmationPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.ts new file mode 100644 index 00000000000..615de74ea34 --- /dev/null +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './external-login-email-confirmation-page.component.html', + styleUrls: ['./external-login-email-confirmation-page.component.scss'] +}) +export class ExternalLoginEmailConfirmationPageComponent { +} diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts new file mode 100644 index 00000000000..36684f2f367 --- /dev/null +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ExternalLoginEmailConfirmationPageRoutingModule } from './external-login-email-confirmation-page-routing.module'; +import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; +import { SharedModule } from '../shared/shared.module'; + + +@NgModule({ + declarations: [ + ExternalLoginEmailConfirmationPageComponent + ], + imports: [ + CommonModule, + ExternalLoginEmailConfirmationPageRoutingModule, + SharedModule + ] +}) +export class ExternalLoginEmailConfirmationPageModule { } diff --git a/src/app/external-login-page/external-login-page-routing.module.ts b/src/app/external-login-page/external-login-page-routing.module.ts index 4e64d267de8..7adbbb7b034 100644 --- a/src/app/external-login-page/external-login-page-routing.module.ts +++ b/src/app/external-login-page/external-login-page-routing.module.ts @@ -1,7 +1,5 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service'; import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; const routes: Routes = [ @@ -9,17 +7,12 @@ const routes: Routes = [ path: '', pathMatch: 'full', component: ThemedExternalLoginPageComponent, - // resolve: { breadcrumb: I18nBreadcrumbResolver }, - // data: { breadcrumbKey: 'external-login', title: 'login.title' }, }, ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], - providers: [ - I18nBreadcrumbResolver, - I18nBreadcrumbsService - ] + providers: [] }) export class ExternalLoginPageRoutingModule { } diff --git a/src/app/external-login-page/external-login-page.component.html b/src/app/external-login-page/external-login-page.component.html index d2daa24a561..7aa4ad6e837 100644 --- a/src/app/external-login-page/external-login-page.component.html +++ b/src/app/external-login-page/external-login-page.component.html @@ -1,3 +1,4 @@
+ diff --git a/src/app/external-login-page/external-login-page.component.spec.ts b/src/app/external-login-page/external-login-page.component.spec.ts index 56f0d81d189..20f46ae833c 100644 --- a/src/app/external-login-page/external-login-page.component.spec.ts +++ b/src/app/external-login-page/external-login-page.component.spec.ts @@ -1,6 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginPageComponent } from './external-login-page.component'; +import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; +import { Router } from '@angular/router'; +import { RouterMock } from '../shared/mocks/router.mock'; describe('ExternalLoginPageComponent', () => { let component: ExternalLoginPageComponent; @@ -8,7 +11,11 @@ describe('ExternalLoginPageComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ExternalLoginPageComponent ] + declarations: [ ExternalLoginPageComponent ], + providers: [ + { provide: EpersonRegistrationService, useValue: {} }, + { provide: Router, useValue: new RouterMock() }, + ], }) .compileComponents(); }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.html b/src/app/external-login-validation-page/email-validated/email-validated.component.html similarity index 73% rename from src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.html rename to src/app/external-login-validation-page/email-validated/email-validated.component.html index 3fa31b0bd53..133243853bf 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.html +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.html @@ -5,5 +5,5 @@

- +
diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.scss b/src/app/external-login-validation-page/email-validated/email-validated.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.spec.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts similarity index 61% rename from src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.spec.ts rename to src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts index e961e6fb913..d5d2c2794cc 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.spec.ts +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts @@ -2,22 +2,29 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { EmailValidatedComponent } from './email-validated.component'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; -import { BehaviorSubject } from 'rxjs'; -import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock'; +import { of } from 'rxjs'; +import { TranslateLoaderMock } from 'src/app/shared/mocks/translate-loader.mock'; describe('EmailValidatedComponent', () => { let component: EmailValidatedComponent; let fixture: ComponentFixture; let compiledTemplate: HTMLElement; + const translateServiceStub = { + get: () => of('Mocked Translation Text'), + instant: (key: any) => 'Mocked Translation Text', + onLangChange: new EventEmitter(), + onTranslationChange: new EventEmitter(), + onDefaultLangChange: new EventEmitter() + }; + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ EmailValidatedComponent ], providers: [ - { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: TranslateService, useValue: translateServiceStub }, ], imports: [ CommonModule, @@ -46,34 +53,17 @@ describe('EmailValidatedComponent', () => { it('should render translated header', () => { const headerElement = compiledTemplate.querySelector('h4'); - expect(headerElement.textContent).toContain('Mocked Header Translation'); + expect(headerElement.textContent).toContain('Mocked Translation Text'); }); it('should render translated info paragraph', () => { const infoParagraphElement = compiledTemplate.querySelector('p'); - expect(infoParagraphElement.innerHTML).toContain('Mocked Info Translation'); + expect(infoParagraphElement.innerHTML).toBeTruthy(); }); it('should render ds-log-in component', () => { const dsLogInComponent = compiledTemplate.querySelector('ds-log-in'); expect(dsLogInComponent).toBeTruthy(); }); - }); -// Mock the TranslateService -class MockTranslateService { - private translationSubject = new BehaviorSubject({}); - - get(key: string) { - const translations = { - 'external-login.validated-email.header': 'Mocked Header Translation', - 'external-login.validated-email.info': 'Mocked Info Translation', - }; - - this.translationSubject.next(translations); - - // Return an Observable that mimics TranslateService's behavior - return this.translationSubject.asObservable(); - } -} diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.ts new file mode 100644 index 00000000000..0703dbed499 --- /dev/null +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.ts @@ -0,0 +1,21 @@ +import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthService } from '../../core/auth/auth.service'; + +@Component({ + selector: 'ds-email-validated', + templateUrl: './email-validated.component.html', + styleUrls: ['./email-validated.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class EmailValidatedComponent { + constructor(private authService: AuthService, private router: Router) { + // if user is logged in, redirect to home page + // in case user logs in with an existing account + this.authService.isAuthenticated().subscribe((isAuthenticated: boolean) => { + if (isAuthenticated) { + this.router.navigate(['/']); + } + }); + } +} diff --git a/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts b/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts new file mode 100644 index 00000000000..d206eba0fe8 --- /dev/null +++ b/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ThemedExternalLoginValidationPageComponent } from './themed-external-login-validation-page.component'; + +const routes: Routes = [ + { + path: '', + pathMatch: 'full', + component: ThemedExternalLoginValidationPageComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ExternalLoginValidationPageRoutingModule { } diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.html b/src/app/external-login-validation-page/external-login-validation-page.component.html new file mode 100644 index 00000000000..10e7e99c261 --- /dev/null +++ b/src/app/external-login-validation-page/external-login-validation-page.component.html @@ -0,0 +1,8 @@ +
+ + + + + + +
diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.scss b/src/app/external-login-validation-page/external-login-validation-page.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts b/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts new file mode 100644 index 00000000000..3ec5c77409f --- /dev/null +++ b/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; + +describe('ExternalLoginValidationPageComponent', () => { + let component: ExternalLoginValidationPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ExternalLoginValidationPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExternalLoginValidationPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.ts b/src/app/external-login-validation-page/external-login-validation-page.component.ts new file mode 100644 index 00000000000..4a2ab4ef942 --- /dev/null +++ b/src/app/external-login-validation-page/external-login-validation-page.component.ts @@ -0,0 +1,21 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + templateUrl: './external-login-validation-page.component.html', + styleUrls: ['./external-login-validation-page.component.scss'] +}) +export class ExternalLoginValidationPageComponent implements OnInit { + + // temporary variable to test the component + newEmail = false; + + existingEmail = true; + + ngOnInit(): void { + // GET data from validation link + // -> if email address is not used by other user => Email Validated component + // -> if email address is used by other user => Review account information component + console.log('ExternalLoginValidationPageComponent ngOnInit'); + } + +} diff --git a/src/app/external-login-validation-page/external-login-validation-page.module.ts b/src/app/external-login-validation-page/external-login-validation-page.module.ts new file mode 100644 index 00000000000..ba41cf95a1b --- /dev/null +++ b/src/app/external-login-validation-page/external-login-validation-page.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ExternalLoginValidationPageRoutingModule } from './external-login-validation-page-routing.module'; +import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; +import { ThemedExternalLoginValidationPageComponent } from './themed-external-login-validation-page.component'; + +import { UiSwitchModule } from 'ngx-ui-switch'; +import { ReviewAccountInfoComponent } from './review-account-info/review-account-info.component'; +import { EmailValidatedComponent } from './email-validated/email-validated.component'; +import { SharedModule } from '../shared/shared.module'; +import { CompareValuesPipe } from './helpers/compare-values.pipe'; + + +@NgModule({ + declarations: [ + ExternalLoginValidationPageComponent, + ThemedExternalLoginValidationPageComponent, + ReviewAccountInfoComponent, + EmailValidatedComponent, + CompareValuesPipe + ], + imports: [ + CommonModule, + ExternalLoginValidationPageRoutingModule, + SharedModule, + UiSwitchModule, + ] +}) +export class ExternalLoginValidationPageModule { } diff --git a/src/app/external-login-validation-page/helpers/compare-values.pipe.ts b/src/app/external-login-validation-page/helpers/compare-values.pipe.ts new file mode 100644 index 00000000000..aefbe9a0ae0 --- /dev/null +++ b/src/app/external-login-validation-page/helpers/compare-values.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'dsCompareValues' +}) +export class CompareValuesPipe implements PipeTransform { + + transform(receivedValue: string, currentValue: string): unknown { + if (receivedValue === currentValue) { + return ''; + } else { + return currentValue; + } + } +} diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.html b/src/app/external-login-validation-page/review-account-info/review-account-info.component.html new file mode 100644 index 00000000000..23e6282f52c --- /dev/null +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.html @@ -0,0 +1,45 @@ +

Review your account information

+ +

+ The information received from ORCID differs from the one recorded in your + profile.
+ Please review them and decide if you want to update any of them.After saving + you will be redirected to your profile page. +

+
+ + + + + + + + + + + + + + + + + +
+ Information + + Received value + + Current value + Override
{{ data.label }}{{ data.receivedValue }} + + + + +
+
diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.scss b/src/app/external-login-validation-page/review-account-info/review-account-info.component.scss new file mode 100644 index 00000000000..1e531f0d8b9 --- /dev/null +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.scss @@ -0,0 +1,13 @@ +:host { + table { + tbody { + background-color: #f7f8f9; + } + + td, + th { + height: 60px; + vertical-align: middle; + } + } +} diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts new file mode 100644 index 00000000000..7cd24cbeec2 --- /dev/null +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReviewAccountInfoComponent } from './review-account-info.component'; + +describe('ReviewAccountInfoComponent', () => { + let component: ReviewAccountInfoComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ReviewAccountInfoComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ReviewAccountInfoComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts b/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts new file mode 100644 index 00000000000..92e8a07239f --- /dev/null +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts @@ -0,0 +1,87 @@ +import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { EPersonDataService } from '../../core/eperson/eperson-data.service'; +import { EPersonMock } from '../../shared/testing/eperson.mock'; +import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; +import { mockRegistrationDataModel } from '../../shared/external-log-in-complete/models/registration-data.mock.model'; +export interface ReviewAccountInfoData { + label: string; + currentValue: string; + receivedValue: string; + overrideValue: boolean; + identifier: string; +} + +@Component({ + selector: 'ds-review-account-info', + templateUrl: './review-account-info.component.html', + styleUrls: ['./review-account-info.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ReviewAccountInfoComponent implements OnInit { + registeredData: RegistrationData = mockRegistrationDataModel; + + epersonData: EPerson = EPersonMock; + + notApplicable = 'N/A'; + + dataToCompare: ReviewAccountInfoData[] = []; + + constructor(private ePersonService: EPersonDataService) { + // GET data from url validation link and display + } + + ngOnInit(): void { + this.dataToCompare = [ + { + label: this.registeredData.registrationType, + receivedValue: this.registeredData.netId, + currentValue: this.notApplicable, + overrideValue: false, + identifier: 'netId', + }, + { + label: 'Last Name', + receivedValue: this.getReceivedValue('eperson.lastname'), + currentValue: this.getCurrentValue('eperson.lastname'), + overrideValue: false, + identifier: 'eperson.lastname', + }, + { + label: 'First Name', + currentValue: this.getCurrentValue('eperson.firstname'), + receivedValue: this.getReceivedValue('eperson.firstname'), + overrideValue: false, + identifier: 'eperson.firstname', + }, + { + label: 'Email', + currentValue: this.epersonData.email, + receivedValue: this.registeredData.email, + overrideValue: false, + identifier: 'email', + }, + ]; + } + + getEPersonData() { + // this.epersonData$ = this.ePersonService.findById() + // .pipe( + // getFirstCompletedRemoteData(), + // getRemoteDataPayload() + // ); + } + + getReceivedValue(metadata: string): string { + return this.registeredData.registrationMetadata[metadata]?.[0]?.value; + } + + getCurrentValue(metadata: string): string { + return this.epersonData.firstMetadataValue(metadata); + } + + test(value: boolean, identifier: string) { + this.dataToCompare.find((data) => data.identifier === identifier).overrideValue = value; + console.log(this.dataToCompare); + } +} diff --git a/src/app/external-login-validation-page/themed-external-login-validation-page.component.ts b/src/app/external-login-validation-page/themed-external-login-validation-page.component.ts new file mode 100644 index 00000000000..a08b2669deb --- /dev/null +++ b/src/app/external-login-validation-page/themed-external-login-validation-page.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { ThemedComponent } from '../shared/theme-support/themed.component'; +import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; + +/** + * Themed wrapper for ExternalLoginValidationPageComponent + */ +@Component({ + selector: 'ds-themed-external-login-page', + styleUrls: [], + templateUrl: './../shared/theme-support/themed.component.html' +}) +export class ThemedExternalLoginValidationPageComponent extends ThemedComponent { + protected getComponentName(): string { + return 'ExternalLoginValidationPageComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../themes/${themeName}/app/external-login-validation-page/external-login-validation-page.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./external-login-validation-page.component`); + } +} diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html index 97d9869f411..2ff2000d41a 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html @@ -31,7 +31,7 @@

- diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts index 8e426f27fc0..58b2b2414cd 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -1,6 +1,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfirmEmailComponent } from './confirm-email.component'; +import { FormBuilder } from '@angular/forms'; +import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; +import { CommonModule } from '@angular/common'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; describe('ConfirmEmailComponent', () => { let component: ConfirmEmailComponent; @@ -8,7 +14,21 @@ describe('ConfirmEmailComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ConfirmEmailComponent ] + declarations: [ ConfirmEmailComponent ], + providers: [ + FormBuilder, + { provide: EpersonRegistrationService, useValue: {} }, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents(); }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html index f81ebf1a170..3d79b16a4e2 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html @@ -2,4 +2,4 @@

{{ "external-login.confirm-email-sent.header" | translate }}

-

+

diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts index 5106220db62..3de88d9ba26 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts @@ -2,20 +2,29 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfirmationSentComponent } from './confirmation-sent.component'; import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; +import { of } from 'rxjs'; describe('ConfirmationSentComponent', () => { let component: ConfirmationSentComponent; let fixture: ComponentFixture; let compiledTemplate: HTMLElement; + const translateServiceStub = { + get: () => of('Mocked Translation Text'), + instant: (key: any) => 'Mocked Translation Text', + onLangChange: new EventEmitter(), + onTranslationChange: new EventEmitter(), + onDefaultLangChange: new EventEmitter() + }; + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ ConfirmationSentComponent ], providers: [ - { provide: TranslateService, useClass: {} }, + { provide: TranslateService, useValue: translateServiceStub }, ], imports: [ CommonModule, @@ -44,12 +53,11 @@ describe('ConfirmationSentComponent', () => { it('should render translated header', () => { const headerElement = compiledTemplate.querySelector('h4'); - expect(headerElement.textContent).toContain('Mocked Header Translation'); + expect(headerElement.textContent).toContain('Mocked Translation Text'); }); it('should render translated info paragraph', () => { const infoParagraphElement = compiledTemplate.querySelector('p'); - expect(infoParagraphElement.innerHTML).toContain('Mocked Info Translation'); + expect(infoParagraphElement.innerHTML).toBeTruthy(); }); - }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts deleted file mode 100644 index c74cbcfd489..00000000000 --- a/src/app/shared/external-log-in-complete/email-confirmation/email-validated/email-validated.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; - -@Component({ - selector: 'ds-email-validated', - templateUrl: './email-validated.component.html', - styleUrls: ['./email-validated.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class EmailValidatedComponent { - -} diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html index 72c8eb7c88c..b5a2efa7b68 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html @@ -32,7 +32,7 @@

- diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts index 03e28c687d3..63e64830394 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts @@ -6,6 +6,7 @@ import { CommonModule } from '@angular/common'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; describe('ProvideEmailComponent', () => { let component: ProvideEmailComponent; @@ -16,6 +17,7 @@ describe('ProvideEmailComponent', () => { declarations: [ ProvideEmailComponent ], providers: [ FormBuilder, + { provide: EpersonRegistrationService, useValue: {} }, ], imports: [ CommonModule, diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html index e9b5f27f572..59219922470 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html @@ -8,22 +8,42 @@

{{ 'external-login.confirmation.header' | translate}}

{{ informationText }}
-
-
- +
+
+
-
-

or

+
+

or

-
- +
+
+ + + + + + diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts index 9268ebeb817..d29ebdb8513 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts @@ -15,8 +15,8 @@ describe('ExternalLogInComponent', () => { let fixture: ComponentFixture; let compiledTemplate: HTMLElement; const translateServiceStub = { - get: () => observableOf('Mocked Translation Text'), - instant: (key: any) => 'Mocked Translation Text', + get: () => observableOf('Info Text'), + instant: (key: any) => 'Info Text', onLangChange: new EventEmitter(), onTranslationChange: new EventEmitter(), onDefaultLangChange: new EventEmitter() @@ -48,6 +48,7 @@ describe('ExternalLogInComponent', () => { fixture = TestBed.createComponent(ExternalLogInComponent); component = fixture.componentInstance; component.registrationData = mockRegistrationDataModel; + component.registrationType = mockRegistrationDataModel.registrationType; compiledTemplate = fixture.nativeElement; fixture.detectChanges(); }); @@ -58,17 +59,20 @@ describe('ExternalLogInComponent', () => { it('should set registrationType and informationText correctly when email is present', () => { expect(component.registrationType).toBe('orcid'); - expect(component.informationText).toContain('orcid'); + expect(component.informationText).toBeDefined(); }); it('should render the template to confirm email when registrationData has email', () => { + component.registrationData = Object.assign({}, mockRegistrationDataModel, { email: 'test@user.com' }); + fixture.detectChanges(); const selector = compiledTemplate.querySelector('ds-confirm-email'); expect(selector).toBeTruthy(); }); it('should render the template with provide email component when registrationData email is null', () => { - component.registrationData.email = null; + component.registrationData = Object.assign({}, mockRegistrationDataModel, { email: null }); fixture.detectChanges(); + component.ngOnInit(); const provideEmailComponent = compiledTemplate.querySelector('ds-provide-email'); expect(provideEmailComponent).toBeTruthy(); }); @@ -79,10 +83,10 @@ describe('ExternalLogInComponent', () => { }); it('should render the template with the translated informationText', () => { - component.informationText = 'Mocked Translation Text'; + component.informationText = 'Info Text'; fixture.detectChanges(); const infoText = fixture.debugElement.query(By.css('[data-test="info-text"]')); - expect(infoText.nativeElement.innerHTML).toContain('Mocked Translation Text'); + expect(infoText.nativeElement.innerHTML).toContain('Info Text'); }); }); diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts index 091f3750398..3b82ab70ca1 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts @@ -1,21 +1,30 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input, Injector } from '@angular/core'; +import { + Component, + OnInit, + ChangeDetectionStrategy, + Input, + Injector, +} from '@angular/core'; import { getExternalLoginConfirmationType } from '../external-log-in.methods-decorator'; import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; import { RegistrationData } from '../models/registration-data.model'; import { hasValue } from '../../empty.util'; import { TranslateService } from '@ngx-translate/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { AuthService } from '../../../core/auth/auth.service'; +import { Router } from '@angular/router'; @Component({ selector: 'ds-external-log-in', templateUrl: './external-log-in.component.html', styleUrls: ['./external-log-in.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ExternalLogInComponent implements OnInit { /** * The type of registration type to be confirmed */ - registrationType: AuthMethodType = AuthMethodType.Orcid; + registrationType: AuthMethodType; /** * The registration data object */ @@ -37,10 +46,25 @@ export class ExternalLogInComponent implements OnInit { */ public objectInjector: Injector; + /** + * Reference to NgbModal + */ + public modalRef: NgbModalRef; + constructor( private injector: Injector, private translate: TranslateService, + private modalService: NgbModal, + private authService: AuthService, + private router: Router ) { + // if user is logged in, redirect to home page + // in case user logs in with an existing account + this.authService.isAuthenticated().subscribe((isAuthenticated: boolean) => { + if (isAuthenticated) { + this.router.navigate(['/']); + } + }); } /** @@ -50,9 +74,13 @@ export class ExternalLogInComponent implements OnInit { ngOnInit(): void { this.objectInjector = Injector.create({ providers: [ - { provide: 'registrationDataProvider', useFactory: () => (this.registrationData), deps: [] }, + { + provide: 'registrationDataProvider', + useFactory: () => this.registrationData, + deps: [], + }, ], - parent: this.injector + parent: this.injector, }); this.registrationType = this.registrationData?.registrationType ?? null; this.informationText = hasValue(this.registrationData?.email) @@ -67,7 +95,9 @@ export class ExternalLogInComponent implements OnInit { private generateInformationTextWhenNOEmail(authMethod: string): string { if (authMethod) { const authMethodUppercase = authMethod.toUpperCase(); - return this.translate.instant('external-login.noEmail.informationText', { authMethod: authMethodUppercase }); + return this.translate.instant('external-login.noEmail.informationText', { + authMethod: authMethodUppercase, + }); } } @@ -78,7 +108,10 @@ export class ExternalLogInComponent implements OnInit { private generateInformationTextWhenEmail(authMethod: string): string { if (authMethod) { const authMethodUppercase = authMethod.toUpperCase(); - return this.translate.instant('external-login.haveEmail.informationText', { authMethod: authMethodUppercase }); + return this.translate.instant( + 'external-login.haveEmail.informationText', + { authMethod: authMethodUppercase } + ); } } @@ -88,4 +121,12 @@ export class ExternalLogInComponent implements OnInit { getExternalLoginConfirmationType() { return getExternalLoginConfirmationType(this.registrationType); } + + openLoginModal(content: any) { + this.modalRef = this.modalService.open(content); + } + + ngOnDestroy(): void { + this.modalRef?.close(); + } } diff --git a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts index 3fdf739e27e..30b7b8526bc 100644 --- a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts +++ b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts @@ -1,6 +1,6 @@ import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; import { RegistrationData } from './registration-data.model'; -import { MetadataValue } from 'src/app/core/shared/metadata.models'; +import { MetadataValue } from '../../../core/shared/metadata.models'; export const mockRegistrationDataModel: RegistrationData = Object.assign( new RegistrationData(), { id: '3', @@ -11,7 +11,7 @@ export const mockRegistrationDataModel: RegistrationData = Object.assign( new Re registrationMetadata: { 'eperson.firstname': [ Object.assign(new MetadataValue(), { - value: 'Power', + value: 'User', language: null, authority: '', confidence: -1, @@ -20,7 +20,7 @@ export const mockRegistrationDataModel: RegistrationData = Object.assign( new Re ], 'eperson.lastname': [ Object.assign(new MetadataValue(), { - value: 'User', + value: 'Power', language: null, authority: '', confidence: -1, diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts index 3c856e6f283..e3f84663539 100644 --- a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts +++ b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts @@ -68,6 +68,7 @@ describe('OrcidConfirmationComponent', () => { it('should not render email input when email is null', () => { component.registratioData.email = null; fixture.detectChanges(); + component.ngOnInit(); const emailInput = fixture.nativeElement.querySelector('input[type="email"]'); expect(emailInput).toBeFalsy(); }); diff --git a/src/app/shared/log-in/log-in.component.html b/src/app/shared/log-in/log-in.component.html index 11d306c1bc4..7adea60aa1d 100644 --- a/src/app/shared/log-in/log-in.component.html +++ b/src/app/shared/log-in/log-in.component.html @@ -7,9 +7,9 @@ - + - {{"login.form.new-user" | translate}} + {{"login.form.new-user" | translate}} {{"login.form.forgot-password" | translate}}
diff --git a/src/app/shared/log-in/log-in.component.ts b/src/app/shared/log-in/log-in.component.ts index 1fe595c1ed2..18a7d6ddf98 100644 --- a/src/app/shared/log-in/log-in.component.ts +++ b/src/app/shared/log-in/log-in.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { Observable, Subscription, take } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { select, Store } from '@ngrx/store'; import uniqBy from 'lodash/uniqBy'; @@ -18,7 +18,6 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { CoreState } from '../../core/core-state.model'; import { AuthMethodType } from '../../core/auth/models/auth.method-type'; -import de from 'date-fns/esm/locale/de/index.js'; /** * /users/sign-in @@ -41,6 +40,8 @@ export class LogInComponent implements OnInit, OnDestroy { @Input() showRegisterLink = true; + @Input() hideAllLinks = false; + /** * The list of authentication methods available * @type {AuthMethod[]} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 3ad876a114e..65bcd2dfa3c 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -337,7 +337,6 @@ import { OrcidConfirmationComponent } from './external-log-in-complete/registrat import { ProvideEmailComponent } from './external-log-in-complete/email-confirmation/provide-email/provide-email.component'; import { ConfirmEmailComponent } from './external-log-in-complete/email-confirmation/confirm-email/confirm-email.component'; import { ConfirmationSentComponent } from './external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component'; -import { EmailValidatedComponent } from './external-log-in-complete/email-confirmation/email-validated/email-validated.component'; const MODULES = [ CommonModule, @@ -477,6 +476,9 @@ const COMPONENTS = [ ThemedBrowseMostElementsComponent, SearchChartBarHorizontalComponent, ExternalLogInComponent, + ProvideEmailComponent, + ConfirmEmailComponent, + ConfirmationSentComponent, ]; const ENTRY_COMPONENTS = [ @@ -591,10 +593,6 @@ const DIRECTIVES = [ ...COMPONENTS, ...ENTRY_COMPONENTS, ...DIRECTIVES, - ProvideEmailComponent, - ConfirmEmailComponent, - ConfirmationSentComponent, - EmailValidatedComponent, ], providers: [ ...PROVIDERS From d413423c4582507dff6aa68d7aaf749805a13855 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 27 Sep 2023 17:41:08 +0200 Subject: [PATCH 128/195] [CST-10703] (partial commit) merge account information & validate email --- .../core/data/eperson-registration.service.ts | 18 +-- src/app/core/eperson/eperson-data.service.ts | 21 +++ .../external-login-page.component.ts | 9 +- .../email-validated.component.ts | 6 +- ...ernal-login-validation-page.component.html | 11 +- ...xternal-login-validation-page.component.ts | 51 ++++++- .../review-account-info.component.html | 44 ++++-- .../review-account-info.component.ts | 131 ++++++++++++------ .../confirm-email/confirm-email.component.ts | 17 ++- .../provide-email/provide-email.component.ts | 18 +-- .../models/registration-data.mock.model.ts | 2 +- .../services/external-login.service.spec.ts | 16 +++ .../services/external-login.service.ts | 37 +++++ src/assets/i18n/en.json5 | 19 +++ src/assets/i18n/it.json5 | 89 ++++++++++++ 15 files changed, 378 insertions(+), 111 deletions(-) create mode 100644 src/app/shared/external-log-in-complete/services/external-login.service.spec.ts create mode 100644 src/app/shared/external-log-in-complete/services/external-login.service.ts diff --git a/src/app/core/data/eperson-registration.service.ts b/src/app/core/data/eperson-registration.service.ts index e1b77977d6a..3385c520dc5 100644 --- a/src/app/core/data/eperson-registration.service.ts +++ b/src/app/core/data/eperson-registration.service.ts @@ -150,7 +150,7 @@ export class EpersonRegistrationService{ * @param updateValue Flag to indicate if the email should be updated or added * @returns Remote Data state of the patch request */ - patchUpdateRegistration(value: string, field: string, registrationId: string, token: string, updateValue: boolean) { + patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operator: 'add' | 'replace') { const requestId = this.requestService.generateRequestId(); const href$ = this.getRegistrationEndpoint().pipe( @@ -159,7 +159,7 @@ export class EpersonRegistrationService{ ); href$.subscribe((href: string) => { - const operations = this.generateOperations(value, field, updateValue); + const operations = this.generateOperations(values, field, operator); const patchRequest = new PatchRequest(requestId, href, operations); this.requestService.send(patchRequest); }); @@ -173,17 +173,11 @@ export class EpersonRegistrationService{ * @param updateValue Flag to indicate if the email should be updated or added * @returns Operations to be performed on the registration object */ - private generateOperations(value: string, field: string, updateValue: boolean): Operation[] { + private generateOperations(values: string[], field: string, operator: 'add' | 'replace'): Operation[] { let operations = []; - if (hasValue(value) && updateValue) { - operations = [...operations, { - op: 'replace', path: `/${field}`, value: value - }]; - } - - if (hasValue(value) && !updateValue) { - operations = [...operations, { - op: 'add', path: `/${field}`, value: value + if (values.length > 0 && hasValue(field) ) { + operations = [{ + op: operator, path: `/${field}`, value: values }]; } diff --git a/src/app/core/eperson/eperson-data.service.ts b/src/app/core/eperson/eperson-data.service.ts index 87827bcc592..affbe6d045d 100644 --- a/src/app/core/eperson/eperson-data.service.ts +++ b/src/app/core/eperson/eperson-data.service.ts @@ -354,6 +354,27 @@ export class EPersonDataService extends IdentifiableDataService impleme return this.rdbService.buildFromRequestUUID(requestId); } + /** + * Sends a POST request to merge registration data related to the provided registration-token, + * into the eperson related to the provided uuid + * @param uuid the user uuid + * @param token registration-token + * @param metadataKey metadata key of the metadata field that should be overriden + */ + mergeEPersonDataWithToken(uuid: string, token: string, metadataKey: string): Observable> { + const requestId = this.requestService.generateRequestId(); + const hrefObs = this.getBrowseEndpoint().pipe( + map((href: string) => `${href}/${uuid}?token=${token}&override=${metadataKey}`)); + + hrefObs.pipe( + find((href: string) => hasValue(href)), + ).subscribe((href: string) => { + const request = new PostRequest(requestId, href); + this.requestService.send(request); + }); + + return this.rdbService.buildFromRequestUUID(requestId); + } /** * Create a new object on the server, and store the response in the object cache diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index 94ab4424b86..91bd59ed3c9 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { hasValue } from '../shared/empty.util'; import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; import { RemoteData } from '../core/data/remote-data'; @@ -19,15 +19,12 @@ export class ExternalLoginPageComponent implements OnInit { constructor( private epersonRegistrationService: EpersonRegistrationService, - private router: Router, + private arouter: ActivatedRoute, ) { - this.token = this.router.parseUrl(this.router.url).queryParams.token; + this.token = this.arouter.snapshot.queryParams.token; } ngOnInit(): void { - // TODO: call the method getTokenSearchEndpoint (eperson-registration.service.ts ) protected searchByTokenPath = '/search/findByToken?token='; - // token will be provided by the url (returned by REST API) - console.log('ExternalLoginPageComponent ngOnInit'); if (hasValue(this.token)) { this.epersonRegistrationService.searchByToken(this.token).subscribe((registration: RemoteData ) => { diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.ts index 0703dbed499..1ff85244386 100644 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.ts +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.ts @@ -1,7 +1,6 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; import { Router } from '@angular/router'; import { AuthService } from '../../core/auth/auth.service'; - @Component({ selector: 'ds-email-validated', templateUrl: './email-validated.component.html', @@ -9,6 +8,9 @@ import { AuthService } from '../../core/auth/auth.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class EmailValidatedComponent { + + @Input() registrationToken: string; + constructor(private authService: AuthService, private router: Router) { // if user is logged in, redirect to home page // in case user logs in with an existing account diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.html b/src/app/external-login-validation-page/external-login-validation-page.component.html index 10e7e99c261..227dc740988 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.html +++ b/src/app/external-login-validation-page/external-login-validation-page.component.html @@ -1,8 +1,11 @@
- - + + - - + +
diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.ts b/src/app/external-login-validation-page/external-login-validation-page.component.ts index 4a2ab4ef942..2f3be1c4e0c 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.ts +++ b/src/app/external-login-validation-page/external-login-validation-page.component.ts @@ -1,21 +1,58 @@ import { Component, OnInit } from '@angular/core'; +import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; +import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; +import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; +import { ActivatedRoute } from '@angular/router'; +import { hasValue } from '../shared/empty.util'; +import { RemoteData } from '../core/data/remote-data'; +import { Registration } from '../core/shared/registration.model'; +import { Observable, map, of, tap } from 'rxjs'; +import { getRemoteDataPayload } from '../core/shared/operators'; @Component({ templateUrl: './external-login-validation-page.component.html', - styleUrls: ['./external-login-validation-page.component.scss'] + styleUrls: ['./external-login-validation-page.component.scss'], }) export class ExternalLoginValidationPageComponent implements OnInit { + /** + * Whether or not the email address is already used by another user + */ + public emailExists: boolean; + /** + * The token used to get the registration data + */ + public token: string; - // temporary variable to test the component - newEmail = false; + /** + * The registration data of the user + */ + public registrationData$: Observable = of( + mockRegistrationDataModel + ); - existingEmail = true; + constructor( + private epersonRegistrationService: EpersonRegistrationService, + private arouter: ActivatedRoute + ) { + this.token = this.arouter.snapshot.queryParams.token; + this.emailExists = true; + } ngOnInit(): void { - // GET data from validation link // -> if email address is not used by other user => Email Validated component // -> if email address is used by other user => Review account information component - console.log('ExternalLoginValidationPageComponent ngOnInit'); + if (hasValue(this.token)) { + this.registrationData$ = this.epersonRegistrationService + .searchByToken(this.token) + .pipe( + tap((registration: RemoteData) => { + this.emailExists = hasValue(registration.payload.email); + }), + getRemoteDataPayload(), + map((registration: Registration) => + Object.assign(new RegistrationData(), registration) + ) + ); + } } - } diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.html b/src/app/external-login-validation-page/review-account-info/review-account-info.component.html index 23e6282f52c..98a01d734ee 100644 --- a/src/app/external-login-validation-page/review-account-info/review-account-info.component.html +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.html @@ -1,45 +1,59 @@ -

Review your account information

+

{{'external-login-validation.review-account-info.header' | translate}}

-

- The information received from ORCID differs from the one recorded in your - profile.
- Please review them and decide if you want to update any of them.After saving - you will be redirected to your profile page. -

+

- + + + + + + + - +
- Information + {{ 'external-login-validation.review-account-info.table.header.information' | translate }} - Received value + {{'external-login-validation.review-account-info.table.header.received-value' | translate }} - Current value + {{'external-login-validation.review-account-info.table.header.current-value' | translate }} Override{{'external-login-validation.review-account-info.table.header.action' | translate }}
{{ registrationData.registrationType | uppercase }}{{ registrationData.netId }} + + {{ notApplicableText }} + +
{{ data.label }}{{ data.label | titlecase }} {{ data.receivedValue }} - +
+
+ +
diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts b/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts index 92e8a07239f..3d244a6294d 100644 --- a/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts @@ -1,9 +1,18 @@ -import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; +import { + Component, + ChangeDetectionStrategy, + OnInit, + Input, +} from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonMock } from '../../shared/testing/eperson.mock'; import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; -import { mockRegistrationDataModel } from '../../shared/external-log-in-complete/models/registration-data.mock.model'; +import { filter, from, switchMap, take } from 'rxjs'; +import { RemoteData } from 'src/app/core/data/remote-data'; +import { ConfirmationModalComponent } from 'src/app/shared/confirmation-modal/confirmation-modal.component'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + export interface ReviewAccountInfoData { label: string; currentValue: string; @@ -19,49 +28,48 @@ export interface ReviewAccountInfoData { changeDetection: ChangeDetectionStrategy.OnPush, }) export class ReviewAccountInfoComponent implements OnInit { - registeredData: RegistrationData = mockRegistrationDataModel; + @Input() registrationToken: string; + + @Input() registrationData: RegistrationData; epersonData: EPerson = EPersonMock; - notApplicable = 'N/A'; + notApplicableText = 'N/A'; dataToCompare: ReviewAccountInfoData[] = []; - constructor(private ePersonService: EPersonDataService) { + constructor( + private ePersonService: EPersonDataService, + private modalService: NgbModal + ) { // GET data from url validation link and display + // Based on the URL data we get + // 1. token + // 1. login user + // TODO: https://4science.atlassian.net/browse/CST-11609?focusedCommentId=206748 + // the header in the review page must show that the user is logged in } ngOnInit(): void { - this.dataToCompare = [ - { - label: this.registeredData.registrationType, - receivedValue: this.registeredData.netId, - currentValue: this.notApplicable, - overrideValue: false, - identifier: 'netId', - }, - { - label: 'Last Name', - receivedValue: this.getReceivedValue('eperson.lastname'), - currentValue: this.getCurrentValue('eperson.lastname'), - overrideValue: false, - identifier: 'eperson.lastname', - }, - { - label: 'First Name', - currentValue: this.getCurrentValue('eperson.firstname'), - receivedValue: this.getReceivedValue('eperson.firstname'), - overrideValue: false, - identifier: 'eperson.firstname', - }, - { - label: 'Email', - currentValue: this.epersonData.email, - receivedValue: this.registeredData.email, - overrideValue: false, - identifier: 'email', - }, - ]; + this.dataToCompare = this.prepareDataToCompare(); + } + + private prepareDataToCompare() { + const dataToCompare: ReviewAccountInfoData[] = []; + Object.entries(this.registrationData.registrationMetadata).forEach( + ([key, value]) => { + console.log(key, value); + dataToCompare.push({ + label: key.split('.')?.[1], + currentValue: this.getCurrentValue(key), + receivedValue: value[0].value, + overrideValue: false, + identifier: key, + }); + } + ); + + return dataToCompare; } getEPersonData() { @@ -72,16 +80,55 @@ export class ReviewAccountInfoComponent implements OnInit { // ); } - getReceivedValue(metadata: string): string { - return this.registeredData.registrationMetadata[metadata]?.[0]?.value; + private getCurrentValue(metadata: string): string { + return this.epersonData.firstMetadataValue(metadata); } - getCurrentValue(metadata: string): string { - return this.epersonData.firstMetadataValue(metadata); + public onOverrideChange(value: boolean, identifier: string) { + this.dataToCompare.find( + (data) => data.identifier === identifier + ).overrideValue = value; } - test(value: boolean, identifier: string) { - this.dataToCompare.find((data) => data.identifier === identifier).overrideValue = value; - console.log(this.dataToCompare); + /** + * Merge the data from the registration token with the data from the eperson + */ + public mergeEPersonRegistrationData() { + const modalRef = this.modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.headerLabel = 'confirmation-modal.review-account-info.header'; + modalRef.componentInstance.infoLabel = 'confirmation-modal.review-account-info.info'; + modalRef.componentInstance.cancelLabel = 'confirmation-modal.review-account-info.cancel'; + modalRef.componentInstance.confirmLabel = 'confirmation-modal.review-account-info.confirm'; + modalRef.componentInstance.brandColor = 'primary'; + modalRef.componentInstance.confirmIcon = 'fa fa-check'; + modalRef.componentInstance.response + .pipe(take(1)) + .subscribe((confirm: boolean) => { + if (confirm) { + from(this.dataToCompare) + .pipe( + // what happens when is not overriden? + filter((data: ReviewAccountInfoData) => data.overrideValue), + switchMap((data: ReviewAccountInfoData) => { + return this.ePersonService.mergeEPersonDataWithToken( + this.epersonData.id, + this.registrationToken, + data.identifier + ); + }) + ) + .subscribe((response: RemoteData) => { + // TODO: https://4science.atlassian.net/browse/CST-11609?focusedCommentId=206748 + // redirect to profile page + if (response.hasSucceeded) { + console.log(response.payload); + } + + if (response.hasFailed) { + console.log(response.errorMessage); + } + }); + } + }); } } diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 0b177db851d..244521d2a65 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -1,7 +1,7 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; -import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; +import { ExternalLoginService } from '../../services/external-login.service'; +import { getRemoteDataPayload } from '../../../../core/shared/operators'; @Component({ selector: 'ds-confirm-email', @@ -19,7 +19,7 @@ export class ConfirmEmailComponent { constructor( private formBuilder: FormBuilder, - private epersonRegistrationService: EpersonRegistrationService, + private externalLoginService: ExternalLoginService, ) { this.emailForm = this.formBuilder.group({ email: ['', [Validators.required, Validators.email]] @@ -30,12 +30,11 @@ export class ConfirmEmailComponent { this.emailForm.markAllAsTouched(); if (this.emailForm.valid) { const email = this.emailForm.get('email').value; - console.log('Email submitted:', email); - this.epersonRegistrationService.patchUpdateRegistration(email, 'email', this.registrationId, this.token, true).pipe( - getFirstCompletedRemoteData() - ).subscribe((update) => { - console.log('Email update:', update); - }); + this.externalLoginService.patchUpdateRegistration([email], 'email', this.registrationId, this.token, 'replace') + .pipe(getRemoteDataPayload()) + .subscribe((update) => { + console.log('Email update:', update); + }); } } } diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts index 1d504c869f4..4b680656392 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts @@ -1,7 +1,7 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; -import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; +import { getRemoteDataPayload } from '../../../../core/shared/operators'; +import { ExternalLoginService } from '../../services/external-login.service'; @Component({ selector: 'ds-provide-email', @@ -18,7 +18,7 @@ export class ProvideEmailComponent { constructor( private formBuilder: FormBuilder, - private epersonRegistrationService: EpersonRegistrationService + private externalLoginService: ExternalLoginService, ) { this.emailForm = this.formBuilder.group({ email: ['', [Validators.required, Validators.email]], @@ -29,16 +29,8 @@ export class ProvideEmailComponent { this.emailForm.markAllAsTouched(); if (this.emailForm.valid) { const email = this.emailForm.get('email').value; - console.log('Email submitted:', email); - this.epersonRegistrationService - .patchUpdateRegistration( - email, - 'email', - this.registrationId, - this.token, - true - ) - .pipe(getFirstCompletedRemoteData()) + this.externalLoginService.patchUpdateRegistration([email], 'email', this.registrationId, this.token, 'add') + .pipe(getRemoteDataPayload()) .subscribe((update) => { console.log('Email update:', update); }); diff --git a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts index 30b7b8526bc..8d02f349b05 100644 --- a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts +++ b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts @@ -7,7 +7,7 @@ export const mockRegistrationDataModel: RegistrationData = Object.assign( new Re email: 'user@institution.edu', user: '028dcbb8-0da2-4122-a0ea-254be49ca107', registrationType: AuthMethodType.Orcid, - netId: '<:orcid>', + netId: '0000-1111-2222-3333', registrationMetadata: { 'eperson.firstname': [ Object.assign(new MetadataValue(), { diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts b/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts new file mode 100644 index 00000000000..7d1d8ac6a2c --- /dev/null +++ b/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ExternalLoginService } from './external-login.service'; + +describe('ExternalLoginService', () => { + let service: ExternalLoginService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ExternalLoginService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/shared/external-log-in-complete/services/external-login.service.ts new file mode 100644 index 00000000000..f0289eb5b78 --- /dev/null +++ b/src/app/shared/external-log-in-complete/services/external-login.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable, map } from 'rxjs'; +import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; +import { RemoteData } from '../../../core/data/remote-data'; +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { NotificationsService } from '../../notifications/notifications.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ExternalLoginService { + + constructor( + private epersonRegistrationService: EpersonRegistrationService, + private router: Router, + private notificationService: NotificationsService + ) { } + + + patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operation: 'add' | 'replace'): Observable> { + const updatedValues = values.map((value) => value); + return this.epersonRegistrationService.patchUpdateRegistration(updatedValues, field, registrationId, token, operation).pipe( + getFirstCompletedRemoteData(), + map((rd) => { + if (rd.hasSucceeded) { + this.router.navigate(['/email-confirmation']); + } + if (rd.hasFailed) { + console.log('Email update failed: email address was omitted or the operation is not valid', rd.errorMessage); + this.notificationService.error('Something went wrong.Email address was omitted or the operation is not valid'); + } + return rd; + }) + ); + } +} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index faddb3a2b0e..328296818c4 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1998,6 +1998,13 @@ "confirmation-modal.pending-changes.confirm": "Leave", + "confirmation-modal.review-account-info.header": "Save the changes", + + "confirmation-modal.review-account-info.info": "Continue to update your profile", + + "confirmation-modal.review-account-info.cancel": "Cancel", + + "confirmation-modal.review-account-info.confirm": "Save", "dataset.listelement.badge" : "Dataset", @@ -7194,4 +7201,16 @@ "external-login.provide-email.header": "Provide email", "external-login.provide-email.button.label": "Send Verification link", + + "external-login-validation.review-account-info.header": "Review your account information", + + "external-login-validation.review-account-info.info": "The information received from ORCID differs from the one recorded in your profile.
Please review them and decide if you want to update any of them.After saving you will be redirected to your profile page.", + + "external-login-validation.review-account-info.table.header.information": "Information", + + "external-login-validation.review-account-info.table.header.received-value": "Received value", + + "external-login-validation.review-account-info.table.header.current-value": "Current value", + + "external-login-validation.review-account-info.table.header.action": "Override", } diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 81336daf11f..c4b8fdaff6e 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -3066,6 +3066,21 @@ // "confirmation-modal.pending-changes.confirm": "Leave", "confirmation-modal.pending-changes.confirm": "Abbandona", + // "confirmation-modal.review-account-info.header": "Save the changes", + // TODO New key - Add a translation + "confirmation-modal.review-account-info.header": "Save the changes", + + // "confirmation-modal.review-account-info.info": "Continue to update your profile", + // TODO New key - Add a translation + "confirmation-modal.review-account-info.info": "Continue to update your profile", + + // "confirmation-modal.review-account-info.cancel": "Cancel", + // TODO New key - Add a translation + "confirmation-modal.review-account-info.cancel": "Cancel", + + // "confirmation-modal.review-account-info.confirm": "Save", + // TODO New key - Add a translation + "confirmation-modal.review-account-info.confirm": "Save", // "dataset.listelement.badge" : "Dataset", "dataset.listelement.badge" : "Dataset", @@ -11217,5 +11232,79 @@ // TODO New key - Add a translation "admin.system-wide-alert.title": "System-wide Alerts", + // "external-login.confirmation.header": "Information needed to complete the login process", + // TODO New key - Add a translation + "external-login.confirmation.header": "Information needed to complete the login process", + + // "external-login.noEmail.informationText": "The information received from {{authMethod}} are not sufficient to complete the login process. Please provide the missing information below, or login via a different method to associate your {{authMethod}} to an existing account.", + // TODO New key - Add a translation + "external-login.noEmail.informationText": "The information received from {{authMethod}} are not sufficient to complete the login process. Please provide the missing information below, or login via a different method to associate your {{authMethod}} to an existing account.", + + // "external-login.haveEmail.informationText": "It seems that you have not yet an account in this system. If this is the case, please confirm the data received from {{authMethod}} and a new account will be created for you. Otherwise, if you already have an account in the system, please update the email address to match the one already in use in the system or login via a different method to associate your {{authMethod}} to your existing account.", + // TODO New key - Add a translation + "external-login.haveEmail.informationText": "It seems that you have not yet an account in this system. If this is the case, please confirm the data received from {{authMethod}} and a new account will be created for you. Otherwise, if you already have an account in the system, please update the email address to match the one already in use in the system or login via a different method to associate your {{authMethod}} to your existing account.", + + // "external-login.confirm-email.header": "Confirm or update email", + // TODO New key - Add a translation + "external-login.confirm-email.header": "Confirm or update email", + + // "external-login.confirmation.email-required": "Email is required.", + // TODO New key - Add a translation + "external-login.confirmation.email-required": "Email is required.", + + // "external-login.confirmation.email-invalid": "Invalid email format.", + // TODO New key - Add a translation + "external-login.confirmation.email-invalid": "Invalid email format.", + + // "external-login.confirm.button.label": "Confirm this email", + // TODO New key - Add a translation + "external-login.confirm.button.label": "Confirm this email", + + // "external-login.confirm-email-sent.header": "Confirmation email sent", + // TODO New key - Add a translation + "external-login.confirm-email-sent.header": "Confirmation email sent", + // "external-login.confirm-email-sent.info": " We have sent an emait to the provided address to validate your input.
Please follow the instructions in the email to complete the login process.", + // TODO New key - Add a translation + "external-login.confirm-email-sent.info": " We have sent an emait to the provided address to validate your input.
Please follow the instructions in the email to complete the login process.", + + // "external-login.validated-email.header": "Email validated", + // TODO New key - Add a translation + "external-login.validated-email.header": "Email validated", + + // "external-login.validated-email.info": "Your email has been validated.
You can now login in the system with your prefered authentication method.", + // TODO New key - Add a translation + "external-login.validated-email.info": "Your email has been validated.
You can now login in the system with your prefered authentication method.", + + // "external-login.provide-email.header": "Provide email", + // TODO New key - Add a translation + "external-login.provide-email.header": "Provide email", + + // "external-login.provide-email.button.label": "Send Verification link", + // TODO New key - Add a translation + "external-login.provide-email.button.label": "Send Verification link", + + // "external-login-validation.review-account-info.header": "Review your account information", + // TODO New key - Add a translation + "external-login-validation.review-account-info.header": "Review your account information", + + // "external-login-validation.review-account-info.info": "The information received from ORCID differs from the one recorded in your profile.
Please review them and decide if you want to update any of them.After saving you will be redirected to your profile page.", + // TODO New key - Add a translation + "external-login-validation.review-account-info.info": "The information received from ORCID differs from the one recorded in your profile.
Please review them and decide if you want to update any of them.After saving you will be redirected to your profile page.", + + // "external-login-validation.review-account-info.table.header.information": "Information", + // TODO New key - Add a translation + "external-login-validation.review-account-info.table.header.information": "Information", + + // "external-login-validation.review-account-info.table.header.received-value": "Received value", + // TODO New key - Add a translation + "external-login-validation.review-account-info.table.header.received-value": "Received value", + + // "external-login-validation.review-account-info.table.header.current-value": "Current value", + // TODO New key - Add a translation + "external-login-validation.review-account-info.table.header.current-value": "Current value", + + // "external-login-validation.review-account-info.table.header.action": "Override", + // TODO New key - Add a translation + "external-login-validation.review-account-info.table.header.action": "Override", } From ade1243f35954aba12f7650e8f6ef7725b179e8f Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 28 Sep 2023 18:30:19 +0200 Subject: [PATCH 129/195] [CST-10703] review account unit tests & other --- src/app/core/eperson/eperson-data.service.ts | 9 +- .../external-login-page.component.html | 11 +- .../external-login-page.component.ts | 53 ++-- .../email-validated.component.spec.ts | 11 +- .../email-validated.component.ts | 2 + ...ernal-login-validation-page.component.html | 13 +- ...al-login-validation-page.component.spec.ts | 105 +++++++- ...xternal-login-validation-page.component.ts | 33 ++- .../helpers/compare-values.pipe.ts | 9 +- .../review-account-info.component.html | 8 +- .../review-account-info.component.spec.ts | 216 +++++++++++++++- .../review-account-info.component.ts | 241 +++++++++++++----- .../models/registration-data.mock.model.ts | 65 +++-- .../services/external-login.service.ts | 16 +- src/assets/i18n/en.json5 | 12 + 15 files changed, 655 insertions(+), 149 deletions(-) diff --git a/src/app/core/eperson/eperson-data.service.ts b/src/app/core/eperson/eperson-data.service.ts index affbe6d045d..7622866833c 100644 --- a/src/app/core/eperson/eperson-data.service.ts +++ b/src/app/core/eperson/eperson-data.service.ts @@ -361,10 +361,15 @@ export class EPersonDataService extends IdentifiableDataService impleme * @param token registration-token * @param metadataKey metadata key of the metadata field that should be overriden */ - mergeEPersonDataWithToken(uuid: string, token: string, metadataKey: string): Observable> { + mergeEPersonDataWithToken(uuid: string, token: string, metadataKey?: string): Observable> { const requestId = this.requestService.generateRequestId(); const hrefObs = this.getBrowseEndpoint().pipe( - map((href: string) => `${href}/${uuid}?token=${token}&override=${metadataKey}`)); + map((href: string) => + hasValue(metadataKey) + ? `${href}/${uuid}?token=${token}&override=${metadataKey}` + : `${href}/${uuid}?token=${token}` + ) + ); hrefObs.pipe( find((href: string) => hasValue(href)), diff --git a/src/app/external-login-page/external-login-page.component.html b/src/app/external-login-page/external-login-page.component.html index 7aa4ad6e837..82c9419930b 100644 --- a/src/app/external-login-page/external-login-page.component.html +++ b/src/app/external-login-page/external-login-page.component.html @@ -1,4 +1,11 @@
- + + + + +
- diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index 91bd59ed3c9..8a41bc4a8ad 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -2,39 +2,62 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { hasValue } from '../shared/empty.util'; import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; -import { RemoteData } from '../core/data/remote-data'; import { Registration } from '../core/shared/registration.model'; import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; +import { AlertType } from '../shared/alert/aletr-type'; +import { Observable, map, of } from 'rxjs'; +import { getRemoteDataPayload } from '../core/shared/operators'; @Component({ templateUrl: './external-login-page.component.html', - styleUrls: ['./external-login-page.component.scss'] + styleUrls: ['./external-login-page.component.scss'], }) export class ExternalLoginPageComponent implements OnInit { - + /** + * The token used to get the registration data, + * retrieved from the url. + * @memberof ExternalLoginPageComponent + */ public token: string; - - public registrationData: RegistrationData = mockRegistrationDataModel; + /** + * The registration data of the user. + */ + public registrationData$: Observable = of( + mockRegistrationDataModel + ); + /** + * The type of alert to show. + */ + public AlertTypeEnum = AlertType; constructor( private epersonRegistrationService: EpersonRegistrationService, - private arouter: ActivatedRoute, + private arouter: ActivatedRoute ) { this.token = this.arouter.snapshot.queryParams.token; } ngOnInit(): void { + this.getRegistrationData(); + // TODO: remove this line (temporary) + // this.token = '1234567890'; + } + + /** + * Get the registration data of the user, + * based on the token. + */ + getRegistrationData() { if (hasValue(this.token)) { - this.epersonRegistrationService.searchByToken(this.token).subscribe((registration: RemoteData - ) => { - console.log('ExternalLoginPageComponent ngOnInit registration', registration); - if (registration.hasSucceeded) { - this.registrationData = Object.assign(new RegistrationData(), registration.payload); - console.log('ExternalLoginPageComponent ngOnInit registrationData', this.registrationData); - } - }); + this.registrationData$ = this.epersonRegistrationService + .searchByToken(this.token) + .pipe( + getRemoteDataPayload(), + map((registration: Registration) => + Object.assign(new RegistrationData(), registration) + ) + ); } } - } diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts index d5d2c2794cc..fee341ad22c 100644 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts @@ -2,10 +2,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { EmailValidatedComponent } from './email-validated.component'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { of } from 'rxjs'; import { TranslateLoaderMock } from 'src/app/shared/mocks/translate-loader.mock'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { Router } from '@angular/router'; +import { RouterStub } from 'src/app/shared/testing/router.stub'; +import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { of } from 'rxjs'; describe('EmailValidatedComponent', () => { let component: EmailValidatedComponent; @@ -24,6 +27,8 @@ describe('EmailValidatedComponent', () => { await TestBed.configureTestingModule({ declarations: [ EmailValidatedComponent ], providers: [ + { provide: AuthService, useValue: {}}, + { provide: Router, useValue: new RouterStub() }, { provide: TranslateService, useValue: translateServiceStub }, ], imports: [ @@ -35,7 +40,7 @@ describe('EmailValidatedComponent', () => { } }), ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); }); diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.ts index 1ff85244386..c469b8ccbc1 100644 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.ts +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.ts @@ -9,6 +9,8 @@ import { AuthService } from '../../core/auth/auth.service'; }) export class EmailValidatedComponent { + // TODO: (temporary) + // evaluate if this is needed @Input() registrationToken: string; constructor(private authService: AuthService, private router: Router) { diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.html b/src/app/external-login-validation-page/external-login-validation-page.component.html index 227dc740988..3c9fa39d0b0 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.html +++ b/src/app/external-login-validation-page/external-login-validation-page.component.html @@ -1,11 +1,16 @@
- - - - + + + + +
diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts b/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts index 3ec5c77409f..155d64fe082 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts +++ b/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts @@ -1,16 +1,68 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; +import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { Observable, of } from 'rxjs'; +import { RemoteData } from '../core/data/remote-data'; +import { CommonModule } from '@angular/common'; +import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; +import { ActivatedRoute } from '@angular/router'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; +import { AlertType } from '../shared/alert/aletr-type'; describe('ExternalLoginValidationPageComponent', () => { let component: ExternalLoginValidationPageComponent; let fixture: ComponentFixture; + let epersonRegistrationService: any; + + const registrationDataMock = { + registrationType: 'orcid', + email: 'test@test.com', + netId: '0000-0000-0000-0000', + user: 'a44d8c9e-9b1f-4e7f-9b1a-5c9d8a0b1f1a', + registrationMetadata: { + 'email': [{ value: 'test@test.com' }], + 'eperson.lastname': [{ value: 'Doe' }], + 'eperson.firstname': [{ value: 'John' }], + }, + }; + + const tokenMock = 'as552-5a5a5-5a5a5-5a5a5'; + + const routeStub = { + snapshot: { + queryParams: { + token: tokenMock + } + } + }; beforeEach(async () => { + epersonRegistrationService = { + searchByToken: (token: string): Observable> => { return createSuccessfulRemoteDataObject$(registrationDataMock); } + }; + await TestBed.configureTestingModule({ - declarations: [ ExternalLoginValidationPageComponent ] + declarations: [ExternalLoginValidationPageComponent], + providers: [ + { provide: ActivatedRoute, useValue: routeStub }, + { provide: EpersonRegistrationService, useValue: epersonRegistrationService }, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock, + }, + }), + ], + schemas: [NO_ERRORS_SCHEMA], }) - .compileComponents(); + .compileComponents(); }); beforeEach(() => { @@ -22,4 +74,53 @@ describe('ExternalLoginValidationPageComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should set the token from the query parameters', () => { + expect(component.token).toEqual(tokenMock); + }); + + it('should initialize the registration data', () => { + spyOn(epersonRegistrationService, 'searchByToken').and.callThrough(); + component.ngOnInit(); + expect(epersonRegistrationService.searchByToken).toHaveBeenCalledWith(tokenMock); + expect(component.registrationData$).toBeTruthy(); + }); + + it('should render ds-email-validated component when registrationData$ does not have an email', () => { + component.registrationData$ = of(Object.assign(new RegistrationData(), { registrationDataMock, email: null })); + component.ngOnInit(); + fixture.detectChanges(); + + const emailValidatedComponent = fixture.nativeElement.querySelector('ds-email-validated'); + const reviewAccountInfoComponent = fixture.nativeElement.querySelector('ds-review-account-info'); + + expect(emailValidatedComponent).toBeTruthy(); + expect(reviewAccountInfoComponent).toBeNull(); + }); + + it('should render ds-review-account-info component when registrationData$ has an email', () => { + component.registrationData$ = of(Object.assign(new RegistrationData(), { registrationDataMock, email: 'hey@hello.com' })); + // component.ngOnInit(); + fixture.detectChanges(); + const emailValidatedComponent = fixture.nativeElement.querySelector('ds-email-validated'); + const reviewAccountInfoComponent = fixture.nativeElement.querySelector('ds-review-account-info'); + + expect(emailValidatedComponent).toBeNull(); + expect(reviewAccountInfoComponent).toBeTruthy(); + }); + + it('should render ds-alert component when token is missing', () => { + component.token = null; + component.ngOnInit(); + fixture.detectChanges(); + + const alertComponent = fixture.nativeElement.querySelector('ds-alert'); + + expect(alertComponent).toBeTruthy(); + expect(component.AlertTypeEnum).toEqual(AlertType); + }); + + afterEach(() => { + fixture.destroy(); + }); }); diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.ts b/src/app/external-login-validation-page/external-login-validation-page.component.ts index 2f3be1c4e0c..4146ace1be2 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.ts +++ b/src/app/external-login-validation-page/external-login-validation-page.component.ts @@ -1,53 +1,58 @@ import { Component, OnInit } from '@angular/core'; import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; -import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; import { ActivatedRoute } from '@angular/router'; import { hasValue } from '../shared/empty.util'; -import { RemoteData } from '../core/data/remote-data'; import { Registration } from '../core/shared/registration.model'; -import { Observable, map, of, tap } from 'rxjs'; +import { Observable, map, of } from 'rxjs'; import { getRemoteDataPayload } from '../core/shared/operators'; +import { AlertType } from '../shared/alert/aletr-type'; @Component({ templateUrl: './external-login-validation-page.component.html', styleUrls: ['./external-login-validation-page.component.scss'], }) export class ExternalLoginValidationPageComponent implements OnInit { - /** - * Whether or not the email address is already used by another user - */ - public emailExists: boolean; /** * The token used to get the registration data */ public token: string; + /** + * The type of alert to show + */ + public AlertTypeEnum = AlertType; + /** * The registration data of the user */ - public registrationData$: Observable = of( - mockRegistrationDataModel - ); + public registrationData$: Observable + // = of( + // mockRegistrationDataModel + // ); constructor( private epersonRegistrationService: EpersonRegistrationService, private arouter: ActivatedRoute ) { this.token = this.arouter.snapshot.queryParams.token; - this.emailExists = true; } ngOnInit(): void { // -> if email address is not used by other user => Email Validated component // -> if email address is used by other user => Review account information component + this.getRegistrationData(); + // TODO: remove this line (temporary) + // this.token = '1234567890'; + } + /** + * Get the registration data from the token + */ + getRegistrationData() { if (hasValue(this.token)) { this.registrationData$ = this.epersonRegistrationService .searchByToken(this.token) .pipe( - tap((registration: RemoteData) => { - this.emailExists = hasValue(registration.payload.email); - }), getRemoteDataPayload(), map((registration: Registration) => Object.assign(new RegistrationData(), registration) diff --git a/src/app/external-login-validation-page/helpers/compare-values.pipe.ts b/src/app/external-login-validation-page/helpers/compare-values.pipe.ts index aefbe9a0ae0..ff5fb906b5b 100644 --- a/src/app/external-login-validation-page/helpers/compare-values.pipe.ts +++ b/src/app/external-login-validation-page/helpers/compare-values.pipe.ts @@ -5,7 +5,14 @@ import { Pipe, PipeTransform } from '@angular/core'; }) export class CompareValuesPipe implements PipeTransform { - transform(receivedValue: string, currentValue: string): unknown { + /** + * Returns a string with a checkmark if the received value is equal to the current value, + * or the current value if they are not equal. + * @param receivedValue the value received from the registration data + * @param currentValue the value from the current user + * @returns the value to be displayed in the template + */ + transform(receivedValue: string, currentValue: string): string { if (receivedValue === currentValue) { return ''; } else { diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.html b/src/app/external-login-validation-page/review-account-info/review-account-info.component.html index 98a01d734ee..82aec84441e 100644 --- a/src/app/external-login-validation-page/review-account-info/review-account-info.component.html +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.html @@ -41,9 +41,9 @@

{{'external-login-validation.review-account-info.header' | translate}}

@@ -52,7 +52,7 @@

{{'external-login-validation.review-account-info.header' | translate}}

-
diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts index 7cd24cbeec2..7e1d3f08de3 100644 --- a/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts @@ -1,25 +1,233 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ReviewAccountInfoComponent } from './review-account-info.component'; +import { + TranslateLoader, + TranslateModule, + TranslateService, +} from '@ngx-translate/core'; +import { CommonModule } from '@angular/common'; +import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; +import { EPersonDataService } from '../../core/eperson/eperson-data.service'; +import { Observable, Subscription, of } from 'rxjs'; +import { RemoteData } from '../../core/data/remote-data'; +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { EPersonMock } from '../../shared/testing/eperson.mock'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; +import { Router } from '@angular/router'; +import { RouterMock } from '../../shared/mocks/router.mock'; +import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; +import { EventEmitter } from '@angular/core'; +import { CompareValuesPipe } from '../helpers/compare-values.pipe'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; let fixture: ComponentFixture; + let ePersonDataServiceStub: any; + let router: any; + let notificationsService: any; + + const translateServiceStub = { + get: () => of('test-message'), + onLangChange: new EventEmitter(), + onTranslationChange: new EventEmitter(), + onDefaultLangChange: new EventEmitter() + }; + const mockEPerson = EPersonMock; + const modalStub = { + open: () => ({ componentInstance: { response: of(true) }}), + close: () => null, + dismiss: () => null, + }; + const registrationDataMock = { + registrationType: 'orcid', + email: 'test@test.com', + netId: '0000-0000-0000-0000', + user: 'a44d8c9e-9b1f-4e7f-9b1a-5c9d8a0b1f1a', + registrationMetadata: { + 'email': [{ value: 'test@test.com' }], + 'eperson.lastname': [{ value: 'Doe' }], + 'eperson.firstname': [{ value: 'John' }], + }, + }; beforeEach(async () => { + ePersonDataServiceStub = { + findById(uuid: string): Observable> { + return createSuccessfulRemoteDataObject$(mockEPerson); + }, + mergeEPersonDataWithToken( + token: string, + metadata?: string + ): Observable> { + return createSuccessfulRemoteDataObject$(mockEPerson); + }, + }; + router = new RouterMock(); + notificationsService = new NotificationsServiceStub(); await TestBed.configureTestingModule({ - declarations: [ ReviewAccountInfoComponent ] - }) - .compileComponents(); + declarations: [ReviewAccountInfoComponent, CompareValuesPipe], + providers: [ + { provide: EPersonDataService, useValue: ePersonDataServiceStub }, + { provide: NgbModal, useValue: modalStub }, + { + provide: NotificationsService, + useValue: notificationsService, + }, + { provide: TranslateService, useValue: translateServiceStub }, + { provide: Router, useValue: router }, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock, + }, + }), + ], + }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(ReviewAccountInfoComponent); component = fixture.componentInstance; + component.registrationData = Object.assign( + new RegistrationData(), + registrationDataMock + ); + component.registrationToken = 'test-token'; + spyOn(router, 'navigate'); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call getEPersonData when ngOnInit is called', () => { + spyOn(component, 'getEPersonData'); + component.ngOnInit(); + expect(component.getEPersonData).toHaveBeenCalled(); + }); + + it('should prepare data to compare', () => { + component.ngOnInit(); + const dataToCompare = component.dataToCompare; + expect(dataToCompare.length).toBe(3); + expect(dataToCompare[0].label).toBe('email'); + expect(dataToCompare[1].label).toBe('lastname'); + expect(dataToCompare[2].label).toBe('firstname'); + expect(dataToCompare[0].overrideValue).toBe(false); + expect(dataToCompare[0].receivedValue).toBe('test@test.com'); + }); + + it('should get EPerson data', fakeAsync(() => { + spyOn(ePersonDataServiceStub, 'findById').and.returnValue( + of({ payload: mockEPerson } as RemoteData) + ); + component.getEPersonData(); + tick(); + expect(ePersonDataServiceStub.findById).toHaveBeenCalledWith(registrationDataMock.user); + expect(component.epersonCurrentData).toEqual(EPersonMock); + })); + + it('should update dataToCompare when overrideValue is changed', () => { + component.onOverrideChange(true, 'email'); + expect(component.dataToCompare[0].overrideValue).toBe(true); + }); + + it('should open a confirmation modal on onSave and confirm', fakeAsync(() => { + spyOn(modalStub, 'open').and.returnValue({ + componentInstance: { response: of(true) }, + }); + spyOn(component, 'mergeEPersonDataWithToken'); + component.onSave(); + tick(); + expect(modalStub.open).toHaveBeenCalled(); + expect(component.mergeEPersonDataWithToken).toHaveBeenCalled(); + })); + + it('should open a confirmation modal on onSave and cancel', fakeAsync(() => { + spyOn(modalStub, 'open').and.returnValue({ + componentInstance: { response: of(false) }, + }); + spyOn(component, 'mergeEPersonDataWithToken'); + component.onSave(); + tick(); + expect(modalStub.open).toHaveBeenCalled(); + expect(component.mergeEPersonDataWithToken).not.toHaveBeenCalled(); + })); + + it('should merge EPerson data with token when overrideValue is true', fakeAsync(() => { + component.dataToCompare[0].overrideValue = true; + spyOn(ePersonDataServiceStub, 'mergeEPersonDataWithToken').and.returnValue( + of({ hasSucceeded: true }) + ); + component.mergeEPersonDataWithToken(); + tick(); + expect(ePersonDataServiceStub.mergeEPersonDataWithToken).toHaveBeenCalledTimes(1); + expect(router.navigate).toHaveBeenCalledWith(['/profile']); + })); + + it('should merge EPerson data with token when overrideValue is false', fakeAsync(() => { + spyOn(ePersonDataServiceStub, 'mergeEPersonDataWithToken').and.returnValue( + of({ hasSucceeded: true }) + ); + component.mergeEPersonDataWithToken(); + tick(); + expect(ePersonDataServiceStub.mergeEPersonDataWithToken).toHaveBeenCalledTimes(1); + expect(router.navigate).toHaveBeenCalledWith(['/profile']); + })); + + + it('should unsubscribe from subscriptions when ngOnDestroy is called', () => { + const subscription1 = jasmine.createSpyObj('Subscription', [ + 'unsubscribe', + ]); + const subscription2 = jasmine.createSpyObj('Subscription', [ + 'unsubscribe', + ]); + component.subs = [subscription1, subscription2]; + component.ngOnDestroy(); + expect(subscription1.unsubscribe).toHaveBeenCalled(); + expect(subscription2.unsubscribe).toHaveBeenCalled(); + }); + + it('should display registration data', () => { + const registrationTypeElement: HTMLElement = fixture.nativeElement.querySelector('tbody tr:first-child th'); + const netIdElement: HTMLElement = fixture.nativeElement.querySelector('tbody tr:first-child td'); + + expect(registrationTypeElement.textContent.trim()).toBe(registrationDataMock.registrationType.toUpperCase()); + expect(netIdElement.textContent.trim()).toBe(registrationDataMock.netId); + }); + + it('should display dataToCompare rows with translated labels and values', () => { + const dataRows: NodeListOf = fixture.nativeElement.querySelectorAll('tbody tr:not(:first-child)'); + // Assuming there are 3 dataToCompare rows based on the registrationDataMock + expect(dataRows.length).toBe(3); + // Assuming the first row is the email row abd the second row is the lastname row + const firstDataRow = dataRows[1]; + const firstDataLabel: HTMLElement = firstDataRow.querySelector('th'); + const firstDataReceivedValue: HTMLElement = firstDataRow.querySelectorAll('td')[0]; + const firstDataOverrideSwitch: HTMLElement = firstDataRow.querySelector('ui-switch'); + + expect(firstDataLabel.textContent.trim()).toBe('Lastname'); + expect(firstDataReceivedValue.textContent.trim()).toBe('Doe'); + expect(firstDataOverrideSwitch).not.toBeNull(); + }); + + it('should trigger onSave() when the button is clicked', () => { + spyOn(component, 'onSave'); + const saveButton: HTMLButtonElement = fixture.nativeElement.querySelector('button.btn-primary'); + saveButton.click(); + expect(component.onSave).toHaveBeenCalled(); + }); + + afterEach(() => { + fixture.destroy(); + }); }); diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts b/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts index 3d244a6294d..80e9e2a1237 100644 --- a/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts @@ -3,15 +3,24 @@ import { ChangeDetectionStrategy, OnInit, Input, + OnDestroy, } from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonMock } from '../../shared/testing/eperson.mock'; import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; -import { filter, from, switchMap, take } from 'rxjs'; +import { Observable, Subscription, filter, from, switchMap, take } from 'rxjs'; import { RemoteData } from 'src/app/core/data/remote-data'; import { ConfirmationModalComponent } from 'src/app/shared/confirmation-modal/confirmation-modal.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { hasValue } from 'src/app/shared/empty.util'; +import { + getFirstCompletedRemoteData, + getRemoteDataPayload, +} from 'src/app/core/shared/operators'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; +import { Router } from '@angular/router'; export interface ReviewAccountInfoData { label: string; @@ -27,40 +36,180 @@ export interface ReviewAccountInfoData { styleUrls: ['./review-account-info.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ReviewAccountInfoComponent implements OnInit { +export class ReviewAccountInfoComponent implements OnInit, OnDestroy { + /** + * The registration token sent from validation link + */ @Input() registrationToken: string; - + /** + * User data from the registration token + */ @Input() registrationData: RegistrationData; - epersonData: EPerson = EPersonMock; - + /** + * Text to display when the value is not applicable + */ notApplicableText = 'N/A'; - + /** + * List of data to compare + */ dataToCompare: ReviewAccountInfoData[] = []; + /** + * List of subscriptions + */ + subs: Subscription[] = []; + /** + * Current eperson data from the database, + * so we can compare it with the data from the registration token + */ + epersonCurrentData: EPerson;// = EPersonMock; constructor( private ePersonService: EPersonDataService, - private modalService: NgbModal + private modalService: NgbModal, + private notificationService: NotificationsService, + private translateService: TranslateService, + private router: Router ) { - // GET data from url validation link and display + // GET data from url validation link // Based on the URL data we get // 1. token - // 1. login user + // User should be automatically logged in // TODO: https://4science.atlassian.net/browse/CST-11609?focusedCommentId=206748 - // the header in the review page must show that the user is logged in + // How to handle the case when the email is not part of metadata + // and we have `eperson.orcid` in metadata list } ngOnInit(): void { - this.dataToCompare = this.prepareDataToCompare(); + this.getEPersonData(); + // TODO: remove after having data + // this.dataToCompare = this.prepareDataToCompare(); + } + + /** + * Get the current eperson data from the database. + * If the eperson is found, prepare the data to compare. + */ + getEPersonData() { + if ( + hasValue(this.registrationData) && + hasValue(this.registrationData.user) + ) { + this.ePersonService + .findById(this.registrationData.user) + .pipe(getFirstCompletedRemoteData(), getRemoteDataPayload()) + .subscribe((eperson: EPerson) => { + if (eperson) { + this.epersonCurrentData = eperson; + this.dataToCompare = this.prepareDataToCompare(); + } + }); + } + } + + /** + * Find the data to compare based on the metadata key and update the override value + * @param value value of the override checkbox + * @param identifier the metadata key + */ + public onOverrideChange(value: boolean, identifier: string) { + this.dataToCompare.find( + (data) => data.identifier === identifier + ).overrideValue = value; + } + + /** + * Open a confirmation modal to confirm the override of the data + * If confirmed, merge the data from the registration token with the data from the eperson + */ + public onSave() { + const modalRef = this.modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.headerLabel = + 'confirmation-modal.review-account-info.header'; + modalRef.componentInstance.infoLabel = + 'confirmation-modal.review-account-info.info'; + modalRef.componentInstance.cancelLabel = + 'confirmation-modal.review-account-info.cancel'; + modalRef.componentInstance.confirmLabel = + 'confirmation-modal.review-account-info.confirm'; + modalRef.componentInstance.brandColor = 'primary'; + modalRef.componentInstance.confirmIcon = 'fa fa-check'; + + this.subs.push( + modalRef.componentInstance.response + .pipe(take(1)) + .subscribe((confirm: boolean) => { + if (confirm) { + this.mergeEPersonDataWithToken(); + } + }) + ); + } + + /** + * Merge the data from the registration token with the data from the eperson. + * If any of the metadata is overridden, sent a merge request for each metadata to override. + * If none of the metadata is overridden, sent a merge request with the registration token only. + */ + mergeEPersonDataWithToken() { + let override$: Observable>; + if (this.dataToCompare.some((d) => d.overrideValue)) { + override$ = from(this.dataToCompare).pipe( + filter((data: ReviewAccountInfoData) => data.overrideValue), + switchMap((data: ReviewAccountInfoData) => { + return this.ePersonService.mergeEPersonDataWithToken( + this.epersonCurrentData.id, + this.registrationToken, + data.identifier + ); + }) + ); + } else { + override$ = this.ePersonService.mergeEPersonDataWithToken( + this.epersonCurrentData.id, + this.registrationToken + ); + } + this.subs.push( + override$.subscribe((response: RemoteData) => { + // TODO: https://4science.atlassian.net/browse/CST-11609?focusedCommentId=206748 + // redirect to profile page + if (response.hasSucceeded) { + console.log('mergeEPersonDataWithToken', response.payload); + this.notificationService.success( + this.translateService.get( + 'review-account-info.merge-data.notification.success' + ) + ); + this.router.navigate(['/profile']); + } + + if (response.hasFailed) { + this.notificationService.success( + this.translateService.get( + 'review-account-info.merge-data.notification.error' + ) + ); + } + }) + ); } - private prepareDataToCompare() { + /** + * Prepare the data to compare and display: + * -> For each metadata from the registration token, get the current value from the eperson. + * -> Label is the metadata key without the prefix e.g `eperson.` + * -> Identifier is the metadata key with the prefix e.g `eperson.lastname` + * -> Override value is false by default + * @returns List of data to compare + */ + private prepareDataToCompare(): ReviewAccountInfoData[] { const dataToCompare: ReviewAccountInfoData[] = []; Object.entries(this.registrationData.registrationMetadata).forEach( ([key, value]) => { console.log(key, value); dataToCompare.push({ - label: key.split('.')?.[1], + label: key.split('.')?.[1] ?? key.split('.')?.[0], currentValue: this.getCurrentValue(key), receivedValue: value[0].value, overrideValue: false, @@ -72,63 +221,19 @@ export class ReviewAccountInfoComponent implements OnInit { return dataToCompare; } - getEPersonData() { - // this.epersonData$ = this.ePersonService.findById() - // .pipe( - // getFirstCompletedRemoteData(), - // getRemoteDataPayload() - // ); - } - - private getCurrentValue(metadata: string): string { - return this.epersonData.firstMetadataValue(metadata); - } - - public onOverrideChange(value: boolean, identifier: string) { - this.dataToCompare.find( - (data) => data.identifier === identifier - ).overrideValue = value; - } - /** - * Merge the data from the registration token with the data from the eperson + * Return the current value of the metadata key from the eperson + * @param metadata metadata key + * @returns the current value of the metadata key from the eperson */ - public mergeEPersonRegistrationData() { - const modalRef = this.modalService.open(ConfirmationModalComponent); - modalRef.componentInstance.headerLabel = 'confirmation-modal.review-account-info.header'; - modalRef.componentInstance.infoLabel = 'confirmation-modal.review-account-info.info'; - modalRef.componentInstance.cancelLabel = 'confirmation-modal.review-account-info.cancel'; - modalRef.componentInstance.confirmLabel = 'confirmation-modal.review-account-info.confirm'; - modalRef.componentInstance.brandColor = 'primary'; - modalRef.componentInstance.confirmIcon = 'fa fa-check'; - modalRef.componentInstance.response - .pipe(take(1)) - .subscribe((confirm: boolean) => { - if (confirm) { - from(this.dataToCompare) - .pipe( - // what happens when is not overriden? - filter((data: ReviewAccountInfoData) => data.overrideValue), - switchMap((data: ReviewAccountInfoData) => { - return this.ePersonService.mergeEPersonDataWithToken( - this.epersonData.id, - this.registrationToken, - data.identifier - ); - }) - ) - .subscribe((response: RemoteData) => { - // TODO: https://4science.atlassian.net/browse/CST-11609?focusedCommentId=206748 - // redirect to profile page - if (response.hasSucceeded) { - console.log(response.payload); - } + private getCurrentValue(metadata: string): string { + if (metadata === 'email') { + return this.epersonCurrentData.email; + } + return this.epersonCurrentData.firstMetadataValue(metadata) ?? ''; + } - if (response.hasFailed) { - console.log(response.errorMessage); - } - }); - } - }); + ngOnDestroy(): void { + this.subs.filter((s) => hasValue(s)).forEach((sub) => sub.unsubscribe()); } } diff --git a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts index 8d02f349b05..961d443a665 100644 --- a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts +++ b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts @@ -2,30 +2,43 @@ import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; import { RegistrationData } from './registration-data.model'; import { MetadataValue } from '../../../core/shared/metadata.models'; -export const mockRegistrationDataModel: RegistrationData = Object.assign( new RegistrationData(), { - id: '3', - email: 'user@institution.edu', - user: '028dcbb8-0da2-4122-a0ea-254be49ca107', - registrationType: AuthMethodType.Orcid, - netId: '0000-1111-2222-3333', - registrationMetadata: { - 'eperson.firstname': [ - Object.assign(new MetadataValue(), { - value: 'User', - language: null, - authority: '', - confidence: -1, - place: -1, - }) - ], - 'eperson.lastname': [ - Object.assign(new MetadataValue(), { - value: 'Power', - language: null, - authority: '', - confidence: -1, - place: -1 - }) - ] +export const mockRegistrationDataModel: RegistrationData = Object.assign( + new RegistrationData(), + { + id: '3', + email: 'user@institution.edu', + user: '028dcbb8-0da2-4122-a0ea-254be49ca107', + registrationType: AuthMethodType.Orcid, + netId: '0000-1111-2222-3333', + registrationMetadata: { + 'eperson.firstname': [ + Object.assign(new MetadataValue(), { + value: 'User 1', + language: null, + authority: '', + confidence: -1, + place: -1, + }), + ], + 'eperson.lastname': [ + Object.assign(new MetadataValue(), { + value: 'Power', + language: null, + authority: '', + confidence: -1, + place: -1, + }), + ], + 'email': [ + { + value: 'power-user@orcid.org', + language: null, + authority: '', + confidence: -1, + place: -1, + overrides: 'power-user@dspace.org', + }, + ], + }, } -}); +); diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/shared/external-log-in-complete/services/external-login.service.ts index f0289eb5b78..f757bb2bdbf 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.ts @@ -5,6 +5,7 @@ import { EpersonRegistrationService } from '../../../core/data/eperson-registrat import { RemoteData } from '../../../core/data/remote-data'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { NotificationsService } from '../../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root' @@ -14,10 +15,18 @@ export class ExternalLoginService { constructor( private epersonRegistrationService: EpersonRegistrationService, private router: Router, - private notificationService: NotificationsService + private notificationService: NotificationsService, + private translate: TranslateService ) { } - + /** + * Update the registration data + * @param values the values to update or add + * @param field the filed to be updated + * @param registrationId the registration id + * @param token the registration token + * @param operation operation to be performed + */ patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operation: 'add' | 'replace'): Observable> { const updatedValues = values.map((value) => value); return this.epersonRegistrationService.patchUpdateRegistration(updatedValues, field, registrationId, token, operation).pipe( @@ -27,8 +36,7 @@ export class ExternalLoginService { this.router.navigate(['/email-confirmation']); } if (rd.hasFailed) { - console.log('Email update failed: email address was omitted or the operation is not valid', rd.errorMessage); - this.notificationService.error('Something went wrong.Email address was omitted or the operation is not valid'); + this.notificationService.error(this.translate.get('external-login-page.provide-email.notifications.error')); } return rd; }) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 328296818c4..9d3161f0aaf 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7213,4 +7213,16 @@ "external-login-validation.review-account-info.table.header.current-value": "Current value", "external-login-validation.review-account-info.table.header.action": "Override", + + "on-label": "ON", + + "off-label": "OFF", + + "review-account-info.merge-data.notification.success": "Your account information has been updated successfully", + + "review-account-info.merge-data.notification.error": "Something went wrong while updating your account information", + + "external-login.validate-email.no-token": "The validation link is no longer valid. Please try again.", + + "external-login-page.provide-email.notifications.error": "Something went wrong.Email address was omitted or the operation is not valid.", } From dc9a683d51f3e868eee4a2e30b16cda047552068 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 29 Sep 2023 17:32:50 +0200 Subject: [PATCH 130/195] Duplicate labels --- src/assets/i18n/en.json5 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 5f5b1797b9a..406b53e06c6 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3784,8 +3784,6 @@ "menu.section.import_from_excel": "Import from excel", - "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_to_excel": "Export to excel", "menu.section.icon.access_control": "Access Control menu section", @@ -4168,8 +4166,6 @@ "nav.user.description" : "User profile bar", - "nav.subscriptions" : "Subscriptions", - "none.listelement.badge": "Item", "openaire.broker.title": "OpenAIRE Broker", From b1256187e9219257a4025d67104df5e8f766bcf4 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 29 Sep 2023 17:38:27 +0200 Subject: [PATCH 131/195] i18n label cleanup & sync --- src/assets/i18n/ar.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/bn.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/ca.json5 | 122 +++++++++++++++++++++++++++++++---- src/assets/i18n/cs.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/de.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/el.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/es.json5 | 122 +++++++++++++++++++++++++++++++---- src/assets/i18n/fi.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/fr.json5 | 122 +++++++++++++++++++++++++++++++---- src/assets/i18n/gd.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/hi.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/hu.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/it.json5 | 119 ++++++++++++++++++++++++++++++---- src/assets/i18n/ja.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/kk.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/lv.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/nl.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/pl.json5 | 122 +++++++++++++++++++++++++++++++---- src/assets/i18n/pt-BR.json5 | 122 +++++++++++++++++++++++++++++++---- src/assets/i18n/pt-PT.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/sv.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/sw.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/tr.json5 | 123 ++++++++++++++++++++++++++++++++---- src/assets/i18n/uk.json5 | 123 ++++++++++++++++++++++++++++++++---- 24 files changed, 2661 insertions(+), 282 deletions(-) diff --git a/src/assets/i18n/ar.json5 b/src/assets/i18n/ar.json5 index e1336f45544..b5a8f0d1aef 100644 --- a/src/assets/i18n/ar.json5 +++ b/src/assets/i18n/ar.json5 @@ -504,6 +504,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", // TODO New key - Add a translation "admin.registries.metadata.schemas.table.namespace": "Namespace", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO New key - Add a translation @@ -3537,6 +3540,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", // TODO New key - Add a translation "curation-task.task.checklinks.label": "Check Links in Metadata", @@ -4374,7 +4393,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -7385,10 +7406,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -8137,10 +8154,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -10188,6 +10201,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -10232,6 +10249,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -10972,6 +10993,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -13205,12 +13274,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -13992,9 +14091,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/bn.json5 b/src/assets/i18n/bn.json5 index 557106576fd..6ce88367ad7 100644 --- a/src/assets/i18n/bn.json5 +++ b/src/assets/i18n/bn.json5 @@ -433,6 +433,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "নামস্থান", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "মেটাডেটা রেজিস্ট্রি", @@ -2971,6 +2974,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "মেটাডেটা লিঙ্ক চেক করুন", @@ -3736,7 +3755,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6281,10 +6302,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6938,10 +6955,6 @@ // "nav.user.description" : "User profile bar", "nav.user.description" : "ব্যবহারকারীর প্রোফাইল বার", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "আইটেম", @@ -8712,6 +8725,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8750,6 +8767,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9417,6 +9438,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11329,12 +11398,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12113,9 +12212,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/ca.json5 b/src/assets/i18n/ca.json5 index 5cf745a3da5..6adc8d8e91a 100644 --- a/src/assets/i18n/ca.json5 +++ b/src/assets/i18n/ca.json5 @@ -421,6 +421,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Espai de noms", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Registre de metadades", @@ -2915,6 +2918,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Comprovar enllaços a metadades", @@ -3662,7 +3681,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description": "Fil de sindicació", @@ -6142,9 +6163,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_batch": "Exportació per lots (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6790,10 +6808,6 @@ // "nav.user.description" : "User profile bar", "nav.user.description": "Barra de perfil d'usuari", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "Ítem", @@ -8520,6 +8534,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8558,6 +8576,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9221,6 +9243,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11058,12 +11128,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "Vista del flux de treball", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -11717,9 +11817,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5 index 0379221d4a7..d89cc09a003 100644 --- a/src/assets/i18n/cs.json5 +++ b/src/assets/i18n/cs.json5 @@ -493,6 +493,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Jmenný prostor", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3512,6 +3515,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", // TODO New key - Add a translation "curation-task.task.checklinks.label": "Check Links in Metadata", @@ -4338,7 +4357,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -7308,10 +7329,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -8058,10 +8075,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -10095,6 +10108,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -10139,6 +10156,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -10857,6 +10878,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -13090,12 +13159,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -13877,9 +13976,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 3f50e337315..755b361d325 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -436,6 +436,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namensraum", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Metadatenreferenzliste", @@ -3021,6 +3024,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Links in Metadaten prüfen", @@ -3788,7 +3807,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6423,10 +6444,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7087,10 +7104,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -8876,6 +8889,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8916,6 +8933,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9592,6 +9613,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11563,12 +11632,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12350,9 +12449,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/el.json5 b/src/assets/i18n/el.json5 index d5432a5cf3b..51b779eebff 100644 --- a/src/assets/i18n/el.json5 +++ b/src/assets/i18n/el.json5 @@ -424,6 +424,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "namespace", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Μητρώο μεταδεδομένων", @@ -2967,6 +2970,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Ελέγξτε τους συνδέσμους στα μεταδεδομένα", @@ -3729,7 +3748,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description": "Συνδικάτο τροφοδοσία", @@ -6276,10 +6297,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6928,10 +6945,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "Τεκμήριο", @@ -8708,6 +8721,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8746,6 +8763,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9411,6 +9432,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11255,12 +11324,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "Προβολή χώρου εργασίας", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -11944,9 +12043,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/es.json5 b/src/assets/i18n/es.json5 index 212a270055d..624e6744d61 100644 --- a/src/assets/i18n/es.json5 +++ b/src/assets/i18n/es.json5 @@ -421,6 +421,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Espacio de nombres", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Registro de metadatos", @@ -2915,6 +2918,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Comprobar enlaces en metadatos", @@ -3661,7 +3680,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description": "Hilo de sindicación", @@ -6128,9 +6149,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_batch": "Exportación por lotes (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6776,10 +6794,6 @@ // "nav.user.description" : "User profile bar", "nav.user.description": "Barra de perfil de usuario", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "Ítem", @@ -8506,6 +8520,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8544,6 +8562,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9207,6 +9229,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11044,12 +11114,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "Vista del flujo de trabajo", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -11703,9 +11803,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/fi.json5 b/src/assets/i18n/fi.json5 index b427560a400..b4789a9da1e 100644 --- a/src/assets/i18n/fi.json5 +++ b/src/assets/i18n/fi.json5 @@ -437,6 +437,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Nimiavaruus", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3072,6 +3075,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Tarkista metadatan linkit", @@ -3854,7 +3873,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6508,10 +6529,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7172,10 +7189,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -8978,6 +8991,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -9018,6 +9035,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9705,6 +9726,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11694,12 +11763,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12481,9 +12580,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5 index f83bd44a71e..7a453b92c06 100644 --- a/src/assets/i18n/fr.json5 +++ b/src/assets/i18n/fr.json5 @@ -427,6 +427,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Espace de nommage", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Registre de métadonnées", @@ -2962,6 +2965,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Vérification des liens dans les métadonnées", @@ -3727,7 +3746,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6283,9 +6304,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_batch": "Exporter en lot (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6940,10 +6958,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "Item", @@ -8724,6 +8738,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8762,6 +8780,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9427,6 +9449,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11297,12 +11367,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "Vue du tableau de suivi", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12081,9 +12181,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/gd.json5 b/src/assets/i18n/gd.json5 index 6e6fbf623a8..49bd9047e0c 100644 --- a/src/assets/i18n/gd.json5 +++ b/src/assets/i18n/gd.json5 @@ -435,6 +435,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Ainm-spàs", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Clàr-lann Metadata", @@ -3003,6 +3006,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Dearbh Ceangalan ann am Metadata", @@ -3775,7 +3794,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6347,10 +6368,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7009,10 +7026,6 @@ // "nav.user.description" : "User profile bar", "nav.user.description" : "Bàr-iomraidh neach-cleachdaidh", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "Nì", @@ -8788,6 +8801,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8826,6 +8843,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9493,6 +9514,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11433,12 +11502,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12217,9 +12316,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/hi.json5 b/src/assets/i18n/hi.json5 index e0c6403a085..a82be3de927 100644 --- a/src/assets/i18n/hi.json5 +++ b/src/assets/i18n/hi.json5 @@ -423,6 +423,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "नाम स्थान", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "मेटाडेटा रजिस्ट्री", @@ -2963,6 +2966,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "मेटाडेटा में लिंक जांचें", @@ -3726,7 +3745,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description": "सिंडिकेशन फ़ीड", @@ -6272,10 +6293,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6924,10 +6941,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "आइटम", @@ -8705,6 +8718,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8743,6 +8760,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9408,6 +9429,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11251,12 +11320,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "कार्यक्षेत्र दृश्य", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -11940,9 +12039,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/hu.json5 b/src/assets/i18n/hu.json5 index 63d590bb1f1..5f09ec7d28d 100644 --- a/src/assets/i18n/hu.json5 +++ b/src/assets/i18n/hu.json5 @@ -438,6 +438,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Névhely", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3080,6 +3083,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Hivatkozások ellenőrzése a metaadatokban", @@ -3862,7 +3881,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6519,10 +6540,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7183,10 +7200,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -8992,6 +9005,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -9032,6 +9049,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9719,6 +9740,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11714,12 +11783,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12501,9 +12600,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 92ddf9d6d2d..a65e208e1d5 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -380,8 +380,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namespace", - // "admin.registries.metadata.schemas.table.downloads": "Download", - "admin.registries.metadata.schemas.table.downloads": "Download", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Registro dei metadati", @@ -2672,6 +2673,22 @@ // "cris-layout.attachment.viewMore": "View More", "cris-layout.attachment.viewMore": "Vedi altro", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Controllare i collegamenti nei metadati", @@ -3302,7 +3319,9 @@ // "explore.title": "Explore section", "explore.title": "Sezione esplora", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description": "Syndication feed", @@ -5582,9 +5601,6 @@ // "menu.section.import_from_excel": "Import from excel", "menu.section.import_from_excel": "Importa da Excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", "menu.section.export_to_excel": "Esporta in Excel", @@ -6150,9 +6166,6 @@ // "nav.user.description" : "User profile bar", "nav.user.description" : "Barra del profilo utente", - // "nav.subscriptions" : "Subscriptions", - "nav.subscriptions" : "Subscription", - // "none.listelement.badge": "Item", "none.listelement.badge": "Item", @@ -7695,6 +7708,9 @@ // "search.filters.filter.investigators.head": "Researcher", "search.filters.filter.investigators.head": "Ricercatore", + // "search.filters.filter.investigators.label": "Researcher", + "search.filters.filter.investigators.label": "Ricercatore", + // "search.filters.filter.investigators.placeholder": "Researcher", "search.filters.filter.investigators.placeholder": "Ricercatore", @@ -7728,6 +7744,9 @@ // "search.filters.filter.projectOrgUnits.head" : "Organizations", "search.filters.filter.projectOrgUnits.head" : "Strutture", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + "search.filters.filter.projectOrgUnits.label" : "Strutture", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", "search.filters.filter.projectOrgUnits.placeholder" : "Strutture", @@ -8286,6 +8305,54 @@ // "statistics.categories.downloadReports.tab": "Downloads reports", "statistics.categories.downloadReports.tab": "Report sui download", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", "statistics.reports.title": "Report", @@ -9969,11 +10036,41 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "Vista Workspace", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", "workflow-item.advanced.title": "Workflow avanzato", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", "workflow-item.selectrevieweraction.notification.success.title": "Revisore selezionato", @@ -10560,7 +10657,7 @@ // "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", "admin.system-wide-alert.breadcrumbs": "Allarmi di sistema", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" "admin.system-wide-alert.title": "Allarmi di sistema", diff --git a/src/assets/i18n/ja.json5 b/src/assets/i18n/ja.json5 index e1336f45544..b5a8f0d1aef 100644 --- a/src/assets/i18n/ja.json5 +++ b/src/assets/i18n/ja.json5 @@ -504,6 +504,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", // TODO New key - Add a translation "admin.registries.metadata.schemas.table.namespace": "Namespace", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO New key - Add a translation @@ -3537,6 +3540,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", // TODO New key - Add a translation "curation-task.task.checklinks.label": "Check Links in Metadata", @@ -4374,7 +4393,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -7385,10 +7406,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -8137,10 +8154,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -10188,6 +10201,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -10232,6 +10249,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -10972,6 +10993,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -13205,12 +13274,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -13992,9 +14091,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/kk.json5 b/src/assets/i18n/kk.json5 index 68a8f79e7e0..01be114b9cf 100644 --- a/src/assets/i18n/kk.json5 +++ b/src/assets/i18n/kk.json5 @@ -422,6 +422,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Аттар кеңістігі", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Метадеректер тізілімі", @@ -2945,6 +2948,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Метадеректердегі Сілтемелерді Тексеріңіз", @@ -3701,7 +3720,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description": "Синдикация арнасы", @@ -6189,10 +6210,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6840,10 +6857,6 @@ // "nav.user.description" : "User profile bar", "nav.user.description" : "Пайдаланушы профилі тақтасы", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "Элемент", @@ -8584,6 +8597,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8622,6 +8639,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9286,6 +9307,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11128,12 +11197,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "Жұмыс аймағының түрі", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -11789,9 +11888,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/lv.json5 b/src/assets/i18n/lv.json5 index 9151b0ada13..129ef019eb9 100644 --- a/src/assets/i18n/lv.json5 +++ b/src/assets/i18n/lv.json5 @@ -450,6 +450,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Nosaukumvieta", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3188,6 +3191,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", // TODO New key - Add a translation "curation-task.task.checklinks.label": "Check Links in Metadata", @@ -3996,7 +4015,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6723,10 +6744,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7394,10 +7411,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -9329,6 +9342,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -9369,6 +9386,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -10063,6 +10084,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -12160,12 +12229,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12947,9 +13046,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/nl.json5 b/src/assets/i18n/nl.json5 index d85a89c476d..fffb7ec187a 100644 --- a/src/assets/i18n/nl.json5 +++ b/src/assets/i18n/nl.json5 @@ -451,6 +451,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namespace", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3369,6 +3372,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", // TODO New key - Add a translation "curation-task.task.checklinks.label": "Check Links in Metadata", @@ -4179,7 +4198,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6979,10 +7000,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7653,10 +7670,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -9621,6 +9634,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -9661,6 +9678,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -10365,6 +10386,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -12518,12 +12587,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -13305,9 +13404,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/pl.json5 b/src/assets/i18n/pl.json5 index 26f7f43438c..aceae933277 100644 --- a/src/assets/i18n/pl.json5 +++ b/src/assets/i18n/pl.json5 @@ -423,6 +423,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace":"Nazwa schematu", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title":"Rejestr metadanych", @@ -2943,6 +2946,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label":"Sprawdź odnośniki w metadanych", @@ -3700,7 +3719,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description":"Aktualności", @@ -6251,9 +6272,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_batch":"Eksport masowy (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6901,10 +6919,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge":"Pozycja", @@ -8655,6 +8669,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8693,6 +8711,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9357,6 +9379,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11200,12 +11270,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title":"Widok wersji roboczej", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -11888,9 +11988,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/pt-BR.json5 b/src/assets/i18n/pt-BR.json5 index c527dd05941..ca2d7acaaed 100644 --- a/src/assets/i18n/pt-BR.json5 +++ b/src/assets/i18n/pt-BR.json5 @@ -422,6 +422,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namespace", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Registro de Metadados", @@ -2931,6 +2934,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Verificar links em metadados", @@ -3681,7 +3700,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", "feed.description": "Feed de distribuição", @@ -6172,9 +6193,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_batch": "Exportação em Lote (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6823,10 +6841,6 @@ // "nav.user.description" : "User profile bar", "nav.user.description" : "Barra de perfil do usuário", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", "none.listelement.badge": "Item", @@ -8563,6 +8577,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8601,6 +8619,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9265,6 +9287,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11114,12 +11184,42 @@ // "workspace-item.view.title": "Workspace View", "workspace-item.view.title": "Visualização da Área de Trabalho (Workspace)", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -11775,9 +11875,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/pt-PT.json5 b/src/assets/i18n/pt-PT.json5 index 53f7b302502..38155c7e184 100644 --- a/src/assets/i18n/pt-PT.json5 +++ b/src/assets/i18n/pt-PT.json5 @@ -437,6 +437,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namespace", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3064,6 +3067,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Verifica ligações nos metadados", @@ -3846,7 +3865,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6497,10 +6518,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7159,10 +7176,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -8955,6 +8968,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8995,6 +9012,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9673,6 +9694,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11644,12 +11713,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12431,9 +12530,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/sv.json5 b/src/assets/i18n/sv.json5 index 8dd9863f04d..656bda5739b 100644 --- a/src/assets/i18n/sv.json5 +++ b/src/assets/i18n/sv.json5 @@ -438,6 +438,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namnrymd", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", "admin.registries.metadata.title": "Metadata registry", @@ -2982,6 +2985,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Kontrollera länkar i metadata", @@ -3750,7 +3769,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6327,10 +6348,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -6990,10 +7007,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -8851,6 +8864,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -8889,6 +8906,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9561,6 +9582,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11496,12 +11565,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12280,9 +12379,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/sw.json5 b/src/assets/i18n/sw.json5 index e1336f45544..b5a8f0d1aef 100644 --- a/src/assets/i18n/sw.json5 +++ b/src/assets/i18n/sw.json5 @@ -504,6 +504,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", // TODO New key - Add a translation "admin.registries.metadata.schemas.table.namespace": "Namespace", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO New key - Add a translation @@ -3537,6 +3540,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", // TODO New key - Add a translation "curation-task.task.checklinks.label": "Check Links in Metadata", @@ -4374,7 +4393,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -7385,10 +7406,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -8137,10 +8154,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -10188,6 +10201,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -10232,6 +10249,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -10972,6 +10993,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -13205,12 +13274,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -13992,9 +14091,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/tr.json5 b/src/assets/i18n/tr.json5 index 8ccbcf0dc5a..c714a352b10 100644 --- a/src/assets/i18n/tr.json5 +++ b/src/assets/i18n/tr.json5 @@ -437,6 +437,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Ad Alanı", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3075,6 +3078,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Metadatalardaki Bağlantıları Kontrol Et", @@ -3858,7 +3877,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6511,10 +6532,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7175,10 +7192,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -8982,6 +8995,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -9022,6 +9039,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9709,6 +9730,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11699,12 +11768,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12486,9 +12585,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file diff --git a/src/assets/i18n/uk.json5 b/src/assets/i18n/uk.json5 index b9c051b357e..f1808d29c42 100644 --- a/src/assets/i18n/uk.json5 +++ b/src/assets/i18n/uk.json5 @@ -438,6 +438,9 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Простір", + // "admin.registries.metadata.schemas.table.download": "Download", + // TODO New key - Add a translation + "admin.registries.metadata.schemas.table.download": "Download", // "admin.registries.metadata.title": "Metadata Registry", // TODO Source message changed - Revise the translation @@ -3094,6 +3097,22 @@ // TODO New key - Add a translation "cris-layout.attachment.viewMore": "View More", + // "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + // TODO New key - Add a translation + "cris-layout.rendering.collections.owning-collection.label": "Owning collection", + + // "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + // TODO New key - Add a translation + "cris-layout.rendering.collections.mapped-collection.label": "Mapped collections", + + // "cris-layout.rendering.collections.loading": "Loading...", + // TODO New key - Add a translation + "cris-layout.rendering.collections.loading": "Loading...", + + // "cris-layout.rendering.collections.load-more": "Load more", + // TODO New key - Add a translation + "cris-layout.rendering.collections.load-more": "Load more", + // "curation-task.task.checklinks.label": "Check Links in Metadata", "curation-task.task.checklinks.label": "Перевірте посилання у метаданих", @@ -3878,7 +3897,9 @@ // "explore.title": "Explore section", // TODO New key - Add a translation "explore.title": "Explore section", - + // "export-schema.process.title": "Metadata Schema Export", + // TODO New key - Add a translation + "export-schema.process.title": "Metadata Schema Export", // "feed.description": "Syndication feed", // TODO New key - Add a translation @@ -6539,10 +6560,6 @@ // TODO New key - Add a translation "menu.section.import_from_excel": "Import from excel", - // "menu.section.export_batch": "Batch Export (ZIP)", - // TODO New key - Add a translation - "menu.section.export_batch": "Batch Export (ZIP)", - // "menu.section.export_to_excel": "Export to excel", // TODO New key - Add a translation "menu.section.export_to_excel": "Export to excel", @@ -7203,10 +7220,6 @@ // TODO New key - Add a translation "nav.user.description" : "User profile bar", - // "nav.subscriptions" : "Subscriptions", - // TODO New key - Add a translation - "nav.subscriptions" : "Subscriptions", - // "none.listelement.badge": "Item", // TODO New key - Add a translation "none.listelement.badge": "Item", @@ -9011,6 +9024,10 @@ // TODO New key - Add a translation "search.filters.filter.investigators.head": "Researcher", + // "search.filters.filter.investigators.label": "Researcher", + // TODO New key - Add a translation + "search.filters.filter.investigators.label": "Researcher", + // "search.filters.filter.investigators.placeholder": "Researcher", // TODO New key - Add a translation "search.filters.filter.investigators.placeholder": "Researcher", @@ -9051,6 +9068,10 @@ // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.head" : "Organizations", + // "search.filters.filter.projectOrgUnits.label" : "Organizations", + // TODO New key - Add a translation + "search.filters.filter.projectOrgUnits.label" : "Organizations", + // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", // TODO New key - Add a translation "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", @@ -9738,6 +9759,54 @@ // TODO New key - Add a translation "statistics.categories.downloadReports.tab": "Downloads reports", + // "statistics.categories.itemReports.tab": "Item views reports", + // TODO New key - Add a translation + "statistics.categories.itemReports.tab": "Item views reports", + + // "statistics.table.itemReports.header.continent" : "Continent", + // TODO New key - Add a translation + "statistics.table.itemReports.header.continent" : "Continent", + + // "statistics.table.itemReports.header.views" : "Views", + // TODO New key - Add a translation + "statistics.table.itemReports.header.views" : "Views", + + // "statistics.table.itemReports.header.city" : "City", + // TODO New key - Add a translation + "statistics.table.itemReports.header.city" : "City", + + // "statistics.table.itemReports.header.item" : "Item", + // TODO New key - Add a translation + "statistics.table.itemReports.header.item" : "Item", + + // "statistics.table.mainReports.header.community" : "Community", + // TODO New key - Add a translation + "statistics.table.mainReports.header.community" : "Community", + + // "statistics.table.mainReports.header.collection" : "Collection", + // TODO New key - Add a translation + "statistics.table.mainReports.header.collection" : "Collection", + + // "statistics.table.itemReports.title.TopContinents" : "Top region views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopContinents" : "Top region views", + + // "statistics.table.itemReports.title.TopCities" : "Top cities views", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCities" : "Top cities views", + + // "statistics.table.itemReports.title.TopItems" : "Most viewed", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopItems" : "Most viewed", + + // "statistics.table.itemReports.title.TopCategories" : "Top categories", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TopCategories" : "Top categories", + + // "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // TODO New key - Add a translation + "statistics.table.itemReports.title.TotalVisitsPerMonth" : "Top views per month", + // "statistics.reports.title": "Reports", // TODO New key - Add a translation "statistics.reports.title": "Reports", @@ -11722,12 +11791,42 @@ // TODO New key - Add a translation "workspace-item.view.title": "Workspace View", + // "workspace-item.delete.breadcrumbs": "Workspace Delete", + // TODO New key - Add a translation + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + // "workspace-item.delete.header": "Delete workspace item", + // TODO New key - Add a translation + "workspace-item.delete.header": "Delete workspace item", + + // "workspace-item.delete.button.confirm": "Delete", + // TODO New key - Add a translation + "workspace-item.delete.button.confirm": "Delete", + + // "workspace-item.delete.button.cancel": "Cancel", + // TODO New key - Add a translation + "workspace-item.delete.button.cancel": "Cancel", + + // "workspace-item.delete.notification.success.title": "Deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.success.title": "Deleted", + + // "workspace-item.delete.title": "This workspace item was successfully deleted", + // TODO New key - Add a translation + "workspace-item.delete.title": "This workspace item was successfully deleted", + + // "workspace-item.delete.notification.error.title": "Something went wrong", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.title": "Something went wrong", + + // "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", + // TODO New key - Add a translation + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", // "workflow-item.advanced.title": "Advanced workflow", // TODO New key - Add a translation "workflow-item.advanced.title": "Advanced workflow", - // "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", // TODO New key - Add a translation "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", @@ -12508,9 +12607,9 @@ // TODO New key - Add a translation "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - // "admin.system-wide-alert.title": "System-wide Alerts", + // "admin.system-wide-alert.title": "System-wide Alerts" // TODO New key - Add a translation - "admin.system-wide-alert.title": "System-wide Alerts", + "admin.system-wide-alert.title": "System-wide Alerts" } \ No newline at end of file From 0397f3c3bd602711b701a7ab86ea1dd335f630c2 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 29 Sep 2023 17:56:11 +0200 Subject: [PATCH 132/195] [CST-10703] component rearrangements & validate email logic --- src/app/app-routing.module.ts | 5 + .../external-login-page.component.ts | 2 +- ...review-account-info-page-routing.module.ts | 16 +++ ...in-review-account-info-page.component.html | 13 +++ ...in-review-account-info-page.component.scss | 0 ...review-account-info-page.component.spec.ts | 25 +++++ ...ogin-review-account-info-page.component.ts | 65 +++++++++++ ...l-login-review-account-info-page.module.ts | 27 +++++ .../helpers/compare-values.pipe.ts | 0 .../review-account-info.component.html | 0 .../review-account-info.component.scss | 0 .../review-account-info.component.spec.ts | 0 .../review-account-info.component.ts | 1 - ...ogin-review-account-info-page.component.ts | 25 +++++ .../email-validated.component.ts | 8 +- ...ernal-login-validation-page.component.html | 12 +- ...xternal-login-validation-page.component.ts | 103 +++++++++++++----- .../external-login-validation-page.module.ts | 8 +- .../provide-email.component.html | 4 +- .../external-log-in.component.html | 6 +- .../external-log-in.component.ts | 19 ++-- .../models/registration-data.mock.model.ts | 2 +- .../services/external-login.service.ts | 1 + src/assets/i18n/en.json5 | 2 +- 24 files changed, 276 insertions(+), 68 deletions(-) create mode 100644 src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts create mode 100644 src/app/external-login-review-account-info/external-login-review-account-info-page.component.html create mode 100644 src/app/external-login-review-account-info/external-login-review-account-info-page.component.scss create mode 100644 src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts create mode 100644 src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts create mode 100644 src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts rename src/app/{external-login-validation-page => external-login-review-account-info}/helpers/compare-values.pipe.ts (100%) rename src/app/{external-login-validation-page => external-login-review-account-info}/review-account-info/review-account-info.component.html (100%) rename src/app/{external-login-validation-page => external-login-review-account-info}/review-account-info/review-account-info.component.scss (100%) rename src/app/{external-login-validation-page => external-login-review-account-info}/review-account-info/review-account-info.component.spec.ts (100%) rename src/app/{external-login-validation-page => external-login-review-account-info}/review-account-info/review-account-info.component.ts (99%) create mode 100644 src/app/external-login-review-account-info/themed-external-login-review-account-info-page.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d5f3a5aecdc..2fd18766af9 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -181,6 +181,11 @@ import { RedirectService } from './redirect/redirect.service'; loadChildren: () => import('./external-login-validation-page/external-login-validation-page.module') .then((m) => m.ExternalLoginValidationPageModule) }, + { + path: 'review-account', + loadChildren: () => import('./external-login-review-account-info/external-login-review-account-info-page.module') + .then((m) => m.ExternalLoginReviewAccountInfoModule) + }, { path: 'email-confirmation', loadChildren: () => import('./external-login-email-confirmation-page/external-login-email-confirmation-page.module') diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index 8a41bc4a8ad..b98e823fc90 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -41,7 +41,7 @@ export class ExternalLoginPageComponent implements OnInit { ngOnInit(): void { this.getRegistrationData(); // TODO: remove this line (temporary) - // this.token = '1234567890'; + this.token = '1234567890'; } /** diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts new file mode 100644 index 00000000000..94f2ef42664 --- /dev/null +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; + +const routes: Routes = [ + { + path: '', + pathMatch: 'full', + component: ExternalLoginReviewAccountInfoPageComponent, +},]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ExternalLoginReviewAccountInfoRoutingModule { } diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html new file mode 100644 index 00000000000..6d5defa2ca4 --- /dev/null +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html @@ -0,0 +1,13 @@ +
+ + + + +
diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.scss b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts new file mode 100644 index 00000000000..ef2dfe82017 --- /dev/null +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; + +describe('ExternalLoginReviewAccountInfoPageComponent', () => { + let component: ExternalLoginReviewAccountInfoPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ExternalLoginReviewAccountInfoPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExternalLoginReviewAccountInfoPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts new file mode 100644 index 00000000000..8b02a347c51 --- /dev/null +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts @@ -0,0 +1,65 @@ +import { Component, OnInit } from '@angular/core'; +import { AlertType } from '../shared/alert/aletr-type'; +import { Observable, map } from 'rxjs'; +import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; +import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; +import { ActivatedRoute } from '@angular/router'; +import { hasValue } from '../shared/empty.util'; +import { getRemoteDataPayload } from '../core/shared/operators'; +import { Registration } from '../core/shared/registration.model'; + +@Component({ + templateUrl: './external-login-review-account-info-page.component.html', + styleUrls: ['./external-login-review-account-info-page.component.scss'] +}) +export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { + /** + * The token used to get the registration data + */ + public token: string; + + /** + * The type of alert to show + */ + public AlertTypeEnum = AlertType; + + /** + * The registration data of the user + */ + public registrationData$: Observable; + // = of( + // mockRegistrationDataModel + // ); + + constructor( + private epersonRegistrationService: EpersonRegistrationService, + private arouter: ActivatedRoute + ) { + this.token = this.arouter.snapshot.queryParams.token; + } + + + ngOnInit(): void { + // -> if email address is not used by other user => Email Validated component + // -> if email address is used by other user => Review account information component + this.getRegistrationData(); + // TODO: remove this line (temporary) + // this.token = '1234567890'; + } + /** + * Get the registration data from the token + */ + getRegistrationData() { + if (hasValue(this.token)) { + this.registrationData$ = this.epersonRegistrationService + .searchByToken(this.token) + .pipe( + getRemoteDataPayload(), + map((registration: Registration) => + Object.assign(new RegistrationData(), registration) + ) + ); + } + } + +} diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts new file mode 100644 index 00000000000..368f988a829 --- /dev/null +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ExternalLoginReviewAccountInfoRoutingModule } from './external-login-review-account-info-page-routing.module'; +import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; +import { CompareValuesPipe } from './helpers/compare-values.pipe'; +import { ThemedExternalLoginReviewAccountInfoPageComponent } from './themed-external-login-review-account-info-page.component'; +import { ReviewAccountInfoComponent } from './review-account-info/review-account-info.component'; +import { UiSwitchModule } from 'ngx-ui-switch'; +import { SharedModule } from '../shared/shared.module'; + + +@NgModule({ + declarations: [ + ExternalLoginReviewAccountInfoPageComponent, + CompareValuesPipe, + ThemedExternalLoginReviewAccountInfoPageComponent, + ReviewAccountInfoComponent + ], + imports: [ + CommonModule, + ExternalLoginReviewAccountInfoRoutingModule, + SharedModule, + UiSwitchModule, + ] +}) +export class ExternalLoginReviewAccountInfoModule { } diff --git a/src/app/external-login-validation-page/helpers/compare-values.pipe.ts b/src/app/external-login-review-account-info/helpers/compare-values.pipe.ts similarity index 100% rename from src/app/external-login-validation-page/helpers/compare-values.pipe.ts rename to src/app/external-login-review-account-info/helpers/compare-values.pipe.ts diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.html b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html similarity index 100% rename from src/app/external-login-validation-page/review-account-info/review-account-info.component.html rename to src/app/external-login-review-account-info/review-account-info/review-account-info.component.html diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.scss b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.scss similarity index 100% rename from src/app/external-login-validation-page/review-account-info/review-account-info.component.scss rename to src/app/external-login-review-account-info/review-account-info/review-account-info.component.scss diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts similarity index 100% rename from src/app/external-login-validation-page/review-account-info/review-account-info.component.spec.ts rename to src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts diff --git a/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts similarity index 99% rename from src/app/external-login-validation-page/review-account-info/review-account-info.component.ts rename to src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index 80e9e2a1237..93952c2a26f 100644 --- a/src/app/external-login-validation-page/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -7,7 +7,6 @@ import { } from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { EPersonMock } from '../../shared/testing/eperson.mock'; import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; import { Observable, Subscription, filter, from, switchMap, take } from 'rxjs'; import { RemoteData } from 'src/app/core/data/remote-data'; diff --git a/src/app/external-login-review-account-info/themed-external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info/themed-external-login-review-account-info-page.component.ts new file mode 100644 index 00000000000..53139bb7811 --- /dev/null +++ b/src/app/external-login-review-account-info/themed-external-login-review-account-info-page.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { ThemedComponent } from '../shared/theme-support/themed.component'; +import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; + +/** + * Themed wrapper for ExternalLoginReviewAccountInfoPageComponent + */ +@Component({ + selector: 'ds-themed-external-login-page', + styleUrls: [], + templateUrl: './../shared/theme-support/themed.component.html' +}) +export class ThemedExternalLoginReviewAccountInfoPageComponent extends ThemedComponent { + protected getComponentName(): string { + return 'ExternalLoginReviewAccountInfoPageComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../themes/${themeName}/app/external-login-review-account-info/external-login-review-account-info-page.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./external-login-review-account-info-page.component`); + } +} diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.ts index c469b8ccbc1..4147dd23720 100644 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.ts +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.ts @@ -14,12 +14,6 @@ export class EmailValidatedComponent { @Input() registrationToken: string; constructor(private authService: AuthService, private router: Router) { - // if user is logged in, redirect to home page - // in case user logs in with an existing account - this.authService.isAuthenticated().subscribe((isAuthenticated: boolean) => { - if (isAuthenticated) { - this.router.navigate(['/']); - } - }); + this.authService.setRedirectUrl('/review-account'); } } diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.html b/src/app/external-login-validation-page/external-login-validation-page.component.html index 3c9fa39d0b0..3efd1ce0e38 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.html +++ b/src/app/external-login-validation-page/external-login-validation-page.component.html @@ -1,15 +1,9 @@
- - - - + - + diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.ts b/src/app/external-login-validation-page/external-login-validation-page.component.ts index 4146ace1be2..7a20473ddbe 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.ts +++ b/src/app/external-login-validation-page/external-login-validation-page.component.ts @@ -1,18 +1,25 @@ -import { Component, OnInit } from '@angular/core'; -import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; +import { Component } from '@angular/core'; import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; import { ActivatedRoute } from '@angular/router'; -import { hasValue } from '../shared/empty.util'; -import { Registration } from '../core/shared/registration.model'; -import { Observable, map, of } from 'rxjs'; -import { getRemoteDataPayload } from '../core/shared/operators'; import { AlertType } from '../shared/alert/aletr-type'; +import { hasNoValue, hasValue } from '../shared/empty.util'; +import { getRemoteDataPayload } from '../core/shared/operators'; +import { BehaviorSubject, Observable, map, of, switchMap, tap } from 'rxjs'; +import { Registration } from '../core/shared/registration.model'; +import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; +import { RemoteData } from '../core/data/remote-data'; +import { NotificationsService } from '../shared/notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { EPersonDataService } from '../core/eperson/eperson-data.service'; +import { MetadataValue } from '../core/shared/metadata.models'; +import { EPerson } from '../core/eperson/models/eperson.model'; +import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; @Component({ templateUrl: './external-login-validation-page.component.html', styleUrls: ['./external-login-validation-page.component.scss'], }) -export class ExternalLoginValidationPageComponent implements OnInit { +export class ExternalLoginValidationPageComponent { /** * The token used to get the registration data */ @@ -23,41 +30,81 @@ export class ExternalLoginValidationPageComponent implements OnInit { */ public AlertTypeEnum = AlertType; - /** - * The registration data of the user - */ - public registrationData$: Observable - // = of( - // mockRegistrationDataModel - // ); + private validationFailed: BehaviorSubject = + new BehaviorSubject(false); constructor( private epersonRegistrationService: EpersonRegistrationService, - private arouter: ActivatedRoute + private arouter: ActivatedRoute, + private epersonDataService: EPersonDataService, + private notificationService: NotificationsService, + private translateService: TranslateService ) { this.token = this.arouter.snapshot.queryParams.token; + this.token = '1234567890'; // TODO: remove this line (temporary) } ngOnInit(): void { - // -> if email address is not used by other user => Email Validated component - // -> if email address is used by other user => Review account information component - this.getRegistrationData(); - // TODO: remove this line (temporary) - // this.token = '1234567890'; + // TODO: Uncomment this line later + // this.getRegistrationData(); } + + public hasFailed(): Observable { + return this.validationFailed.asObservable(); + } + /** * Get the registration data from the token */ getRegistrationData() { + this.validationFailed.next(true); + if (hasValue(this.token)) { - this.registrationData$ = this.epersonRegistrationService - .searchByToken(this.token) - .pipe( - getRemoteDataPayload(), - map((registration: Registration) => - Object.assign(new RegistrationData(), registration) - ) - ); + this.fetchRegistrationDataAndCreateUser(this.token); } } + + fetchRegistrationDataAndCreateUser(token: string) { + this.epersonRegistrationService + .searchByToken(token) + .pipe( + switchMap((rd) => { + if (hasValue(rd.payload) && hasNoValue(rd.payload.user)) { + const registrationData = Object.assign( + new RegistrationData(), + rd.payload + ); + return this.createUserFromToken(token, registrationData); + } else { + return of(rd); + } + }) + ) + .subscribe((rd: RemoteData) => { + if (rd.hasFailed) { + this.validationFailed.next(true); + } + }); + } + + createUserFromToken(token: string, registrationData: RegistrationData) { + const metadataValues = Object.entries(registrationData.registrationMetadata) + .filter(([key, value]) => hasValue(value[0]?.value)) + .map(([key, value]) => ({ + key, + value: value[0]?.value, + })); + const eperson = Object.assign(new EPerson(), { + metadata: metadataValues, + canLogIn: true, + requireCertificate: false, + }); + return this.epersonDataService.createEPersonForToken(eperson, token).pipe( + tap((rd: RemoteData) => { + if (rd.hasFailed) { + this.validationFailed.next(true); + } + }) + ); + } } diff --git a/src/app/external-login-validation-page/external-login-validation-page.module.ts b/src/app/external-login-validation-page/external-login-validation-page.module.ts index ba41cf95a1b..3bad614d7e6 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.module.ts +++ b/src/app/external-login-validation-page/external-login-validation-page.module.ts @@ -5,26 +5,20 @@ import { ExternalLoginValidationPageRoutingModule } from './external-login-valid import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; import { ThemedExternalLoginValidationPageComponent } from './themed-external-login-validation-page.component'; -import { UiSwitchModule } from 'ngx-ui-switch'; -import { ReviewAccountInfoComponent } from './review-account-info/review-account-info.component'; import { EmailValidatedComponent } from './email-validated/email-validated.component'; import { SharedModule } from '../shared/shared.module'; -import { CompareValuesPipe } from './helpers/compare-values.pipe'; @NgModule({ declarations: [ ExternalLoginValidationPageComponent, ThemedExternalLoginValidationPageComponent, - ReviewAccountInfoComponent, EmailValidatedComponent, - CompareValuesPipe ], imports: [ CommonModule, ExternalLoginValidationPageRoutingModule, - SharedModule, - UiSwitchModule, + SharedModule ] }) export class ExternalLoginValidationPageModule { } diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html index b5a2efa7b68..46e804e1c2e 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html @@ -17,7 +17,7 @@

emailForm.get('email').hasError('required') && emailForm.get('email').touched " - class="error-message" + class="text-danger" > {{ "external-login.confirmation.email-required" | translate }}

@@ -26,7 +26,7 @@

emailForm.get('email').hasError('email') && emailForm.get('email').touched " - class="error-message" + class="text-danger" > {{ "external-login.confirmation.email-invalid" | translate }}

diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html index 59219922470..47a301501bf 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html @@ -36,14 +36,16 @@
diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts index 3b82ab70ca1..2ef5df0cae1 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts @@ -57,15 +57,7 @@ export class ExternalLogInComponent implements OnInit { private modalService: NgbModal, private authService: AuthService, private router: Router - ) { - // if user is logged in, redirect to home page - // in case user logs in with an existing account - this.authService.isAuthenticated().subscribe((isAuthenticated: boolean) => { - if (isAuthenticated) { - this.router.navigate(['/']); - } - }); - } + ) {} /** * Provide the registration data object to the objectInjector. @@ -124,6 +116,15 @@ export class ExternalLogInComponent implements OnInit { openLoginModal(content: any) { this.modalRef = this.modalService.open(content); + this.authService.setRedirectUrl('/review-account'); + + this.modalRef.dismissed.subscribe(() => { + this.clearRedirectUrl(); + }); + } + + clearRedirectUrl() { + this.authService.clearRedirectUrl(); } ngOnDestroy(): void { diff --git a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts index 961d443a665..9ef3d4162c1 100644 --- a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts +++ b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts @@ -6,7 +6,7 @@ export const mockRegistrationDataModel: RegistrationData = Object.assign( new RegistrationData(), { id: '3', - email: 'user@institution.edu', + email: null,//'user@institution.edu', user: '028dcbb8-0da2-4122-a0ea-254be49ca107', registrationType: AuthMethodType.Orcid, netId: '0000-1111-2222-3333', diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/shared/external-log-in-complete/services/external-login.service.ts index f757bb2bdbf..4f60d9e5d25 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.ts @@ -29,6 +29,7 @@ export class ExternalLoginService { */ patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operation: 'add' | 'replace'): Observable> { const updatedValues = values.map((value) => value); + this.router.navigate(['/email-confirmation']); // TODO: remove this line (temporary) return this.epersonRegistrationService.patchUpdateRegistration(updatedValues, field, registrationId, token, operation).pipe( getFirstCompletedRemoteData(), map((rd) => { diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 9d3161f0aaf..c2be335f99f 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7222,7 +7222,7 @@ "review-account-info.merge-data.notification.error": "Something went wrong while updating your account information", - "external-login.validate-email.no-token": "The validation link is no longer valid. Please try again.", + "external-login.validate-email.no-token": "Something went wrong. Your email address is not validated succesfully.", "external-login-page.provide-email.notifications.error": "Something went wrong.Email address was omitted or the operation is not valid.", } From 95600d75761da5ca5292647227c5a90e51b84371 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Mon, 2 Oct 2023 15:43:37 +0300 Subject: [PATCH 133/195] [DSC-1226] set this.model.metadataValue as currentValue if present on init in dynamic-scrollable-dropdown.component.ts --- .../dynamic-scrollable-dropdown.component.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts index 75a21973590..4be743f2162 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts @@ -77,6 +77,10 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom * Initialize the component, setting up the init form value */ ngOnInit() { + if (this.model.metadataValue) { + this.setCurrentValue(this.model.metadataValue, true); + } + this.updatePageInfo(this.model.maxOptions, 1); this.retrieveEntries(null, true); From 8ab3fc91f239331e447174aa1449b66f501f918c Mon Sep 17 00:00:00 2001 From: Andrea Barbasso <´andrea.barbasso@4science.com´> Date: Mon, 2 Oct 2023 15:38:45 +0200 Subject: [PATCH 134/195] [DSC-1242] fix explore browsing --- .../shared/dso-page/dso-edit-menu.resolver.ts | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.ts index e7d4695d1c3..cd2aa27fca5 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -1,26 +1,26 @@ -import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; -import { combineLatest, Observable, of as observableOf } from 'rxjs'; -import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { MenuService } from '../menu/menu.service'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { Injectable } from '@angular/core'; -import { LinkMenuItemModel } from '../menu/menu-item/models/link.model'; -import { Item } from '../../core/shared/item.model'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { OnClickMenuItemModel } from '../menu/menu-item/models/onclick.model'; -import { getFirstCompletedRemoteData } from '../../core/shared/operators'; -import { map, switchMap } from 'rxjs/operators'; -import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; -import { URLCombiner } from '../../core/url-combiner/url-combiner'; -import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service'; -import { hasNoValue, hasValue, isNotEmpty } from '../empty.util'; -import { MenuID } from '../menu/menu-id.model'; -import { MenuItemType } from '../menu/menu-item-type.model'; -import { MenuSection } from '../menu/menu-section.model'; -import { getDSORoute } from '../../app-routing-paths'; -import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; -import { NotificationsService } from '../notifications/notifications.service'; -import { TranslateService } from '@ngx-translate/core'; +import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router'; +import {combineLatest, Observable, of as observableOf} from 'rxjs'; +import {FeatureID} from '../../core/data/feature-authorization/feature-id'; +import {MenuService} from '../menu/menu.service'; +import {AuthorizationDataService} from '../../core/data/feature-authorization/authorization-data.service'; +import {Injectable} from '@angular/core'; +import {LinkMenuItemModel} from '../menu/menu-item/models/link.model'; +import {Item} from '../../core/shared/item.model'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {OnClickMenuItemModel} from '../menu/menu-item/models/onclick.model'; +import {getFirstCompletedRemoteData} from '../../core/shared/operators'; +import {map, switchMap} from 'rxjs/operators'; +import {DSpaceObjectDataService} from '../../core/data/dspace-object-data.service'; +import {URLCombiner} from '../../core/url-combiner/url-combiner'; +import {DsoVersioningModalService} from './dso-versioning-modal-service/dso-versioning-modal.service'; +import {hasNoValue, hasValue, isNotEmpty} from '../empty.util'; +import {MenuID} from '../menu/menu-id.model'; +import {MenuItemType} from '../menu/menu-item-type.model'; +import {MenuSection} from '../menu/menu-section.model'; +import {getDSORoute} from '../../app-routing-paths'; +import {ResearcherProfileDataService} from '../../core/profile/researcher-profile-data.service'; +import {NotificationsService} from '../notifications/notifications.service'; +import {TranslateService} from '@ngx-translate/core'; /** * Creates the menus for the dspace object pages @@ -49,6 +49,8 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection let id = route.params.id; if (hasNoValue(id) && hasValue(route.queryParams.scope)) { id = route.queryParams.scope; + } else if (hasNoValue(id) && hasNoValue(route.queryParams.scope)) { + return observableOf({}); } return this.dSpaceObjectDataService.findById(id, true, false).pipe( getFirstCompletedRemoteData(), From 5175285b5bc43864b08ba2d0809bbbfbbc26e78d Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 2 Oct 2023 15:55:03 +0200 Subject: [PATCH 135/195] [DSC-1153] Fixes --- .../bitstream-attachment.component.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html index 038da280b9a..aaa4d4d43ca 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html @@ -1,19 +1,19 @@ -
+
-
+
-
-
+
+
-
+
-
+
From 7e307097d6b3a5949ee4a93bdc5e86ccdba4cf7d Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 3 Oct 2023 12:55:01 +0200 Subject: [PATCH 136/195] [DSC-1277] Fix en.json5 --- src/assets/i18n/en.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 406b53e06c6..1f7250507e1 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7171,5 +7171,5 @@ "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", - "admin.system-wide-alert.title": "System-wide Alerts" + "admin.system-wide-alert.title": "System-wide Alerts", } From f3939626d293e025e8f9e1db251fccaf57119888 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 3 Oct 2023 16:08:24 +0200 Subject: [PATCH 137/195] [DSC-1277] New Italian translations --- src/assets/i18n/it.json5 | 77 +++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index a65e208e1d5..8f16730d429 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -422,7 +422,7 @@ "admin.registries.schema.form.element": "Elemento", // "admin.registries.schema.form.qualifier": "Qualifier", - "admin.registries.schema.form.qualifier": "Qualificatore", + "admin.registries.schema.form.qualifier": "Qualifier", // "admin.registries.schema.form.scopenote": "Scope Note", "admin.registries.schema.form.scopenote": "Nota di ambito", @@ -1051,7 +1051,7 @@ "admin.batch-import.page.header": "Batch Import", // "admin.metadata-import.page.help": "You can drop or browse CSV files that contain batch metadata operations on files here", - "admin.metadata-import.page.help": "È possibile rilasciare o sfogliare i file CSV che contengono operazioni di metadati batch sui file qui", + "admin.metadata-import.page.help": "È possibile trascinare o ricercare qui i file CSV che contengono le informazioni di import metadata", // "admin.batch-import.page.help": "Select the Collection to import into. Then, drop or browse to a Simple Archive Format (SAF) zip file that includes the Items to import", "admin.batch-import.page.help": "Selezionare la Collection in cui effettuare l'import. Quindi, rilasciare o sfogliare un file zip Simple Archive Format (SAF) che include gli elementi da importare", @@ -1442,7 +1442,7 @@ "browse.metadata.rpname": "Nome", // "browse.metadata.subject": "Subject", - "browse.metadata.subject": "Subject", + "browse.metadata.subject": "Oggetto", // "browse.metadata.type": "Type", "browse.metadata.type": "Digitare", @@ -3258,7 +3258,7 @@ "explore.index.rpname" : "Nome", // "explore.index.subject" : "Subject", - "explore.index.subject" : "Subject", + "explore.index.subject" : "Oggetto", // "explore.index.title" : "Title", "explore.index.title" : "Titolo", @@ -3291,7 +3291,7 @@ "explore.search-section.search-button": "Ricerca", // "explore.search-section.reset-button": "Reset", - "explore.search-section.reset-button": "Reset", + "explore.search-section.reset-button": "Annulla", // "explore.infrastructure.breadcrumbs": "Infrastructure", "explore.infrastructure.breadcrumbs": "Infrastrutture", @@ -3585,7 +3585,7 @@ "grant-deny-request-copy.email.send": "Invia", // "grant-deny-request-copy.email.subject": "Subject", - "grant-deny-request-copy.email.subject": "Subject", + "grant-deny-request-copy.email.subject": "Oggetto", // "grant-deny-request-copy.email.subject.empty": "Please enter a subject", "grant-deny-request-copy.email.subject.empty": "Inserisci un subject", @@ -4213,7 +4213,7 @@ "item.edit.move.inheritpolicies.checkbox": "Ereditare i criteri", // "item.edit.move.inheritpolicies.description": "Inherit the default policies of the destination collection", - "item.edit.move.inheritpolicies.description": "Ereditare i criteri predefiniti dell'insieme di destinazione", + "item.edit.move.inheritpolicies.description": "Eredita le policy di default della collezione di destinazione", // "item.edit.move.move": "Move", "item.edit.move.move": "Sposta", @@ -5567,16 +5567,16 @@ // "menu.section.edit": "Edit", - "menu.section.edit": "Modificare", + "menu.section.edit": "Modifica", // "menu.section.edit_collection": "Collection", - "menu.section.edit_collection": "Collezione", + "menu.section.edit_collection": "Collezioni", // "menu.section.edit_community": "Community", "menu.section.edit_community": "Community", // "menu.section.edit_item": "Item", - "menu.section.edit_item": "Articolo", + "menu.section.edit_item": "Item", @@ -5596,7 +5596,7 @@ "menu.section.export_metadata": "Metadati", // "menu.section.export_batch": "Batch Export (ZIP)", - "menu.section.export_batch": "Batch Export (ZIP)", + "menu.section.export_batch": "Export batch (ZIP)", // "menu.section.import_from_excel": "Import from excel", "menu.section.import_from_excel": "Importa da Excel", @@ -5656,7 +5656,7 @@ "menu.section.icon.user_agreement_edit": "Modifica accordo con l'utente finale", // "menu.section.icon.workflow": "Administer workflow menu section", - "menu.section.icon.workflow": "Amministrare la sezione del menu del flusso di lavoro", + "menu.section.icon.workflow": "Amministra la sezione del menu del workflow", // "menu.section.icon.unpin": "Unpin sidebar", "menu.section.icon.unpin": "Rimuovi la barra laterale", @@ -5664,10 +5664,10 @@ // "menu.section.import": "Import", - "menu.section.import": "Importazione", + "menu.section.import": "Importa", // "menu.section.import_batch": "Batch Import (ZIP)", - "menu.section.import_batch": "Importazione batch (ZIP)", + "menu.section.import_batch": "Import batch (ZIP)", // "menu.section.import_metadata": "Metadata", "menu.section.import_metadata": "Metadata", @@ -5695,10 +5695,10 @@ // "menu.section.pin": "Pin sidebar", - "menu.section.pin": "Pin barra laterale", + "menu.section.pin": "Fissa la barra laterale", // "menu.section.unpin": "Unpin sidebar", - "menu.section.unpin": "Rimuovere la barra laterale", + "menu.section.unpin": "Rimuovi la barra laterale", @@ -5706,7 +5706,7 @@ "menu.section.processes": "Processi", // "menu.section.health": "Health", - "menu.section.health": "Salute", + "menu.section.health": "Stato del sistema", @@ -5768,7 +5768,7 @@ "statistics.workflow.page.step-table.header": "Passaggi del Workflow eseguiti", // "statistics.workflow.page.step-table.step": "Step", - "statistics.workflow.page.step-table.step": "Passo", + "statistics.workflow.page.step-table.step": "Step", // "statistics.workflow.page.step-table.count": "Performed", "statistics.workflow.page.step-table.count": "Eseguito", @@ -5933,7 +5933,7 @@ "menu.section.edit_user_agreement": "Modifica accordo con l'utente finale", // "menu.section.workflow": "Administer Workflow", - "menu.section.workflow": "Amministrare il flusso di lavoro", + "menu.section.workflow": "Amministra il workflow", // "metadata-export-search.tooltip": "Export search results as CSV", @@ -6152,7 +6152,7 @@ "nav.search": "Ricerca", // "nav.statistics.header": "Statistics", - "nav.statistics.header": "Statistica", + "nav.statistics.header": "Statistiche", // "nav.stop-impersonating": "Stop impersonating EPerson", "nav.stop-impersonating": "Smetti di impersonare EPerson", @@ -7478,7 +7478,7 @@ "search.filters.applied.f.projectorgunits": "Organizzazione", // "search.filters.applied.f.subject": "Subject", - "search.filters.applied.f.subject": "Soggetto", + "search.filters.applied.f.subject": "Oggetto", // "search.filters.applied.f.submitter": "Submitter", "search.filters.applied.f.submitter": "Submitter", @@ -8038,22 +8038,22 @@ "sorting.score.DESC": "Maggior rilevanza", // "sorting.dc.date.issued.ASC": "Date Issued Ascending", - "sorting.dc.date.issued.ASC": "Data di pubblicazione Ascendente", + "sorting.dc.date.issued.ASC": "Data di pubblicazione crescente", // "sorting.dc.date.issued.DESC": "Date Issued Descending", - "sorting.dc.date.issued.DESC": "Data di pubblicazione Discendente", + "sorting.dc.date.issued.DESC": "Data di pubblicazione decrescente", // "sorting.dc.date.accessioned.ASC": "Accessioned Date Ascending", - "sorting.dc.date.accessioned.ASC": "Data di accesso Ascendente", + "sorting.dc.date.accessioned.ASC": "Data di accesso crescente", // "sorting.dc.date.accessioned.DESC": "Accessioned Date Descending", - "sorting.dc.date.accessioned.DESC": "Data di accesso Discendente", + "sorting.dc.date.accessioned.DESC": "Data di accesso decrescente", // "sorting.lastModified.ASC": "Last modified Ascending", - "sorting.lastModified.ASC": "Ultima modifica Ascendente", + "sorting.lastModified.ASC": "Ultima modifica crescente", // "sorting.lastModified.DESC": "Last modified Descending", - "sorting.lastModified.DESC": "Ultima modifica Discendente", + "sorting.lastModified.DESC": "Ultima modifica decrescente", // "sorting.ownerselection.ASC": "Owner Relevance Ascending", "sorting.ownerselection.ASC": "Rilevanza del proprietario crescente", @@ -8132,13 +8132,13 @@ "statistics.table.mainReports.title.TopCountries": "Paesi con più visite", // "statistics.table.mainReports.title.TopContinents": "Top region views", - "statistics.table.mainReports.title.TopContinents": "Regioni con più visite", + "statistics.table.mainReports.title.TopContinents": "Più visualizzati per regione", // "statistics.table.mainReports.title.TopCategories": "Categories", "statistics.table.mainReports.title.TopCategories": "Categorie", // "statistics.table.mainReports.title.TopCities": "Top city views", - "statistics.table.mainReports.title.TopCities": "Città con più visite", + "statistics.table.mainReports.title.TopCities": "Più visualizzati per città", // "statistics.table.downloadReports.title.TotalVisits": "Most downloaded", "statistics.table.downloadReports.title.TotalVisits": "I più scaricati", @@ -8156,16 +8156,16 @@ "statistics.table.downloadReports.title.TotalDownloadsPerMonth": "Download totali al mese", // "statistics.table.downloadReports.title.TopCountries": "Top country downloads", - "statistics.table.downloadReports.title.TopCountries": "Top country downloads", + "statistics.table.downloadReports.title.TopCountries": "Più scaricati per nazione", // "statistics.table.downloadReports.title.TopContinents": "Top region downloads", - "statistics.table.downloadReports.title.TopContinents": "Top region downloads", + "statistics.table.downloadReports.title.TopContinents": "Più scaricati per regione", // "statistics.table.downloadReports.title.TopCategories": "Categories", "statistics.table.downloadReports.title.TopCategories": "Categorie", // "statistics.table.downloadReports.title.TopCities": "Top city downloads", - "statistics.table.downloadReports.title.TopCities": "Top city downloads", + "statistics.table.downloadReports.title.TopCities": "Più scaricati per città", // "statistics.table.mainReports.header.views": "Views", "statistics.table.mainReports.header.views": "Visualizzazioni", @@ -8326,24 +8326,19 @@ "statistics.table.itemReports.header.item" : "Item", // "statistics.table.mainReports.header.community" : "Community", - // TODO New key - Add a translation "statistics.table.mainReports.header.community" : "Community", // "statistics.table.mainReports.header.collection" : "Collection", - // TODO New key - Add a translation - "statistics.table.mainReports.header.collection" : "Collection", + "statistics.table.mainReports.header.collection" : "Collezione", // "statistics.table.itemReports.title.TopContinents" : "Top region views", - // TODO New key - Add a translation - "statistics.table.itemReports.title.TopContinents" : "Top region views", + "statistics.table.itemReports.title.TopContinents" : "Più visualizzati per regione", // "statistics.table.itemReports.title.TopCities" : "Top cities views", - // TODO New key - Add a translation - "statistics.table.itemReports.title.TopCities" : "Top cities views", + "statistics.table.itemReports.title.TopCities" : "Più visualizzati per città", // "statistics.table.itemReports.title.TopItems" : "Most viewed", - // TODO New key - Add a translation - "statistics.table.itemReports.title.TopItems" : "Most viewed", + "statistics.table.itemReports.title.TopItems" : "Più visualizzati", // "statistics.table.itemReports.title.TopCategories" : "Top categories", // TODO New key - Add a translation From 173c1a8f79b33d3f6175e591a26d0645d33d4563 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 3 Oct 2023 16:48:42 +0200 Subject: [PATCH 138/195] [DSC-1277] Italian translation fixes --- src/assets/i18n/it.json5 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 8f16730d429..3466ca341ea 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -1442,7 +1442,7 @@ "browse.metadata.rpname": "Nome", // "browse.metadata.subject": "Subject", - "browse.metadata.subject": "Oggetto", + "browse.metadata.subject": "Soggetto", // "browse.metadata.type": "Type", "browse.metadata.type": "Digitare", @@ -3258,7 +3258,7 @@ "explore.index.rpname" : "Nome", // "explore.index.subject" : "Subject", - "explore.index.subject" : "Oggetto", + "explore.index.subject" : "Soggetto", // "explore.index.title" : "Title", "explore.index.title" : "Titolo", @@ -7478,7 +7478,7 @@ "search.filters.applied.f.projectorgunits": "Organizzazione", // "search.filters.applied.f.subject": "Subject", - "search.filters.applied.f.subject": "Oggetto", + "search.filters.applied.f.subject": "Soggetto", // "search.filters.applied.f.submitter": "Submitter", "search.filters.applied.f.submitter": "Submitter", @@ -7604,10 +7604,10 @@ "search.filters.filter.creativeWorkEditor.label": "Editor di ricerca", // "search.filters.filter.creativeWorkKeywords.head": "Subject", - "search.filters.filter.creativeWorkKeywords.head": "Oggetto", + "search.filters.filter.creativeWorkKeywords.head": "Soggetto", // "search.filters.filter.creativeWorkKeywords.placeholder": "Subject", - "search.filters.filter.creativeWorkKeywords.placeholder": "Oggetto", + "search.filters.filter.creativeWorkKeywords.placeholder": "Soggetto", // "search.filters.filter.creativeWorkKeywords.label": "Search subject", "search.filters.filter.creativeWorkKeywords.label": "Oggetto della ricerca", @@ -7829,10 +7829,10 @@ "search.filters.filter.show-more": "Mostra altro", // "search.filters.filter.subject.head": "Subject", - "search.filters.filter.subject.head": "Oggetto", + "search.filters.filter.subject.head": "Soggetto", // "search.filters.filter.subject.placeholder": "Subject", - "search.filters.filter.subject.placeholder": "Oggetto", + "search.filters.filter.subject.placeholder": "Soggetto", // "search.filters.filter.subject.label": "Search subject", "search.filters.filter.subject.label": "Oggetto della ricerca", From 5cc7a44dbac0bfeb567dfea755732d59f95c917c Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 3 Oct 2023 17:40:38 +0200 Subject: [PATCH 139/195] [CST-10703] final workflow implemntation --- src/app/core/auth/models/auth.method-type.ts | 3 +- src/app/core/core.module.ts | 2 +- .../data/eperson-registration.service.spec.ts | 4 +- .../core/data/eperson-registration.service.ts | 17 ++- ...-email-confirmation-page.component.spec.ts | 20 ++- .../external-login-page-routing.module.ts | 4 + .../external-login-page.component.html | 6 +- .../external-login-page.component.spec.ts | 64 ++++++++- .../external-login-page.component.ts | 45 +++--- ...review-account-info-page-routing.module.ts | 4 + ...in-review-account-info-page.component.html | 4 +- ...review-account-info-page.component.spec.ts | 52 ++++++- ...ogin-review-account-info-page.component.ts | 45 +++--- .../helpers/review-account.guard.spec.ts | 79 +++++++++++ .../helpers/review-account.guard.ts | 73 ++++++++++ .../review-account-info.component.html | 2 +- .../review-account-info.component.ts | 75 ++-------- .../email-validated.component.html | 2 +- .../email-validated.component.spec.ts | 3 +- .../email-validated.component.ts | 11 +- ...al-login-validation-page-routing.module.ts | 5 +- ...ernal-login-validation-page.component.html | 6 +- ...al-login-validation-page.component.spec.ts | 62 ++++----- ...xternal-login-validation-page.component.ts | 98 +++---------- ...stration-data-create-user.resolver.spec.ts | 129 ++++++++++++++++++ .../registration-data-create-user.resolver.ts | 93 +++++++++++++ .../invitation-acceptance.component.spec.ts | 2 +- .../invitation-acceptance.component.ts | 2 +- src/app/invitation/valid-token.guard.spec.ts | 4 +- src/app/invitation/valid-token.guard.ts | 2 +- src/app/login-page/login-page.component.html | 1 + src/app/login-page/login-page.component.ts | 4 + .../registration.resolver.spec.ts | 2 +- .../registration.resolver.ts | 2 +- .../register-page/registration.guard.spec.ts | 6 +- src/app/register-page/registration.guard.ts | 2 +- .../confirm-email.component.html | 2 +- .../confirm-email.component.spec.ts | 6 +- .../confirm-email/confirm-email.component.ts | 92 ++++++++++++- .../provide-email.component.spec.ts | 20 ++- .../provide-email/provide-email.component.ts | 36 +++-- .../external-log-in.component.html | 10 +- .../external-log-in.component.spec.ts | 65 ++++++--- .../external-log-in.component.ts | 10 +- .../guards/registration-token.guard.spec.ts | 79 +++++++++++ .../guards/registration-token.guard.ts | 54 ++++++++ .../models/registration-data.mock.model.ts | 5 +- .../models/registration-data.model.ts | 14 +- .../registration-data.resolver.spec.ts | 16 +++ .../resolvers/registration-data.resolver.ts | 50 +++++++ .../services/external-login.service.ts | 7 +- src/app/shared/log-in/log-in.component.html | 9 +- src/app/shared/log-in/log-in.component.ts | 18 ++- src/assets/i18n/en.json5 | 10 ++ src/assets/i18n/it.json5 | 44 ++++++ 55 files changed, 1133 insertions(+), 349 deletions(-) create mode 100644 src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts create mode 100644 src/app/external-login-review-account-info/helpers/review-account.guard.ts create mode 100644 src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts create mode 100644 src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts create mode 100644 src/app/shared/external-log-in-complete/guards/registration-token.guard.spec.ts create mode 100644 src/app/shared/external-log-in-complete/guards/registration-token.guard.ts create mode 100644 src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts create mode 100644 src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts diff --git a/src/app/core/auth/models/auth.method-type.ts b/src/app/core/auth/models/auth.method-type.ts index 600c3c80761..e5b4e0a194c 100644 --- a/src/app/core/auth/models/auth.method-type.ts +++ b/src/app/core/auth/models/auth.method-type.ts @@ -5,5 +5,6 @@ export enum AuthMethodType { Ip = 'ip', X509 = 'x509', Oidc = 'oidc', - Orcid = 'orcid' + Orcid = 'orcid', + Validation = 'validation', } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index d22aadc8a55..c75ce8e65c9 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -472,7 +472,7 @@ export const models = LoginStatistics, Metric, ItemRequest, - RegistrationData + RegistrationData, ]; @NgModule({ diff --git a/src/app/core/data/eperson-registration.service.spec.ts b/src/app/core/data/eperson-registration.service.spec.ts index 74e45591125..5fcfbcbdcd7 100644 --- a/src/app/core/data/eperson-registration.service.spec.ts +++ b/src/app/core/data/eperson-registration.service.spec.ts @@ -104,7 +104,7 @@ describe('EpersonRegistrationService', () => { describe('searchByToken', () => { it('should return a registration corresponding to the provided token', () => { - const expected = service.searchByToken('test-token'); + const expected = service.searchByTokenAndUpdateData('test-token'); expect(expected).toBeObservable(cold('(a|)', { a: jasmine.objectContaining({ @@ -124,7 +124,7 @@ describe('EpersonRegistrationService', () => { testScheduler.run(({ cold, expectObservable }) => { rdbService.buildSingle.and.returnValue(cold('a', { a: rd })); - service.searchByToken('test-token'); + service.searchByTokenAndUpdateData('test-token'); expect(requestService.send).toHaveBeenCalledWith( jasmine.objectContaining({ diff --git a/src/app/core/data/eperson-registration.service.ts b/src/app/core/data/eperson-registration.service.ts index 3385c520dc5..259b5f66c50 100644 --- a/src/app/core/data/eperson-registration.service.ts +++ b/src/app/core/data/eperson-registration.service.ts @@ -89,10 +89,11 @@ export class EpersonRegistrationService{ } /** - * Search a registration based on the provided token - * @param token + * Searches for a registration based on the provided token. + * @param token The token to search for. + * @returns An observable of remote data containing the registration. */ - searchByToken(token: string): Observable> { + searchByTokenAndUpdateData(token: string): Observable> { const requestId = this.requestService.generateRequestId(); const href$ = this.getTokenSearchEndpoint(token).pipe( @@ -123,7 +124,13 @@ export class EpersonRegistrationService{ }) ); } - searchByTokenAndHandleError(token: string): Observable> { + + /** + * Searches for a registration by token and handles any errors that may occur. + * @param token The token to search for. + * @returns An observable of remote data containing the registration. + */ + searchRegistrationByToken(token: string): Observable> { const requestId = this.requestService.generateRequestId(); const href$ = this.getTokenSearchEndpoint(token).pipe( @@ -150,7 +157,7 @@ export class EpersonRegistrationService{ * @param updateValue Flag to indicate if the email should be updated or added * @returns Remote Data state of the patch request */ - patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operator: 'add' | 'replace') { + patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operator: 'add' | 'replace'): Observable> { const requestId = this.requestService.generateRequestId(); const href$ = this.getRegistrationEndpoint().pipe( diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts index cd646f25cf0..89a96222d13 100644 --- a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts @@ -1,6 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; +import { ConfirmationSentComponent } from '../shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; describe('ExternalLoginEmailConfirmationPageComponent', () => { let component: ExternalLoginEmailConfirmationPageComponent; @@ -8,7 +11,17 @@ describe('ExternalLoginEmailConfirmationPageComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ExternalLoginEmailConfirmationPageComponent ] + declarations: [ + ExternalLoginEmailConfirmationPageComponent, + ConfirmationSentComponent ], + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock, + }, + }), + ] }) .compileComponents(); }); @@ -22,4 +35,9 @@ describe('ExternalLoginEmailConfirmationPageComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should render ConfirmationSentComponent', () => { + const compiled = fixture.nativeElement; + expect(compiled.querySelector('ds-confirmation-sent')).toBeTruthy(); + }); }); diff --git a/src/app/external-login-page/external-login-page-routing.module.ts b/src/app/external-login-page/external-login-page-routing.module.ts index 7adbbb7b034..390db1eaf00 100644 --- a/src/app/external-login-page/external-login-page-routing.module.ts +++ b/src/app/external-login-page/external-login-page-routing.module.ts @@ -1,12 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; +import { RegistrationTokenGuard } from '../shared/external-log-in-complete/guards/registration-token.guard'; +import { RegistrationDataResolver } from '../shared/external-log-in-complete/resolvers/registration-data.resolver'; const routes: Routes = [ { path: '', pathMatch: 'full', component: ThemedExternalLoginPageComponent, + // canActivate: [RegistrationTokenGuard], // TODO: uncomment this line to enable the guard later + resolve: { registrationData: RegistrationDataResolver }, }, ]; diff --git a/src/app/external-login-page/external-login-page.component.html b/src/app/external-login-page/external-login-page.component.html index 82c9419930b..755896211de 100644 --- a/src/app/external-login-page/external-login-page.component.html +++ b/src/app/external-login-page/external-login-page.component.html @@ -1,11 +1,11 @@
- +
diff --git a/src/app/external-login-page/external-login-page.component.spec.ts b/src/app/external-login-page/external-login-page.component.spec.ts index 20f46ae833c..c705b131b3e 100644 --- a/src/app/external-login-page/external-login-page.component.spec.ts +++ b/src/app/external-login-page/external-login-page.component.spec.ts @@ -1,20 +1,53 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginPageComponent } from './external-login-page.component'; -import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; -import { Router } from '@angular/router'; -import { RouterMock } from '../shared/mocks/router.mock'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; +import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; +import { CommonModule } from '@angular/common'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; describe('ExternalLoginPageComponent', () => { let component: ExternalLoginPageComponent; let fixture: ComponentFixture; + const registrationDataMock = { + registrationType: 'orcid', + email: 'test@test.com', + netId: '0000-0000-0000-0000', + user: 'a44d8c9e-9b1f-4e7f-9b1a-5c9d8a0b1f1a', + registrationMetadata: { + 'email': [{ value: 'test@test.com' }], + 'eperson.lastname': [{ value: 'Doe' }], + 'eperson.firstname': [{ value: 'John' }], + }, + }; + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ ExternalLoginPageComponent ], providers: [ - { provide: EpersonRegistrationService, useValue: {} }, - { provide: Router, useValue: new RouterMock() }, + { + provide: ActivatedRoute, + useValue: { + snapshot: { + queryParams: { + token: '1234567890', + }, + }, + data: of(registrationDataMock), + }, + }, + ], + imports: [ + CommonModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock, + }, + }), ], }) .compileComponents(); @@ -29,4 +62,25 @@ describe('ExternalLoginPageComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should set the token from the query params', () => { + expect(component.token).toEqual('1234567890'); + }); + + it('should set the hasErrors flag if the token is not present', () => { + const activatedRoute = TestBed.inject(ActivatedRoute); + activatedRoute.snapshot.queryParams.token = undefined; + fixture.detectChanges(); + expect(component.hasErrors).toBeTrue(); + }); + + it('should display the DsExternalLogIn component when there are no errors', () => { + const registrationData = Object.assign(new RegistrationData(), registrationDataMock); + component.registrationData$ = of(registrationData); + component.token = '1234567890'; + component.hasErrors = false; + fixture.detectChanges(); + const dsExternalLogInComponent = fixture.nativeElement.querySelector('ds-external-log-in'); + expect(dsExternalLogInComponent).toBeTruthy(); + }); }); diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index b98e823fc90..c8bde7cd0da 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -1,13 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { hasValue } from '../shared/empty.util'; -import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; -import { Registration } from '../core/shared/registration.model'; +import { hasNoValue } from '../shared/empty.util'; import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; import { AlertType } from '../shared/alert/aletr-type'; -import { Observable, map, of } from 'rxjs'; -import { getRemoteDataPayload } from '../core/shared/operators'; +import { Observable, first, map, of, tap } from 'rxjs'; @Component({ templateUrl: './external-login-page.component.html', @@ -23,41 +20,35 @@ export class ExternalLoginPageComponent implements OnInit { /** * The registration data of the user. */ - public registrationData$: Observable = of( - mockRegistrationDataModel - ); + public registrationData$: Observable; /** * The type of alert to show. */ public AlertTypeEnum = AlertType; + /** + * Whether the component has errors. + */ + public hasErrors = false; constructor( - private epersonRegistrationService: EpersonRegistrationService, private arouter: ActivatedRoute ) { this.token = this.arouter.snapshot.queryParams.token; + this.hasErrors = hasNoValue(this.arouter.snapshot.queryParams.token); } ngOnInit(): void { - this.getRegistrationData(); + this.registrationData$ = this.arouter.data.pipe( + first(), + tap((data) => this.hasErrors = hasNoValue(data.registrationData)), + map((data) => data.registrationData)); + + // TODO: remove this line (temporary) + this.registrationData$ = of( + mockRegistrationDataModel + ); + this.hasErrors = false; this.token = '1234567890'; } - - /** - * Get the registration data of the user, - * based on the token. - */ - getRegistrationData() { - if (hasValue(this.token)) { - this.registrationData$ = this.epersonRegistrationService - .searchByToken(this.token) - .pipe( - getRemoteDataPayload(), - map((registration: Registration) => - Object.assign(new RegistrationData(), registration) - ) - ); - } - } } diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts index 94f2ef42664..ffec9928371 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts @@ -1,12 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; +import { RegistrationDataResolver } from '../shared/external-log-in-complete/resolvers/registration-data.resolver'; + const routes: Routes = [ { path: '', pathMatch: 'full', component: ExternalLoginReviewAccountInfoPageComponent, + // canActivate: [ReviewAccountGuard],// TODO: Remove comment + resolve: { registrationData: RegistrationDataResolver } },]; @NgModule({ diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html index 6d5defa2ca4..97034110392 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html @@ -1,12 +1,12 @@
- + diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts index ef2dfe82017..7622f081d0d 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts @@ -1,16 +1,33 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; +import { AlertType } from '../shared/alert/aletr-type'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; +import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; describe('ExternalLoginReviewAccountInfoPageComponent', () => { let component: ExternalLoginReviewAccountInfoPageComponent; let fixture: ComponentFixture; + const mockActivatedRoute = { + snapshot: { + queryParams: { + token: '1234567890' + } + }, + data: of({ + registrationData: mockRegistrationDataModel + }) + }; + beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ExternalLoginReviewAccountInfoPageComponent ] + declarations: [ExternalLoginReviewAccountInfoPageComponent], + providers: [ + { provide: ActivatedRoute, useValue: mockActivatedRoute } + ] }) - .compileComponents(); + .compileComponents(); }); beforeEach(() => { @@ -22,4 +39,33 @@ describe('ExternalLoginReviewAccountInfoPageComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should set the token from the query params', () => { + expect(component.token).toEqual('1234567890'); + }); + + it('should set hasErrors to false if registrationData is not empty', () => { + expect(component.hasErrors).toBeFalse(); + }); + + it('should set the registrationData$', () => { + component.registrationData$.subscribe((registrationData) => { + expect(registrationData.email).toEqual(mockRegistrationDataModel.email); + }); + }); + + it('should display review account info component when there are no errors', () => { + component.hasErrors = false; + component.registrationData$ = of(mockRegistrationDataModel); + fixture.detectChanges(); + const reviewAccountInfoComponent = fixture.nativeElement.querySelector('ds-review-account-info'); + expect(reviewAccountInfoComponent).toBeTruthy(); + }); + + it('should display error alert when there are errors', () => { + component.hasErrors = true; + fixture.detectChanges(); + const errorAlertComponent = fixture.nativeElement.querySelector('ds-alert'); + expect(errorAlertComponent).toBeTruthy(); + }); }); diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts index 8b02a347c51..5c984445031 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts @@ -1,12 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { AlertType } from '../shared/alert/aletr-type'; -import { Observable, map } from 'rxjs'; +import { Observable, first, map, of, tap } from 'rxjs'; import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; -import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; import { ActivatedRoute } from '@angular/router'; -import { hasValue } from '../shared/empty.util'; -import { getRemoteDataPayload } from '../core/shared/operators'; -import { Registration } from '../core/shared/registration.model'; +import { hasNoValue } from '../shared/empty.util'; +import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; @Component({ templateUrl: './external-login-review-account-info-page.component.html', @@ -27,12 +25,12 @@ export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { * The registration data of the user */ public registrationData$: Observable; - // = of( - // mockRegistrationDataModel - // ); + /** + * Whether the component has errors + */ + public hasErrors = false; constructor( - private epersonRegistrationService: EpersonRegistrationService, private arouter: ActivatedRoute ) { this.token = this.arouter.snapshot.queryParams.token; @@ -40,26 +38,15 @@ export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { ngOnInit(): void { - // -> if email address is not used by other user => Email Validated component - // -> if email address is used by other user => Review account information component - this.getRegistrationData(); + this.registrationData$ = this.arouter.data.pipe(first(), + tap((data) => this.hasErrors = hasNoValue(data?.registrationData)), + map((data) => data.registrationData)); + // TODO: remove this line (temporary) - // this.token = '1234567890'; + this.registrationData$ = of( + mockRegistrationDataModel + ); + this.hasErrors = false; + this.token = '1234567890'; } - /** - * Get the registration data from the token - */ - getRegistrationData() { - if (hasValue(this.token)) { - this.registrationData$ = this.epersonRegistrationService - .searchByToken(this.token) - .pipe( - getRemoteDataPayload(), - map((registration: Registration) => - Object.assign(new RegistrationData(), registration) - ) - ); - } - } - } diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts new file mode 100644 index 00000000000..61cf3f178cc --- /dev/null +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts @@ -0,0 +1,79 @@ +import { TestBed } from '@angular/core/testing'; +import { ReviewAccountGuard } from './review-account.guard'; +import { ActivatedRoute, convertToParamMap, Params, Router } from '@angular/router'; +import { of as observableOf } from 'rxjs'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { EpersonRegistrationService } from 'src/app/core/data/eperson-registration.service'; +import { EPerson } from 'src/app/core/eperson/models/eperson.model'; +import { Registration } from 'src/app/core/shared/registration.model'; +import { RouterMock } from 'src/app/shared/mocks/router.mock'; +import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils'; + +describe('ReviewAccountGuard', () => { + let guard: ReviewAccountGuard; + const route = new RouterMock(); + const registrationWithGroups = Object.assign(new Registration(), + { + email: 'test@email.org', + token: 'test-token', + }); + const epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { + searchRegistrationByToken: createSuccessfulRemoteDataObject$(registrationWithGroups) + }); + const authService = { + getAuthenticatedUserFromStore: () => observableOf(ePerson), + setRedirectUrl: () => { + return true; + } + } as any; + const ePerson = Object.assign(new EPerson(), { + id: 'test-eperson', + uuid: 'test-eperson' + }); + beforeEach(() => { + const paramObject: Params = {}; + paramObject.token = '1234'; + TestBed.configureTestingModule({ + providers: [{provide: Router, useValue: route}, + { + provide: ActivatedRoute, + useValue: { + queryParamMap: observableOf(convertToParamMap(paramObject)) + }, + }, + {provide: EpersonRegistrationService, useValue: epersonRegistrationService}, + {provide: AuthService, useValue: authService} + ] + }); + guard = TestBed.get(ReviewAccountGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); + describe('based on the response of "searchByToken have', () => { + it('can activate must return true when registration data includes groups', () => { + (guard.canActivate({ params: { token: '123456789' } } as any, {} as any) as any) + .subscribe( + (canActivate) => { + expect(canActivate).toEqual(true); + } + ); + }); + it('can activate must return false when registration data includes groups', () => { + const registrationWithDifferentUsedFromLoggedInt = Object.assign(new Registration(), + { + email: 't1@email.org', + token: 'test-token', + }); + epersonRegistrationService.searchRegistrationByToken.and.returnValue(observableOf(registrationWithDifferentUsedFromLoggedInt)); + (guard.canActivate({ params: { token: '123456789' } } as any, {} as any) as any) + .subscribe( + (canActivate) => { + expect(canActivate).toEqual(false); + } + ); + }); + + }); +}); diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.ts new file mode 100644 index 00000000000..c979e3bfc2c --- /dev/null +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.ts @@ -0,0 +1,73 @@ +import { Injectable } from '@angular/core'; +import { + ActivatedRouteSnapshot, + CanActivate, + Router, + RouterStateSnapshot, +} from '@angular/router'; +import isEqual from 'lodash/isEqual'; +import { Observable, catchError, mergeMap, of, tap } from 'rxjs'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; +import { EpersonRegistrationService } from 'src/app/core/data/eperson-registration.service'; +import { RemoteData } from 'src/app/core/data/remote-data'; +import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; +import { Registration } from 'src/app/core/shared/registration.model'; +import { hasValue } from 'src/app/shared/empty.util'; +import { RegistrationData } from 'src/app/shared/external-log-in-complete/models/registration-data.model'; + +@Injectable({ + providedIn: 'root', +}) +export class ReviewAccountGuard implements CanActivate { + constructor( + private router: Router, + private epersonRegistrationService: EpersonRegistrationService, + private authService: AuthService + ) { } + + /** + * Determines if a user can activate a route based on the registration token. + * @param route - The activated route snapshot. + * @param state - The router state snapshot. + * @returns A value indicating if the user can activate the route. + */ + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Promise | boolean | Observable { + if (route.params.token) { + return this.epersonRegistrationService + .searchRegistrationByToken(route.params.token) + .pipe( + getFirstCompletedRemoteData(), + mergeMap( + (data: RemoteData) => { + if (data.hasSucceeded && hasValue(data)) { + const registrationData = Object.assign(new RegistrationData(), data.payload); + // is the registration type validation (account valid) + if (isEqual(registrationData.registrationType, AuthMethodType.Validation)) { + return of(true); + } else { + return this.authService.isAuthenticated(); + } + } + return of(false); + } + ), + tap((isValid: boolean) => { + if (!isValid) { + this.router.navigate(['/404']); + } + }), + catchError(() => { + this.router.navigate(['/404']); + return of(false); + }) + ); + } else { + this.router.navigate(['/404']); + return of(false); + } + } +} diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html index 82aec84441e..039bc763b28 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html @@ -41,7 +41,7 @@

{{'external-login-validation.review-account-info.header' | translate}}

{ - if (eperson) { - this.epersonCurrentData = eperson; - this.dataToCompare = this.prepareDataToCompare(); - } - }); - } + this.dataToCompare = this.prepareDataToCompare(); } /** @@ -157,7 +117,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { filter((data: ReviewAccountInfoData) => data.overrideValue), switchMap((data: ReviewAccountInfoData) => { return this.ePersonService.mergeEPersonDataWithToken( - this.epersonCurrentData.id, + this.registrationData.user, this.registrationToken, data.identifier ); @@ -165,15 +125,14 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); } else { override$ = this.ePersonService.mergeEPersonDataWithToken( - this.epersonCurrentData.id, + this.registrationData.user, this.registrationToken ); } this.subs.push( override$.subscribe((response: RemoteData) => { - // TODO: https://4science.atlassian.net/browse/CST-11609?focusedCommentId=206748 - // redirect to profile page if (response.hasSucceeded) { + // TODO: remove this line (temporary) console.log('mergeEPersonDataWithToken', response.payload); this.notificationService.success( this.translateService.get( @@ -197,7 +156,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { /** * Prepare the data to compare and display: * -> For each metadata from the registration token, get the current value from the eperson. - * -> Label is the metadata key without the prefix e.g `eperson.` + * -> Label is the metadata key without the prefix e.g `eperson.` but only `email` * -> Identifier is the metadata key with the prefix e.g `eperson.lastname` * -> Override value is false by default * @returns List of data to compare @@ -209,7 +168,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { console.log(key, value); dataToCompare.push({ label: key.split('.')?.[1] ?? key.split('.')?.[0], - currentValue: this.getCurrentValue(key), + currentValue: value[0]?.overrides ?? '', receivedValue: value[0].value, overrideValue: false, identifier: key, @@ -220,18 +179,6 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { return dataToCompare; } - /** - * Return the current value of the metadata key from the eperson - * @param metadata metadata key - * @returns the current value of the metadata key from the eperson - */ - private getCurrentValue(metadata: string): string { - if (metadata === 'email') { - return this.epersonCurrentData.email; - } - return this.epersonCurrentData.firstMetadataValue(metadata) ?? ''; - } - ngOnDestroy(): void { this.subs.filter((s) => hasValue(s)).forEach((sub) => sub.unsubscribe()); } diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.html b/src/app/external-login-validation-page/email-validated/email-validated.component.html index 133243853bf..b58ed085890 100644 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.html +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.html @@ -5,5 +5,5 @@

- +
diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts index fee341ad22c..21c7f5d7d8f 100644 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts @@ -9,6 +9,7 @@ import { Router } from '@angular/router'; import { RouterStub } from 'src/app/shared/testing/router.stub'; import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { of } from 'rxjs'; +import { AuthServiceMock } from 'src/app/shared/mocks/auth.service.mock'; describe('EmailValidatedComponent', () => { let component: EmailValidatedComponent; @@ -27,7 +28,7 @@ describe('EmailValidatedComponent', () => { await TestBed.configureTestingModule({ declarations: [ EmailValidatedComponent ], providers: [ - { provide: AuthService, useValue: {}}, + { provide: AuthService, useValue: new AuthServiceMock()}, { provide: Router, useValue: new RouterStub() }, { provide: TranslateService, useValue: translateServiceStub }, ], diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.ts index 4147dd23720..8fd45bc4a7f 100644 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.ts +++ b/src/app/external-login-validation-page/email-validated/email-validated.component.ts @@ -1,5 +1,4 @@ -import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component, ChangeDetectionStrategy } from '@angular/core'; import { AuthService } from '../../core/auth/auth.service'; @Component({ selector: 'ds-email-validated', @@ -9,11 +8,9 @@ import { AuthService } from '../../core/auth/auth.service'; }) export class EmailValidatedComponent { - // TODO: (temporary) - // evaluate if this is needed - @Input() registrationToken: string; - - constructor(private authService: AuthService, private router: Router) { + constructor(private authService: AuthService) { + // After the user has validated his email, we need to redirect him to the review account page, + // in order to review his account information this.authService.setRedirectUrl('/review-account'); } } diff --git a/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts b/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts index d206eba0fe8..241cebebb2f 100644 --- a/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts +++ b/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts @@ -1,12 +1,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ThemedExternalLoginValidationPageComponent } from './themed-external-login-validation-page.component'; - +import { RegistrationTokenGuard } from '../shared/external-log-in-complete/guards/registration-token.guard'; +import { RegistrationDataCreateUserResolver } from './resolvers/registration-data-create-user.resolver'; const routes: Routes = [ { path: '', pathMatch: 'full', component: ThemedExternalLoginValidationPageComponent, + // canActivate: [RegistrationTokenGuard] // TODO: uncomment this line to enable the guard later + resolve: { createUser: RegistrationDataCreateUserResolver }, }, ]; diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.html b/src/app/external-login-validation-page/external-login-validation-page.component.html index 3efd1ce0e38..cfdaed0d6db 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.html +++ b/src/app/external-login-validation-page/external-login-validation-page.component.html @@ -1,9 +1,9 @@
- - + + diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts b/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts index 155d64fe082..975fca9f81f 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts +++ b/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts @@ -1,7 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; -import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { Observable, of } from 'rxjs'; import { RemoteData } from '../core/data/remote-data'; @@ -10,11 +9,12 @@ import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; import { ActivatedRoute } from '@angular/router'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; import { AlertType } from '../shared/alert/aletr-type'; +import { tr } from 'date-fns/locale'; describe('ExternalLoginValidationPageComponent', () => { let component: ExternalLoginValidationPageComponent; + let componentAsAny: any; let fixture: ComponentFixture; let epersonRegistrationService: any; @@ -37,19 +37,21 @@ describe('ExternalLoginValidationPageComponent', () => { queryParams: { token: tokenMock } - } + }, + data: of({ + createUser: true, + }), }; beforeEach(async () => { epersonRegistrationService = { - searchByToken: (token: string): Observable> => { return createSuccessfulRemoteDataObject$(registrationDataMock); } + searchByTokenAndUpdateData: (token: string): Observable> => { return createSuccessfulRemoteDataObject$(registrationDataMock); } }; await TestBed.configureTestingModule({ declarations: [ExternalLoginValidationPageComponent], providers: [ { provide: ActivatedRoute, useValue: routeStub }, - { provide: EpersonRegistrationService, useValue: epersonRegistrationService }, ], imports: [ CommonModule, @@ -68,6 +70,7 @@ describe('ExternalLoginValidationPageComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ExternalLoginValidationPageComponent); component = fixture.componentInstance; + componentAsAny = component; fixture.detectChanges(); }); @@ -75,49 +78,32 @@ describe('ExternalLoginValidationPageComponent', () => { expect(component).toBeTruthy(); }); - it('should set the token from the query parameters', () => { - expect(component.token).toEqual(tokenMock); - }); - - it('should initialize the registration data', () => { - spyOn(epersonRegistrationService, 'searchByToken').and.callThrough(); + it('should set validationFailed to true if createUser is falsy', () => { + const activatedRoute = TestBed.inject(ActivatedRoute); + activatedRoute.data = of({ createUser: false }); component.ngOnInit(); - expect(epersonRegistrationService.searchByToken).toHaveBeenCalledWith(tokenMock); - expect(component.registrationData$).toBeTruthy(); + expect(componentAsAny.validationFailed.value).toBeTrue(); }); - it('should render ds-email-validated component when registrationData$ does not have an email', () => { - component.registrationData$ = of(Object.assign(new RegistrationData(), { registrationDataMock, email: null })); + it('should set validationFailed to false if createUser is truthy', () => { + const activatedRoute = TestBed.inject(ActivatedRoute); + activatedRoute.data = of({ createUser: true }); component.ngOnInit(); - fixture.detectChanges(); - - const emailValidatedComponent = fixture.nativeElement.querySelector('ds-email-validated'); - const reviewAccountInfoComponent = fixture.nativeElement.querySelector('ds-review-account-info'); - - expect(emailValidatedComponent).toBeTruthy(); - expect(reviewAccountInfoComponent).toBeNull(); + expect(componentAsAny.validationFailed.value).toBeFalse(); }); - it('should render ds-review-account-info component when registrationData$ has an email', () => { - component.registrationData$ = of(Object.assign(new RegistrationData(), { registrationDataMock, email: 'hey@hello.com' })); - // component.ngOnInit(); + it('should show DsEmailValidatedComponent when hasFailed() returns false', () => { + spyOn(component, 'hasFailed').and.returnValue(of(false)); fixture.detectChanges(); - const emailValidatedComponent = fixture.nativeElement.querySelector('ds-email-validated'); - const reviewAccountInfoComponent = fixture.nativeElement.querySelector('ds-review-account-info'); - - expect(emailValidatedComponent).toBeNull(); - expect(reviewAccountInfoComponent).toBeTruthy(); + const dsEmailValidated = fixture.nativeElement.querySelector('ds-email-validated'); + expect(dsEmailValidated).toBeTruthy(); }); - it('should render ds-alert component when token is missing', () => { - component.token = null; - component.ngOnInit(); + it('should show DsAlertComponent when hasFailed() returns true', () => { + spyOn(component, 'hasFailed').and.returnValue(of(true)); fixture.detectChanges(); - - const alertComponent = fixture.nativeElement.querySelector('ds-alert'); - - expect(alertComponent).toBeTruthy(); - expect(component.AlertTypeEnum).toEqual(AlertType); + const dsAlert = fixture.nativeElement.querySelector('ds-alert'); + expect(dsAlert).toBeTruthy(); }); afterEach(() => { diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.ts b/src/app/external-login-validation-page/external-login-validation-page.component.ts index 7a20473ddbe..abcd256f8a7 100644 --- a/src/app/external-login-validation-page/external-login-validation-page.component.ts +++ b/src/app/external-login-validation-page/external-login-validation-page.component.ts @@ -1,110 +1,44 @@ -import { Component } from '@angular/core'; -import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { AlertType } from '../shared/alert/aletr-type'; -import { hasNoValue, hasValue } from '../shared/empty.util'; -import { getRemoteDataPayload } from '../core/shared/operators'; -import { BehaviorSubject, Observable, map, of, switchMap, tap } from 'rxjs'; -import { Registration } from '../core/shared/registration.model'; -import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; -import { RemoteData } from '../core/data/remote-data'; -import { NotificationsService } from '../shared/notifications/notifications.service'; -import { TranslateService } from '@ngx-translate/core'; -import { EPersonDataService } from '../core/eperson/eperson-data.service'; -import { MetadataValue } from '../core/shared/metadata.models'; -import { EPerson } from '../core/eperson/models/eperson.model'; -import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; +import { BehaviorSubject, Observable } from 'rxjs'; @Component({ templateUrl: './external-login-validation-page.component.html', styleUrls: ['./external-login-validation-page.component.scss'], }) -export class ExternalLoginValidationPageComponent { - /** - * The token used to get the registration data - */ - public token: string; - +export class ExternalLoginValidationPageComponent implements OnInit { /** * The type of alert to show */ public AlertTypeEnum = AlertType; + /** + * Whether the component has errors + */ private validationFailed: BehaviorSubject = new BehaviorSubject(false); constructor( - private epersonRegistrationService: EpersonRegistrationService, private arouter: ActivatedRoute, - private epersonDataService: EPersonDataService, - private notificationService: NotificationsService, - private translateService: TranslateService ) { - this.token = this.arouter.snapshot.queryParams.token; - this.token = '1234567890'; // TODO: remove this line (temporary) } ngOnInit(): void { - // TODO: Uncomment this line later - // this.getRegistrationData(); - } + this.arouter.data.subscribe((data) => { + const resolvedData = data.createUser; + this.validationFailed.next(!resolvedData); + }); - public hasFailed(): Observable { - return this.validationFailed.asObservable(); + // TODO: remove this line (temporary) + this.validationFailed.next(false); } + /** - * Get the registration data from the token + * Check if the validation has failed */ - getRegistrationData() { - this.validationFailed.next(true); - - if (hasValue(this.token)) { - this.fetchRegistrationDataAndCreateUser(this.token); - } - } - - fetchRegistrationDataAndCreateUser(token: string) { - this.epersonRegistrationService - .searchByToken(token) - .pipe( - switchMap((rd) => { - if (hasValue(rd.payload) && hasNoValue(rd.payload.user)) { - const registrationData = Object.assign( - new RegistrationData(), - rd.payload - ); - return this.createUserFromToken(token, registrationData); - } else { - return of(rd); - } - }) - ) - .subscribe((rd: RemoteData) => { - if (rd.hasFailed) { - this.validationFailed.next(true); - } - }); - } - - createUserFromToken(token: string, registrationData: RegistrationData) { - const metadataValues = Object.entries(registrationData.registrationMetadata) - .filter(([key, value]) => hasValue(value[0]?.value)) - .map(([key, value]) => ({ - key, - value: value[0]?.value, - })); - const eperson = Object.assign(new EPerson(), { - metadata: metadataValues, - canLogIn: true, - requireCertificate: false, - }); - return this.epersonDataService.createEPersonForToken(eperson, token).pipe( - tap((rd: RemoteData) => { - if (rd.hasFailed) { - this.validationFailed.next(true); - } - }) - ); + public hasFailed(): Observable { + return this.validationFailed.asObservable(); } } diff --git a/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts b/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts new file mode 100644 index 00000000000..979bdc89491 --- /dev/null +++ b/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts @@ -0,0 +1,129 @@ +import { TestBed } from '@angular/core/testing'; + +import { RegistrationDataCreateUserResolver } from './registration-data-create-user.resolver'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { EPersonDataService } from '../../core/eperson/eperson-data.service'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RouterStateSnapshot } from '@angular/router'; +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; +import { Registration } from '../../core/shared/registration.model'; +import { MetadataValue } from 'src/app/core/shared/metadata.models'; + +describe('RegistrationDataCreateUserResolver', () => { + let resolver: RegistrationDataCreateUserResolver; + let epersonRegistrationService: jasmine.SpyObj; + let epersonDataService: jasmine.SpyObj; + + const registrationDataMock = { + registrationType: 'orcid', + email: 'test@test.com', + netId: '0000-0000-0000-0000', + user: null, + registrationMetadata: { + 'email': [{ value: 'test@test.com' }], + 'eperson.lastname': [{ value: 'Doe' }], + 'eperson.firstname': [{ value: 'John' }], + }, + }; + + const tokenMock = 'as552-5a5a5-5a5a5-5a5a5'; + + + beforeEach(() => { + const spyEpersonRegistrationService = jasmine.createSpyObj('EpersonRegistrationService', [ + 'searchByTokenAndUpdateData' + ]); + const spyEpersonDataService = jasmine.createSpyObj('EPersonDataService', [ + 'createEPersonForToken' + ]); + + TestBed.configureTestingModule({ + providers: [ + RegistrationDataCreateUserResolver, + { provide: EpersonRegistrationService, useValue: spyEpersonRegistrationService }, + { provide: EPersonDataService, useValue: spyEpersonDataService } + ] + }); + resolver = TestBed.inject(RegistrationDataCreateUserResolver); + epersonRegistrationService = TestBed.inject(EpersonRegistrationService) as jasmine.SpyObj; + epersonDataService = TestBed.inject(EPersonDataService) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); + + describe('resolve', () => { + const token = tokenMock; + const route = { params: { token: token } } as any; + const state = {} as RouterStateSnapshot; + + it('should create a new user and return true when registration data does not contain a user', (done) => { + const registration = new Registration(); + epersonRegistrationService.searchByTokenAndUpdateData.and.returnValue(createSuccessfulRemoteDataObject$(registration)); + + const createdEPerson = new EPerson(); + createdEPerson.email = registrationDataMock.email; + createdEPerson.metadata = { + 'eperson.lastname': [Object.assign(new MetadataValue(), { value: 'Doe' })], + 'eperson.firstname': [Object.assign(new MetadataValue(), { value: 'John' })], + }; + createdEPerson.canLogIn = true; + createdEPerson.requireCertificate = false; + epersonDataService.createEPersonForToken.and.returnValue(createSuccessfulRemoteDataObject$(createdEPerson)); + + resolver.resolve(route, state).subscribe((result) => { + expect(result).toBeTrue(); + expect(epersonRegistrationService.searchByTokenAndUpdateData).toHaveBeenCalledWith(token); + expect(epersonDataService.createEPersonForToken).toHaveBeenCalledWith(createdEPerson, token); + done(); + }); + }); + + it('should return false when search by token and update data fails', (done) => { + epersonRegistrationService.searchByTokenAndUpdateData.and.returnValue(createFailedRemoteDataObject$()); + + resolver.resolve(route, state).subscribe((result) => { + expect(result).toBeFalse(); + expect(epersonRegistrationService.searchByTokenAndUpdateData).toHaveBeenCalledWith(token); + expect(epersonDataService.createEPersonForToken).not.toHaveBeenCalled(); + done(); + }); + }); + + }); + + describe('createUserFromToken', () => { + const token = tokenMock; + const registrationData: RegistrationData = Object.assign(new RegistrationData(), registrationDataMock); + + it('should create a new user and return true', (done) => { + const createdEPerson = new EPerson(); + createdEPerson.email = registrationData.email; + createdEPerson.metadata = { + 'eperson.lastname': 'Doe', //[{ value: 'Doe' }], + 'eperson.firstname': 'John',// [{ value: 'John' }], + } as any; + createdEPerson.canLogIn = true; + createdEPerson.requireCertificate = false; + epersonDataService.createEPersonForToken.and.returnValue(createSuccessfulRemoteDataObject$(createdEPerson)); + + resolver.createUserFromToken(token, registrationData).subscribe((result) => { + expect(result).toBeTrue(); + expect(epersonDataService.createEPersonForToken).toHaveBeenCalledWith(createdEPerson, token); + done(); + }); + }); + + it('should return false when create EPerson for token fails', (done) => { + epersonDataService.createEPersonForToken.and.returnValue(createFailedRemoteDataObject$()); + + resolver.createUserFromToken(token, registrationData).subscribe((result) => { + expect(result).toBeFalse(); + expect(epersonDataService.createEPersonForToken).toHaveBeenCalledWith(jasmine.any(EPerson), token); + done(); + }); + }); + }); +}); diff --git a/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts b/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts new file mode 100644 index 00000000000..000422f7623 --- /dev/null +++ b/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts @@ -0,0 +1,93 @@ +import { Injectable } from '@angular/core'; +import { + Resolve, + RouterStateSnapshot, + ActivatedRouteSnapshot, +} from '@angular/router'; +import { Observable, map, of, switchMap } from 'rxjs'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RemoteData } from '../../core/data/remote-data'; +import { EPersonDataService } from '../../core/eperson/eperson-data.service'; +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { hasValue, hasNoValue } from '../../shared/empty.util'; +import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; + +@Injectable({ + providedIn: 'root', +}) +export class RegistrationDataCreateUserResolver implements Resolve { + constructor( + private epersonRegistrationService: EpersonRegistrationService, + private epersonDataService: EPersonDataService + ) {} + + /** + * Resolves the registration data and creates a new user account from the given token. + * The account will be created only when the registration data does NOT contain a user (uuid). + * @param route The activated route snapshot. + * @param state The router state snapshot. + * @returns An observable of a boolean indicating whether the user was created successfully or not. + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable { + const token = route.params.token; + return this.epersonRegistrationService + .searchByTokenAndUpdateData(token) + .pipe( + getFirstCompletedRemoteData(), + switchMap((rd) => { + if ( + rd.hasSucceeded && + hasValue(rd.payload) && + hasNoValue(rd.payload.user) + ) { + const registrationData = Object.assign( + new RegistrationData(), + rd.payload + ); + return this.createUserFromToken(token, registrationData); + } + if (rd.hasFailed) { + return of(false); + } + }) + ); + } + + /** + * Creates a new user from a given token and registration data. + * Based on the registration data, the user will be created with the following properties: + * - email: the email address from the registration data + * - metadata: all metadata values from the registration data, except for the email metadata key (ePerson object does not have an email metadata field) + * - canLogIn: true + * - requireCertificate: false + * @param token The token used to create the user. + * @param registrationData The registration data used to create the user. + * @returns An Observable that emits a boolean indicating whether the user creation was successful. + */ + createUserFromToken( + token: string, + registrationData: RegistrationData + ): Observable { + const metadataValues = {}; + for (const [key, value] of Object.entries(registrationData.registrationMetadata)) { + if (hasValue(value[0]?.value) && key !== 'email') { + metadataValues[key] = value[0]?.value; + } + } + const eperson = new EPerson(); + eperson.email = registrationData.email; + eperson.metadata = metadataValues; + eperson.canLogIn = true; + eperson.requireCertificate = false; + return this.epersonDataService.createEPersonForToken(eperson, token).pipe( + getFirstCompletedRemoteData(), + map((rd: RemoteData) => { + return rd.hasSucceeded; + }) + ); + } +} diff --git a/src/app/invitation/invitation-acceptance/invitation-acceptance.component.spec.ts b/src/app/invitation/invitation-acceptance/invitation-acceptance.component.spec.ts index 31d23e2b073..9d9dcf79c3c 100644 --- a/src/app/invitation/invitation-acceptance/invitation-acceptance.component.spec.ts +++ b/src/app/invitation/invitation-acceptance/invitation-acceptance.component.spec.ts @@ -27,7 +27,7 @@ describe('InvitationAcceptanceComponent', () => { groupNames: ['group1', 'group2'] }); const epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { - searchByToken: createSuccessfulRemoteDataObject$(registrationWithGroups) + searchByTokenAndUpdateData: createSuccessfulRemoteDataObject$(registrationWithGroups) }); const ePersonDataServiceStub = { acceptInvitationToJoinGroups(person: EPerson): Observable> { diff --git a/src/app/invitation/invitation-acceptance/invitation-acceptance.component.ts b/src/app/invitation/invitation-acceptance/invitation-acceptance.component.ts index a019eb855a6..9ba247975ee 100644 --- a/src/app/invitation/invitation-acceptance/invitation-acceptance.component.ts +++ b/src/app/invitation/invitation-acceptance/invitation-acceptance.component.ts @@ -28,7 +28,7 @@ export class InvitationAcceptanceComponent implements OnInit { this.route.paramMap.pipe( switchMap((paramMap: ParamMap) => { const token = paramMap.get('registrationToken'); - return this.epersonRegistrationService.searchByToken(token); + return this.epersonRegistrationService.searchByTokenAndUpdateData(token); }), getFirstCompletedRemoteData(), getRemoteDataPayload() diff --git a/src/app/invitation/valid-token.guard.spec.ts b/src/app/invitation/valid-token.guard.spec.ts index 964942d081f..981f4506e7c 100644 --- a/src/app/invitation/valid-token.guard.spec.ts +++ b/src/app/invitation/valid-token.guard.spec.ts @@ -20,7 +20,7 @@ describe('DirectAccessGuard', () => { groupNames: ['group1', 'group2'] }); const epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { - searchByTokenAndHandleError: createSuccessfulRemoteDataObject$(registrationWithGroups) + searchRegistrationByToken: createSuccessfulRemoteDataObject$(registrationWithGroups) }); const authService = { getAuthenticatedUserFromStore: () => observableOf(ePerson), @@ -70,7 +70,7 @@ describe('DirectAccessGuard', () => { groups: [], groupNames: [] }); - epersonRegistrationService.searchByTokenAndHandleError.and.returnValue(observableOf(registrationWithDifferentUsedFromLoggedInt)); + epersonRegistrationService.searchRegistrationByToken.and.returnValue(observableOf(registrationWithDifferentUsedFromLoggedInt)); (guard.canActivate({ params: { registrationToken: '123456789' } } as any, {} as any) as any) .subscribe( (canActivate) => { diff --git a/src/app/invitation/valid-token.guard.ts b/src/app/invitation/valid-token.guard.ts index 9c26f3d85a8..0a7fe6f5987 100644 --- a/src/app/invitation/valid-token.guard.ts +++ b/src/app/invitation/valid-token.guard.ts @@ -22,7 +22,7 @@ export class ValidTokenGuard implements CanActivate { // get the url if (route.params.registrationToken) { // search by token found - return this.epersonRegistrationService.searchByTokenAndHandleError(route.params.registrationToken).pipe( + return this.epersonRegistrationService.searchRegistrationByToken(route.params.registrationToken).pipe( getFirstCompletedRemoteData(), map((data: RemoteData) => data.hasSucceeded && hasValue('groupNames') && data.payload.groupNames.length > 0), tap((isValid: boolean) => { diff --git a/src/app/login-page/login-page.component.html b/src/app/login-page/login-page.component.html index 2a95e0ce1c5..067da65739a 100644 --- a/src/app/login-page/login-page.component.html +++ b/src/app/login-page/login-page.component.html @@ -4,6 +4,7 @@

{{"login.form.header" | translate}}

diff --git a/src/app/login-page/login-page.component.ts b/src/app/login-page/login-page.component.ts index d9ecf3e8e6b..62a72ab7907 100644 --- a/src/app/login-page/login-page.component.ts +++ b/src/app/login-page/login-page.component.ts @@ -15,6 +15,7 @@ import { import { hasValue, isNotEmpty } from '../shared/empty.util'; import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model'; import { isAuthenticated } from '../core/auth/selectors'; +import { AuthMethodType } from '../core/auth/models/auth.method-type'; /** * This component represents the login page @@ -33,6 +34,8 @@ export class LoginPageComponent implements OnDestroy, OnInit { */ sub: Subscription; + externalLoginMethod: AuthMethodType; + /** * Initialize instance variables * @@ -48,6 +51,7 @@ export class LoginPageComponent implements OnDestroy, OnInit { ngOnInit() { const queryParamsObs = this.route.queryParams; const authenticated = this.store.select(isAuthenticated); + this.externalLoginMethod = this.route.snapshot.queryParams.authMethod; this.sub = observableCombineLatest(queryParamsObs, authenticated).pipe( filter(([params, auth]) => isNotEmpty(params.token) || isNotEmpty(params.expired)), take(1) diff --git a/src/app/register-email-form/registration.resolver.spec.ts b/src/app/register-email-form/registration.resolver.spec.ts index 86b200018d1..066740c126e 100644 --- a/src/app/register-email-form/registration.resolver.spec.ts +++ b/src/app/register-email-form/registration.resolver.spec.ts @@ -13,7 +13,7 @@ describe('RegistrationResolver', () => { beforeEach(() => { epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { - searchByToken: createSuccessfulRemoteDataObject$(registration) + searchByTokenAndUpdateData: createSuccessfulRemoteDataObject$(registration) }); resolver = new RegistrationResolver(epersonRegistrationService); }); diff --git a/src/app/register-email-form/registration.resolver.ts b/src/app/register-email-form/registration.resolver.ts index 498d029492b..0d7c3fed13a 100644 --- a/src/app/register-email-form/registration.resolver.ts +++ b/src/app/register-email-form/registration.resolver.ts @@ -17,7 +17,7 @@ export class RegistrationResolver implements Resolve> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { const token = route.params.token; - return this.epersonRegistrationService.searchByToken(token).pipe( + return this.epersonRegistrationService.searchByTokenAndUpdateData(token).pipe( getFirstCompletedRemoteData(), ); } diff --git a/src/app/register-page/registration.guard.spec.ts b/src/app/register-page/registration.guard.spec.ts index 9fb8dd3a338..09a6f2432e1 100644 --- a/src/app/register-page/registration.guard.spec.ts +++ b/src/app/register-page/registration.guard.spec.ts @@ -48,7 +48,7 @@ describe('RegistrationGuard', () => { }); epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { - searchByToken: observableOf(registrationRD), + searchByTokenAndUpdateData: observableOf(registrationRD), }); router = jasmine.createSpyObj('router', { navigateByUrl: Promise.resolve(), @@ -66,7 +66,7 @@ describe('RegistrationGuard', () => { describe('canActivate', () => { describe('when searchByToken returns a successful response', () => { beforeEach(() => { - (epersonRegistrationService.searchByToken as jasmine.Spy).and.returnValue(observableOf(registrationRD)); + (epersonRegistrationService.searchByTokenAndUpdateData as jasmine.Spy).and.returnValue(observableOf(registrationRD)); }); it('should return true', (done) => { @@ -93,7 +93,7 @@ describe('RegistrationGuard', () => { describe('when searchByToken returns a 404 response', () => { beforeEach(() => { - (epersonRegistrationService.searchByToken as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Not Found', 404)); + (epersonRegistrationService.searchByTokenAndUpdateData as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Not Found', 404)); }); it('should redirect', () => { diff --git a/src/app/register-page/registration.guard.ts b/src/app/register-page/registration.guard.ts index a0f07d2fbe3..9f156df4737 100644 --- a/src/app/register-page/registration.guard.ts +++ b/src/app/register-page/registration.guard.ts @@ -30,7 +30,7 @@ export class RegistrationGuard implements CanActivate { */ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { const token = route.params.token; - return this.epersonRegistrationService.searchByToken(token).pipe( + return this.epersonRegistrationService.searchByTokenAndUpdateData(token).pipe( getFirstCompletedRemoteData(), redirectOn4xx(this.router, this.authService), map((rd) => { diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html index 2ff2000d41a..455aaf75e73 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html @@ -8,7 +8,7 @@

type="email" id="email" formControlName="email" - placeholder="orcid.profile.email@example.com" + placeholder="profile.email@example.com" class="form-control form-control-lg position-relative" />
{ let component: ConfirmEmailComponent; @@ -18,6 +19,7 @@ describe('ConfirmEmailComponent', () => { providers: [ FormBuilder, { provide: EpersonRegistrationService, useValue: {} }, + { provide: ExternalLoginService, useValue: {} }, ], imports: [ CommonModule, @@ -28,7 +30,7 @@ describe('ConfirmEmailComponent', () => { } }), ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 244521d2a65..d4e470f9e72 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -1,7 +1,18 @@ -import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; -import { getRemoteDataPayload } from '../../../../core/shared/operators'; +import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../../core/shared/operators'; +import { RegistrationData } from '../../models/registration-data.model'; +import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; +import { hasValue } from '../../../../shared/empty.util'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import isEqual from 'lodash/isEqual'; +import { AuthService } from '../../../../core/auth/auth.service'; +import { Router } from '@angular/router'; +import { Subscription } from 'rxjs'; @Component({ selector: 'ds-confirm-email', @@ -9,17 +20,24 @@ import { getRemoteDataPayload } from '../../../../core/shared/operators'; styleUrls: ['./confirm-email.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class ConfirmEmailComponent { +export class ConfirmEmailComponent implements OnDestroy { emailForm: FormGroup; - @Input() registrationId: string; + @Input() registrationData: RegistrationData; @Input() token: string; + subs: Subscription[] = []; + constructor( private formBuilder: FormBuilder, private externalLoginService: ExternalLoginService, + private epersonDataService: EPersonDataService, + private notificationService: NotificationsService, + private translate: TranslateService, + private authService: AuthService, + private router: Router ) { this.emailForm = this.formBuilder.group({ email: ['', [Validators.required, Validators.email]] @@ -28,13 +46,73 @@ export class ConfirmEmailComponent { submitForm() { this.emailForm.markAllAsTouched(); + this.router.navigate(['/login'], { queryParams: { authMethod: this.registrationData.registrationType } }); + if (this.emailForm.valid) { - const email = this.emailForm.get('email').value; - this.externalLoginService.patchUpdateRegistration([email], 'email', this.registrationId, this.token, 'replace') + const confirmedEmail = this.emailForm.get('email').value; + if (confirmedEmail && isEqual(this.registrationData.email, confirmedEmail.trim())) { + this.postCreateAccountFromToken(this.token, this.registrationData); + } else { + this.patchUpdateRegistration([confirmedEmail]); + } + } + } + + private patchUpdateRegistration(values: string[]) { + this.subs.push( + this.externalLoginService.patchUpdateRegistration(values, 'email', this.registrationData.id, this.token, 'replace') .pipe(getRemoteDataPayload()) .subscribe((update) => { + // TODO: remove this line (temporary) console.log('Email update:', update); - }); + })); + } + + /** + * Creates a new user from a given token and registration data. + * Based on the registration data, the user will be created with the following properties: + * - email: the email address from the registration data + * - metadata: all metadata values from the registration data, except for the email metadata key (ePerson object does not have an email metadata field) + * - canLogIn: true + * - requireCertificate: false + * @param token The token used to create the user. + * @param registrationData The registration data used to create the user. + * @returns An Observable that emits a boolean indicating whether the user creation was successful. + */ + private postCreateAccountFromToken( + token: string, + registrationData: RegistrationData + ) { + const metadataValues = {}; + for (const [key, value] of Object.entries(registrationData.registrationMetadata)) { + if (hasValue(value[0]?.value) && key !== 'email') { + metadataValues[key] = value[0]; + } } + const eperson = new EPerson(); + eperson.email = registrationData.email; + eperson.metadata = metadataValues; + eperson.canLogIn = true; + eperson.requireCertificate = false; + this.subs.push( + this.epersonDataService.createEPersonForToken(eperson, token).pipe( + getFirstCompletedRemoteData(), + ).subscribe((rd: RemoteData) => { + if (rd.hasFailed) { + this.notificationService.error( + this.translate.get('external-login-page.provide-email.create-account.notifications.error.header'), + this.translate.get('external-login-page.provide-email.create-account.notifications.error.content') + ); + } else if (rd.hasSucceeded) { + // TODO: redirect to ORCID login page + // set Redirect URL to User profile + this.router.navigate(['/login'], { queryParams: { authMethod: registrationData.registrationType } }); + this.authService.setRedirectUrl('/review-account'); + } + })); + } + + ngOnDestroy(): void { + this.subs.filter(sub => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } } diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts index 63e64830394..71d56e73cd1 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts @@ -7,17 +7,21 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; +import { ExternalLoginService } from '../../services/external-login.service'; describe('ProvideEmailComponent', () => { let component: ProvideEmailComponent; let fixture: ComponentFixture; + let externalLoginServiceSpy: jasmine.SpyObj; beforeEach(async () => { + const externalLoginService = jasmine.createSpyObj('ExternalLoginService', ['patchUpdateRegistration']); + await TestBed.configureTestingModule({ declarations: [ ProvideEmailComponent ], providers: [ FormBuilder, - { provide: EpersonRegistrationService, useValue: {} }, + { provide: ExternalLoginService, useValue: externalLoginService }, ], imports: [ CommonModule, @@ -36,10 +40,24 @@ describe('ProvideEmailComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ProvideEmailComponent); component = fixture.componentInstance; + externalLoginServiceSpy = TestBed.inject(ExternalLoginService) as jasmine.SpyObj; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + // it('should call externalLoginService.patchUpdateRegistration when form is submitted with valid email', () => { + // const email = 'test@example.com'; + // component.emailForm.setValue({ email }); + // component.registrationId = '123'; + // component.token = '456'; + // fixture.detectChanges(); + + // const button = fixture.nativeElement.querySelector('button[type="submit"]'); + // button.click(); + + // expect(externalLoginServiceSpy.patchUpdateRegistration).toHaveBeenCalledWith([email], 'email', component.registrationId, component.token, 'add'); + // }); }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts index 4b680656392..95e7a41ae91 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts @@ -1,7 +1,10 @@ -import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { getRemoteDataPayload } from '../../../../core/shared/operators'; import { ExternalLoginService } from '../../services/external-login.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Registration } from '../../../../core/shared/registration.model'; +import { Subscription } from 'rxjs'; +import { hasValue } from '../../../../shared/empty.util'; @Component({ selector: 'ds-provide-email', @@ -9,13 +12,22 @@ import { ExternalLoginService } from '../../services/external-login.service'; styleUrls: ['./provide-email.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ProvideEmailComponent { +export class ProvideEmailComponent implements OnDestroy { + /** + * The form group for the email input + */ emailForm: FormGroup; - + /** + * The registration id + */ @Input() registrationId: string; - + /** + * The token from the URL + */ @Input() token: string; + subs: Subscription[] = []; + constructor( private formBuilder: FormBuilder, private externalLoginService: ExternalLoginService, @@ -29,11 +41,15 @@ export class ProvideEmailComponent { this.emailForm.markAllAsTouched(); if (this.emailForm.valid) { const email = this.emailForm.get('email').value; - this.externalLoginService.patchUpdateRegistration([email], 'email', this.registrationId, this.token, 'add') - .pipe(getRemoteDataPayload()) - .subscribe((update) => { - console.log('Email update:', update); - }); + this.subs.push(this.externalLoginService.patchUpdateRegistration([email], 'email', this.registrationId, this.token, 'add') + .subscribe((rd: RemoteData) => { + // TODO: remove this line (temporary) + console.log('Email update:', rd); + })); } } + + ngOnDestroy(): void { + this.subs.filter(sub => hasValue(sub)).forEach((sub) => sub.unsubscribe()); + } } diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html index 47a301501bf..bc96b37844d 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html @@ -11,7 +11,7 @@

{{ 'external-login.confirmation.header' | translate}}

- + @@ -22,14 +22,14 @@

or

diff --git a/src/app/shared/log-in/log-in.component.ts b/src/app/shared/log-in/log-in.component.ts index 18a7d6ddf98..0322079475f 100644 --- a/src/app/shared/log-in/log-in.component.ts +++ b/src/app/shared/log-in/log-in.component.ts @@ -36,11 +36,20 @@ export class LogInComponent implements OnInit, OnDestroy { */ @Input() isStandalonePage: boolean; + /** + * Method to exclude from the list of authentication methods + */ @Input() excludedAuthMethod: AuthMethodType; - + /** + * Weather or not to show the register link + */ @Input() showRegisterLink = true; - @Input() hideAllLinks = false; + /** + * The external login method to force + * the user to use to login while completing the external login process + */ + @Input() externalLoginMethod: AuthMethodType; /** * The list of authentication methods available @@ -77,7 +86,6 @@ export class LogInComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.store.pipe( select(getAuthenticationMethods), ).subscribe(methods => { @@ -87,6 +95,10 @@ export class LogInComponent implements OnInit, OnDestroy { if (hasValue(this.excludedAuthMethod)) { this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType !== this.excludedAuthMethod); } + // if there is an external login method the user should follow, filter the auth methods to only show that one + if (hasValue(this.externalLoginMethod)) { + this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType === this.externalLoginMethod); + } }); // set loading diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index c2be335f99f..978ee6782d1 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7225,4 +7225,14 @@ "external-login.validate-email.no-token": "Something went wrong. Your email address is not validated succesfully.", "external-login-page.provide-email.notifications.error": "Something went wrong.Email address was omitted or the operation is not valid.", + + "external-login.error.notification": "There was an error while processing your request. Please try again later.", + + "external-login.connect-to-existing-account.label": "Connect to an existing user", + + "external-login.modal.label.close": "Close", + + "external-login-page.provide-email.create-account.notifications.error.header": "Something went wrong", + + "external-login-page.provide-email.create-account.notifications.error.content": "Please check again your email address and try again.", } diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index c4b8fdaff6e..7c2d5edca4e 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -11307,4 +11307,48 @@ // "external-login-validation.review-account-info.table.header.action": "Override", // TODO New key - Add a translation "external-login-validation.review-account-info.table.header.action": "Override", + + // "on-label": "ON", + // TODO New key - Add a translation + "on-label": "ON", + + // "off-label": "OFF", + // TODO New key - Add a translation + "off-label": "OFF", + + // "review-account-info.merge-data.notification.success": "Your account information has been updated successfully", + // TODO New key - Add a translation + "review-account-info.merge-data.notification.success": "Your account information has been updated successfully", + + // "review-account-info.merge-data.notification.error": "Something went wrong while updating your account information", + // TODO New key - Add a translation + "review-account-info.merge-data.notification.error": "Something went wrong while updating your account information", + + // "external-login.validate-email.no-token": "Something went wrong. Your email address is not validated succesfully.", + // TODO New key - Add a translation + "external-login.validate-email.no-token": "Something went wrong. Your email address is not validated succesfully.", + + // "external-login-page.provide-email.notifications.error": "Something went wrong.Email address was omitted or the operation is not valid.", + // TODO New key - Add a translation + "external-login-page.provide-email.notifications.error": "Something went wrong.Email address was omitted or the operation is not valid.", + + // "external-login.error.notification": "There was an error while processing your request. Please try again later.", + // TODO New key - Add a translation + "external-login.error.notification": "There was an error while processing your request. Please try again later.", + + // "external-login.connect-to-existing-account.label": "Connect to an existing user", + // TODO New key - Add a translation + "external-login.connect-to-existing-account.label": "Connect to an existing user", + + // "external-login.modal.label.close": "Close", + // TODO New key - Add a translation + "external-login.modal.label.close": "Close", + + // "external-login-page.provide-email.create-account.notifications.error.header": "Something went wrong", + // TODO New key - Add a translation + "external-login-page.provide-email.create-account.notifications.error.header": "Something went wrong", + + // "external-login-page.provide-email.create-account.notifications.error.content": "Please check again your email address and try again.", + // TODO New key - Add a translation + "external-login-page.provide-email.create-account.notifications.error.content": "Please check again your email address and try again.", } From 8f4067b6f19c6029ccd935d7fb4c7218aca6078e Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 3 Oct 2023 18:52:32 +0200 Subject: [PATCH 140/195] [DSC-1277] Italian translation fixes --- src/assets/i18n/it.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 3466ca341ea..5f171a40910 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -5722,7 +5722,7 @@ // "menu.section.statistics": "Statistics", - "menu.section.statistics": "Statistica", + "menu.section.statistics": "Statistiche", // "menu.section.statistics.workflow": "Workflow", "menu.section.statistics.workflow": "Workflow", From c85e8a7175cf4046b98f121c4c8a537e2db9ea5f Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 4 Oct 2023 10:05:33 +0200 Subject: [PATCH 141/195] [CST-10703] refactor --- src/app/app-routing.module.ts | 5 - .../external-login-page-routing.module.ts | 2 +- .../external-login-page.component.ts | 11 +- ...review-account-info-page-routing.module.ts | 3 +- ...in-review-account-info-page.component.html | 2 +- ...ogin-review-account-info-page.component.ts | 10 +- .../email-validated.component.html | 9 -- .../email-validated.component.scss | 0 .../email-validated.component.spec.ts | 75 ---------- .../email-validated.component.ts | 16 --- ...al-login-validation-page-routing.module.ts | 20 --- ...ernal-login-validation-page.component.html | 10 -- ...ernal-login-validation-page.component.scss | 0 ...al-login-validation-page.component.spec.ts | 112 --------------- ...xternal-login-validation-page.component.ts | 44 ------ .../external-login-validation-page.module.ts | 24 ---- ...stration-data-create-user.resolver.spec.ts | 129 ------------------ .../registration-data-create-user.resolver.ts | 93 ------------- ...xternal-login-validation-page.component.ts | 25 ---- .../confirm-email/confirm-email.component.ts | 19 ++- src/assets/i18n/en.json5 | 6 +- src/assets/i18n/it.json5 | 12 +- 22 files changed, 30 insertions(+), 597 deletions(-) delete mode 100644 src/app/external-login-validation-page/email-validated/email-validated.component.html delete mode 100644 src/app/external-login-validation-page/email-validated/email-validated.component.scss delete mode 100644 src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts delete mode 100644 src/app/external-login-validation-page/email-validated/email-validated.component.ts delete mode 100644 src/app/external-login-validation-page/external-login-validation-page-routing.module.ts delete mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.html delete mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.scss delete mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.spec.ts delete mode 100644 src/app/external-login-validation-page/external-login-validation-page.component.ts delete mode 100644 src/app/external-login-validation-page/external-login-validation-page.module.ts delete mode 100644 src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts delete mode 100644 src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts delete mode 100644 src/app/external-login-validation-page/themed-external-login-validation-page.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 2fd18766af9..fb4d658d8cb 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -176,11 +176,6 @@ import { RedirectService } from './redirect/redirect.service'; loadChildren: () => import('./external-login-page/external-login-page.module') .then((m) => m.ExternalLoginPageModule) }, - { - path: 'validate-email', - loadChildren: () => import('./external-login-validation-page/external-login-validation-page.module') - .then((m) => m.ExternalLoginValidationPageModule) - }, { path: 'review-account', loadChildren: () => import('./external-login-review-account-info/external-login-review-account-info-page.module') diff --git a/src/app/external-login-page/external-login-page-routing.module.ts b/src/app/external-login-page/external-login-page-routing.module.ts index 390db1eaf00..f7425b63d82 100644 --- a/src/app/external-login-page/external-login-page-routing.module.ts +++ b/src/app/external-login-page/external-login-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ path: '', pathMatch: 'full', component: ThemedExternalLoginPageComponent, - // canActivate: [RegistrationTokenGuard], // TODO: uncomment this line to enable the guard later + canActivate: [RegistrationTokenGuard], resolve: { registrationData: RegistrationDataResolver }, }, ]; diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index c8bde7cd0da..fd76a047ba5 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -43,12 +43,11 @@ export class ExternalLoginPageComponent implements OnInit { tap((data) => this.hasErrors = hasNoValue(data.registrationData)), map((data) => data.registrationData)); - // TODO: remove this line (temporary) - this.registrationData$ = of( - mockRegistrationDataModel - ); - this.hasErrors = false; - this.token = '1234567890'; + // this.registrationData$ = of( + // mockRegistrationDataModel + // ); + // this.hasErrors = false; + // this.token = '1234567890'; } } diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts index ffec9928371..b83816ece02 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; import { RegistrationDataResolver } from '../shared/external-log-in-complete/resolvers/registration-data.resolver'; +import { ReviewAccountGuard } from './helpers/review-account.guard'; const routes: Routes = [ @@ -9,7 +10,7 @@ const routes: Routes = [ path: '', pathMatch: 'full', component: ExternalLoginReviewAccountInfoPageComponent, - // canActivate: [ReviewAccountGuard],// TODO: Remove comment + canActivate: [ReviewAccountGuard], resolve: { registrationData: RegistrationDataResolver } },]; diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html index 97034110392..831b53ce714 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html @@ -8,6 +8,6 @@
diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts index 5c984445031..62da6995fa1 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts @@ -43,10 +43,10 @@ export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { map((data) => data.registrationData)); // TODO: remove this line (temporary) - this.registrationData$ = of( - mockRegistrationDataModel - ); - this.hasErrors = false; - this.token = '1234567890'; + // this.registrationData$ = of( + // mockRegistrationDataModel + // ); + // this.hasErrors = false; + // this.token = '1234567890'; } } diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.html b/src/app/external-login-validation-page/email-validated/email-validated.component.html deleted file mode 100644 index b58ed085890..00000000000 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.html +++ /dev/null @@ -1,9 +0,0 @@ -

- {{ "external-login.validated-email.header" | translate }} -

- -

- -
- -
diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.scss b/src/app/external-login-validation-page/email-validated/email-validated.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts deleted file mode 100644 index 21c7f5d7d8f..00000000000 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { EmailValidatedComponent } from './email-validated.component'; -import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { CommonModule } from '@angular/common'; -import { TranslateLoaderMock } from 'src/app/shared/mocks/translate-loader.mock'; -import { AuthService } from 'src/app/core/auth/auth.service'; -import { Router } from '@angular/router'; -import { RouterStub } from 'src/app/shared/testing/router.stub'; -import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; -import { of } from 'rxjs'; -import { AuthServiceMock } from 'src/app/shared/mocks/auth.service.mock'; - -describe('EmailValidatedComponent', () => { - let component: EmailValidatedComponent; - let fixture: ComponentFixture; - let compiledTemplate: HTMLElement; - - const translateServiceStub = { - get: () => of('Mocked Translation Text'), - instant: (key: any) => 'Mocked Translation Text', - onLangChange: new EventEmitter(), - onTranslationChange: new EventEmitter(), - onDefaultLangChange: new EventEmitter() - }; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ EmailValidatedComponent ], - providers: [ - { provide: AuthService, useValue: new AuthServiceMock()}, - { provide: Router, useValue: new RouterStub() }, - { provide: TranslateService, useValue: translateServiceStub }, - ], - imports: [ - CommonModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - ], - schemas: [NO_ERRORS_SCHEMA] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(EmailValidatedComponent); - component = fixture.componentInstance; - compiledTemplate = fixture.nativeElement; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should render translated header', () => { - const headerElement = compiledTemplate.querySelector('h4'); - expect(headerElement.textContent).toContain('Mocked Translation Text'); - }); - - it('should render translated info paragraph', () => { - const infoParagraphElement = compiledTemplate.querySelector('p'); - expect(infoParagraphElement.innerHTML).toBeTruthy(); - }); - - it('should render ds-log-in component', () => { - const dsLogInComponent = compiledTemplate.querySelector('ds-log-in'); - expect(dsLogInComponent).toBeTruthy(); - }); -}); - diff --git a/src/app/external-login-validation-page/email-validated/email-validated.component.ts b/src/app/external-login-validation-page/email-validated/email-validated.component.ts deleted file mode 100644 index 8fd45bc4a7f..00000000000 --- a/src/app/external-login-validation-page/email-validated/email-validated.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; -import { AuthService } from '../../core/auth/auth.service'; -@Component({ - selector: 'ds-email-validated', - templateUrl: './email-validated.component.html', - styleUrls: ['./email-validated.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class EmailValidatedComponent { - - constructor(private authService: AuthService) { - // After the user has validated his email, we need to redirect him to the review account page, - // in order to review his account information - this.authService.setRedirectUrl('/review-account'); - } -} diff --git a/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts b/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts deleted file mode 100644 index 241cebebb2f..00000000000 --- a/src/app/external-login-validation-page/external-login-validation-page-routing.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { ThemedExternalLoginValidationPageComponent } from './themed-external-login-validation-page.component'; -import { RegistrationTokenGuard } from '../shared/external-log-in-complete/guards/registration-token.guard'; -import { RegistrationDataCreateUserResolver } from './resolvers/registration-data-create-user.resolver'; -const routes: Routes = [ - { - path: '', - pathMatch: 'full', - component: ThemedExternalLoginValidationPageComponent, - // canActivate: [RegistrationTokenGuard] // TODO: uncomment this line to enable the guard later - resolve: { createUser: RegistrationDataCreateUserResolver }, - }, -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class ExternalLoginValidationPageRoutingModule { } diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.html b/src/app/external-login-validation-page/external-login-validation-page.component.html deleted file mode 100644 index cfdaed0d6db..00000000000 --- a/src/app/external-login-validation-page/external-login-validation-page.component.html +++ /dev/null @@ -1,10 +0,0 @@ -
- - - - -
diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.scss b/src/app/external-login-validation-page/external-login-validation-page.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts b/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts deleted file mode 100644 index 975fca9f81f..00000000000 --- a/src/app/external-login-validation-page/external-login-validation-page.component.spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; -import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; -import { Observable, of } from 'rxjs'; -import { RemoteData } from '../core/data/remote-data'; -import { CommonModule } from '@angular/common'; -import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; -import { ActivatedRoute } from '@angular/router'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { AlertType } from '../shared/alert/aletr-type'; -import { tr } from 'date-fns/locale'; - -describe('ExternalLoginValidationPageComponent', () => { - let component: ExternalLoginValidationPageComponent; - let componentAsAny: any; - let fixture: ComponentFixture; - let epersonRegistrationService: any; - - const registrationDataMock = { - registrationType: 'orcid', - email: 'test@test.com', - netId: '0000-0000-0000-0000', - user: 'a44d8c9e-9b1f-4e7f-9b1a-5c9d8a0b1f1a', - registrationMetadata: { - 'email': [{ value: 'test@test.com' }], - 'eperson.lastname': [{ value: 'Doe' }], - 'eperson.firstname': [{ value: 'John' }], - }, - }; - - const tokenMock = 'as552-5a5a5-5a5a5-5a5a5'; - - const routeStub = { - snapshot: { - queryParams: { - token: tokenMock - } - }, - data: of({ - createUser: true, - }), - }; - - beforeEach(async () => { - epersonRegistrationService = { - searchByTokenAndUpdateData: (token: string): Observable> => { return createSuccessfulRemoteDataObject$(registrationDataMock); } - }; - - await TestBed.configureTestingModule({ - declarations: [ExternalLoginValidationPageComponent], - providers: [ - { provide: ActivatedRoute, useValue: routeStub }, - ], - imports: [ - CommonModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock, - }, - }), - ], - schemas: [NO_ERRORS_SCHEMA], - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(ExternalLoginValidationPageComponent); - component = fixture.componentInstance; - componentAsAny = component; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should set validationFailed to true if createUser is falsy', () => { - const activatedRoute = TestBed.inject(ActivatedRoute); - activatedRoute.data = of({ createUser: false }); - component.ngOnInit(); - expect(componentAsAny.validationFailed.value).toBeTrue(); - }); - - it('should set validationFailed to false if createUser is truthy', () => { - const activatedRoute = TestBed.inject(ActivatedRoute); - activatedRoute.data = of({ createUser: true }); - component.ngOnInit(); - expect(componentAsAny.validationFailed.value).toBeFalse(); - }); - - it('should show DsEmailValidatedComponent when hasFailed() returns false', () => { - spyOn(component, 'hasFailed').and.returnValue(of(false)); - fixture.detectChanges(); - const dsEmailValidated = fixture.nativeElement.querySelector('ds-email-validated'); - expect(dsEmailValidated).toBeTruthy(); - }); - - it('should show DsAlertComponent when hasFailed() returns true', () => { - spyOn(component, 'hasFailed').and.returnValue(of(true)); - fixture.detectChanges(); - const dsAlert = fixture.nativeElement.querySelector('ds-alert'); - expect(dsAlert).toBeTruthy(); - }); - - afterEach(() => { - fixture.destroy(); - }); -}); diff --git a/src/app/external-login-validation-page/external-login-validation-page.component.ts b/src/app/external-login-validation-page/external-login-validation-page.component.ts deleted file mode 100644 index abcd256f8a7..00000000000 --- a/src/app/external-login-validation-page/external-login-validation-page.component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { AlertType } from '../shared/alert/aletr-type'; -import { BehaviorSubject, Observable } from 'rxjs'; - -@Component({ - templateUrl: './external-login-validation-page.component.html', - styleUrls: ['./external-login-validation-page.component.scss'], -}) -export class ExternalLoginValidationPageComponent implements OnInit { - /** - * The type of alert to show - */ - public AlertTypeEnum = AlertType; - - /** - * Whether the component has errors - */ - private validationFailed: BehaviorSubject = - new BehaviorSubject(false); - - constructor( - private arouter: ActivatedRoute, - ) { - } - - ngOnInit(): void { - this.arouter.data.subscribe((data) => { - const resolvedData = data.createUser; - this.validationFailed.next(!resolvedData); - }); - - // TODO: remove this line (temporary) - this.validationFailed.next(false); - } - - - /** - * Check if the validation has failed - */ - public hasFailed(): Observable { - return this.validationFailed.asObservable(); - } -} diff --git a/src/app/external-login-validation-page/external-login-validation-page.module.ts b/src/app/external-login-validation-page/external-login-validation-page.module.ts deleted file mode 100644 index 3bad614d7e6..00000000000 --- a/src/app/external-login-validation-page/external-login-validation-page.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { ExternalLoginValidationPageRoutingModule } from './external-login-validation-page-routing.module'; -import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; -import { ThemedExternalLoginValidationPageComponent } from './themed-external-login-validation-page.component'; - -import { EmailValidatedComponent } from './email-validated/email-validated.component'; -import { SharedModule } from '../shared/shared.module'; - - -@NgModule({ - declarations: [ - ExternalLoginValidationPageComponent, - ThemedExternalLoginValidationPageComponent, - EmailValidatedComponent, - ], - imports: [ - CommonModule, - ExternalLoginValidationPageRoutingModule, - SharedModule - ] -}) -export class ExternalLoginValidationPageModule { } diff --git a/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts b/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts deleted file mode 100644 index 979bdc89491..00000000000 --- a/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.spec.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { RegistrationDataCreateUserResolver } from './registration-data-create-user.resolver'; -import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; -import { RouterStateSnapshot } from '@angular/router'; -import { EPerson } from '../../core/eperson/models/eperson.model'; -import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; -import { Registration } from '../../core/shared/registration.model'; -import { MetadataValue } from 'src/app/core/shared/metadata.models'; - -describe('RegistrationDataCreateUserResolver', () => { - let resolver: RegistrationDataCreateUserResolver; - let epersonRegistrationService: jasmine.SpyObj; - let epersonDataService: jasmine.SpyObj; - - const registrationDataMock = { - registrationType: 'orcid', - email: 'test@test.com', - netId: '0000-0000-0000-0000', - user: null, - registrationMetadata: { - 'email': [{ value: 'test@test.com' }], - 'eperson.lastname': [{ value: 'Doe' }], - 'eperson.firstname': [{ value: 'John' }], - }, - }; - - const tokenMock = 'as552-5a5a5-5a5a5-5a5a5'; - - - beforeEach(() => { - const spyEpersonRegistrationService = jasmine.createSpyObj('EpersonRegistrationService', [ - 'searchByTokenAndUpdateData' - ]); - const spyEpersonDataService = jasmine.createSpyObj('EPersonDataService', [ - 'createEPersonForToken' - ]); - - TestBed.configureTestingModule({ - providers: [ - RegistrationDataCreateUserResolver, - { provide: EpersonRegistrationService, useValue: spyEpersonRegistrationService }, - { provide: EPersonDataService, useValue: spyEpersonDataService } - ] - }); - resolver = TestBed.inject(RegistrationDataCreateUserResolver); - epersonRegistrationService = TestBed.inject(EpersonRegistrationService) as jasmine.SpyObj; - epersonDataService = TestBed.inject(EPersonDataService) as jasmine.SpyObj; - }); - - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); - - describe('resolve', () => { - const token = tokenMock; - const route = { params: { token: token } } as any; - const state = {} as RouterStateSnapshot; - - it('should create a new user and return true when registration data does not contain a user', (done) => { - const registration = new Registration(); - epersonRegistrationService.searchByTokenAndUpdateData.and.returnValue(createSuccessfulRemoteDataObject$(registration)); - - const createdEPerson = new EPerson(); - createdEPerson.email = registrationDataMock.email; - createdEPerson.metadata = { - 'eperson.lastname': [Object.assign(new MetadataValue(), { value: 'Doe' })], - 'eperson.firstname': [Object.assign(new MetadataValue(), { value: 'John' })], - }; - createdEPerson.canLogIn = true; - createdEPerson.requireCertificate = false; - epersonDataService.createEPersonForToken.and.returnValue(createSuccessfulRemoteDataObject$(createdEPerson)); - - resolver.resolve(route, state).subscribe((result) => { - expect(result).toBeTrue(); - expect(epersonRegistrationService.searchByTokenAndUpdateData).toHaveBeenCalledWith(token); - expect(epersonDataService.createEPersonForToken).toHaveBeenCalledWith(createdEPerson, token); - done(); - }); - }); - - it('should return false when search by token and update data fails', (done) => { - epersonRegistrationService.searchByTokenAndUpdateData.and.returnValue(createFailedRemoteDataObject$()); - - resolver.resolve(route, state).subscribe((result) => { - expect(result).toBeFalse(); - expect(epersonRegistrationService.searchByTokenAndUpdateData).toHaveBeenCalledWith(token); - expect(epersonDataService.createEPersonForToken).not.toHaveBeenCalled(); - done(); - }); - }); - - }); - - describe('createUserFromToken', () => { - const token = tokenMock; - const registrationData: RegistrationData = Object.assign(new RegistrationData(), registrationDataMock); - - it('should create a new user and return true', (done) => { - const createdEPerson = new EPerson(); - createdEPerson.email = registrationData.email; - createdEPerson.metadata = { - 'eperson.lastname': 'Doe', //[{ value: 'Doe' }], - 'eperson.firstname': 'John',// [{ value: 'John' }], - } as any; - createdEPerson.canLogIn = true; - createdEPerson.requireCertificate = false; - epersonDataService.createEPersonForToken.and.returnValue(createSuccessfulRemoteDataObject$(createdEPerson)); - - resolver.createUserFromToken(token, registrationData).subscribe((result) => { - expect(result).toBeTrue(); - expect(epersonDataService.createEPersonForToken).toHaveBeenCalledWith(createdEPerson, token); - done(); - }); - }); - - it('should return false when create EPerson for token fails', (done) => { - epersonDataService.createEPersonForToken.and.returnValue(createFailedRemoteDataObject$()); - - resolver.createUserFromToken(token, registrationData).subscribe((result) => { - expect(result).toBeFalse(); - expect(epersonDataService.createEPersonForToken).toHaveBeenCalledWith(jasmine.any(EPerson), token); - done(); - }); - }); - }); -}); diff --git a/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts b/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts deleted file mode 100644 index 000422f7623..00000000000 --- a/src/app/external-login-validation-page/resolvers/registration-data-create-user.resolver.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable } from '@angular/core'; -import { - Resolve, - RouterStateSnapshot, - ActivatedRouteSnapshot, -} from '@angular/router'; -import { Observable, map, of, switchMap } from 'rxjs'; -import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; -import { RemoteData } from '../../core/data/remote-data'; -import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { EPerson } from '../../core/eperson/models/eperson.model'; -import { getFirstCompletedRemoteData } from '../../core/shared/operators'; -import { hasValue, hasNoValue } from '../../shared/empty.util'; -import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; - -@Injectable({ - providedIn: 'root', -}) -export class RegistrationDataCreateUserResolver implements Resolve { - constructor( - private epersonRegistrationService: EpersonRegistrationService, - private epersonDataService: EPersonDataService - ) {} - - /** - * Resolves the registration data and creates a new user account from the given token. - * The account will be created only when the registration data does NOT contain a user (uuid). - * @param route The activated route snapshot. - * @param state The router state snapshot. - * @returns An observable of a boolean indicating whether the user was created successfully or not. - */ - resolve( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot - ): Observable { - const token = route.params.token; - return this.epersonRegistrationService - .searchByTokenAndUpdateData(token) - .pipe( - getFirstCompletedRemoteData(), - switchMap((rd) => { - if ( - rd.hasSucceeded && - hasValue(rd.payload) && - hasNoValue(rd.payload.user) - ) { - const registrationData = Object.assign( - new RegistrationData(), - rd.payload - ); - return this.createUserFromToken(token, registrationData); - } - if (rd.hasFailed) { - return of(false); - } - }) - ); - } - - /** - * Creates a new user from a given token and registration data. - * Based on the registration data, the user will be created with the following properties: - * - email: the email address from the registration data - * - metadata: all metadata values from the registration data, except for the email metadata key (ePerson object does not have an email metadata field) - * - canLogIn: true - * - requireCertificate: false - * @param token The token used to create the user. - * @param registrationData The registration data used to create the user. - * @returns An Observable that emits a boolean indicating whether the user creation was successful. - */ - createUserFromToken( - token: string, - registrationData: RegistrationData - ): Observable { - const metadataValues = {}; - for (const [key, value] of Object.entries(registrationData.registrationMetadata)) { - if (hasValue(value[0]?.value) && key !== 'email') { - metadataValues[key] = value[0]?.value; - } - } - const eperson = new EPerson(); - eperson.email = registrationData.email; - eperson.metadata = metadataValues; - eperson.canLogIn = true; - eperson.requireCertificate = false; - return this.epersonDataService.createEPersonForToken(eperson, token).pipe( - getFirstCompletedRemoteData(), - map((rd: RemoteData) => { - return rd.hasSucceeded; - }) - ); - } -} diff --git a/src/app/external-login-validation-page/themed-external-login-validation-page.component.ts b/src/app/external-login-validation-page/themed-external-login-validation-page.component.ts deleted file mode 100644 index a08b2669deb..00000000000 --- a/src/app/external-login-validation-page/themed-external-login-validation-page.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Component } from '@angular/core'; -import { ThemedComponent } from '../shared/theme-support/themed.component'; -import { ExternalLoginValidationPageComponent } from './external-login-validation-page.component'; - -/** - * Themed wrapper for ExternalLoginValidationPageComponent - */ -@Component({ - selector: 'ds-themed-external-login-page', - styleUrls: [], - templateUrl: './../shared/theme-support/themed.component.html' -}) -export class ThemedExternalLoginValidationPageComponent extends ThemedComponent { - protected getComponentName(): string { - return 'ExternalLoginValidationPageComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../themes/${themeName}/app/external-login-validation-page/external-login-validation-page.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./external-login-validation-page.component`); - } -} diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index d4e470f9e72..45683aaee5b 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -21,13 +21,22 @@ import { Subscription } from 'rxjs'; changeDetection: ChangeDetectionStrategy.OnPush }) export class ConfirmEmailComponent implements OnDestroy { - + /** + * The form containing the email input + */ emailForm: FormGroup; - + /** + * The registration data object + */ @Input() registrationData: RegistrationData; + /** + * The token to be used to confirm the registration + */ @Input() token: string; - + /** + * The subscriptions to unsubscribe from + */ subs: Subscription[] = []; constructor( @@ -46,8 +55,6 @@ export class ConfirmEmailComponent implements OnDestroy { submitForm() { this.emailForm.markAllAsTouched(); - this.router.navigate(['/login'], { queryParams: { authMethod: this.registrationData.registrationType } }); - if (this.emailForm.valid) { const confirmedEmail = this.emailForm.get('email').value; if (confirmedEmail && isEqual(this.registrationData.email, confirmedEmail.trim())) { @@ -104,7 +111,7 @@ export class ConfirmEmailComponent implements OnDestroy { this.translate.get('external-login-page.provide-email.create-account.notifications.error.content') ); } else if (rd.hasSucceeded) { - // TODO: redirect to ORCID login page + // redirect to ORCID login page // set Redirect URL to User profile this.router.navigate(['/login'], { queryParams: { authMethod: registrationData.registrationType } }); this.authService.setRedirectUrl('/review-account'); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 978ee6782d1..94a153d1772 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7194,10 +7194,6 @@ "external-login.confirm-email-sent.info": " We have sent an emait to the provided address to validate your input.
Please follow the instructions in the email to complete the login process.", - "external-login.validated-email.header": "Email validated", - - "external-login.validated-email.info": "Your email has been validated.
You can now login in the system with your prefered authentication method.", - "external-login.provide-email.header": "Provide email", "external-login.provide-email.button.label": "Send Verification link", @@ -7222,7 +7218,7 @@ "review-account-info.merge-data.notification.error": "Something went wrong while updating your account information", - "external-login.validate-email.no-token": "Something went wrong. Your email address is not validated succesfully.", + "review-account-info.alert.error.content": "Something went wrong. Please try again later.", "external-login-page.provide-email.notifications.error": "Something went wrong.Email address was omitted or the operation is not valid.", diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 7c2d5edca4e..fd5fb8ca8b5 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -11268,14 +11268,6 @@ // TODO New key - Add a translation "external-login.confirm-email-sent.info": " We have sent an emait to the provided address to validate your input.
Please follow the instructions in the email to complete the login process.", - // "external-login.validated-email.header": "Email validated", - // TODO New key - Add a translation - "external-login.validated-email.header": "Email validated", - - // "external-login.validated-email.info": "Your email has been validated.
You can now login in the system with your prefered authentication method.", - // TODO New key - Add a translation - "external-login.validated-email.info": "Your email has been validated.
You can now login in the system with your prefered authentication method.", - // "external-login.provide-email.header": "Provide email", // TODO New key - Add a translation "external-login.provide-email.header": "Provide email", @@ -11324,9 +11316,9 @@ // TODO New key - Add a translation "review-account-info.merge-data.notification.error": "Something went wrong while updating your account information", - // "external-login.validate-email.no-token": "Something went wrong. Your email address is not validated succesfully.", + // "review-account-info.alert.error.content": "Something went wrong. Please try again later.", // TODO New key - Add a translation - "external-login.validate-email.no-token": "Something went wrong. Your email address is not validated succesfully.", + "review-account-info.alert.error.content": "Something went wrong. Please try again later.", // "external-login-page.provide-email.notifications.error": "Something went wrong.Email address was omitted or the operation is not valid.", // TODO New key - Add a translation From 457324e48cce7d53e690601230a9f43fbf245765 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 4 Oct 2023 11:58:45 +0200 Subject: [PATCH 142/195] [DSC-1277] Italian translation fixes --- src/assets/i18n/it.json5 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 5f171a40910..8f50f76d0c5 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -1247,7 +1247,7 @@ "bitstream.edit.form.embargo.label": "Embargo fino a data specifica", // "bitstream.edit.form.fileName.hint": "Change the filename for the bitstream. Note that this will change the display bitstream URL, but old links will still resolve as long as the sequence ID does not change.", - "bitstream.edit.form.fileName.hint": "Modificare il nome del file per il bitstream. Si noti che questo cambierà il display bitstream URL , i vecchi collegamenti verranno comunque risolti finché il sequence ID non cambierà  .", + "bitstream.edit.form.fileName.hint": "Modificare il nome del file per il bitstream. Si noti che questo cambierà il display bitstream URL, i vecchi collegamenti verranno comunque risolti finché il sequence ID non cambierà.", // "bitstream.edit.form.fileName.label": "Filename", "bitstream.edit.form.fileName.label": "Filename", @@ -1314,7 +1314,7 @@ "bitstream.edit.title": "Modifica bitstream", // "bitstream-request-a-copy.alert.canDownload1": "You already have access to this file. If you want to download the file, click ", - "bitstream-request-a-copy.alert.canDownload1": "Hai già accesso a questo file. Se si desidera scaricare il file, fare clic su", + "bitstream-request-a-copy.alert.canDownload1": "Hai già accesso a questo file. Se si desidera scaricare il file, fare clic su", // "bitstream-request-a-copy.alert.canDownload2": "here", "bitstream-request-a-copy.alert.canDownload2": "qui", @@ -2366,7 +2366,7 @@ "context-menu.actions.request-correction.confirm.submit": "Sì, sono sicuro.", // "context-menu.actions.request-correction.error.403": "A request for correction has already been sent, impossible to proceed with the operation.", - "context-menu.actions.request-correction.error.403": "È già stata inviata una richiesta di correzione, impossibile procedere con l'operazione.", + "context-menu.actions.request-correction.error.403": "È già stata inviata una richiesta di correzione, impossibile procedere con l'operazione.", // "context-menu.actions.request-correction.error.generic": "There was an issue when requesting a correction for the item, please try again later.", "context-menu.actions.request-correction.error.generic": "Si è verificato un problema durante la richiesta di correzione per l'item, riprova più tardi.", From 1d9689df9b8b12e3f5a8889fe1073480e313f27a Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 4 Oct 2023 16:52:09 +0200 Subject: [PATCH 143/195] [DSC-1277] CRIS/GLAM translation sync 1 --- src/assets/i18n/it.json5 | 88 ++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 8f50f76d0c5..0aaff9e328d 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -21,7 +21,7 @@ "403.link.home-page": "Torna alla home page", // "403.forbidden": "forbidden", - "403.forbidden": "vietato", + "403.forbidden": "Accesso negato", // "500.page-internal-server-error": "Service Unavailable", "500.page-internal-server-error": "Servizio non disponibile", @@ -49,10 +49,10 @@ "410.link.home-page": "Torna alla home page", // "error-page.description.401": "unauthorized", - "error-page.description.401": "non autorizzato", + "error-page.description.401": "Non autorizzato", // "error-page.description.403": "forbidden", - "error-page.description.403": "accesso negato", + "error-page.description.403": "Accesso negato", // "error-page.description.500": "Service Unavailable", "error-page.description.500": "Servizio non disponibile", @@ -90,13 +90,13 @@ // "admin.edit-user-agreement.breadcrumbs": "Edit User Agreement", - "admin.edit-user-agreement.breadcrumbs": "Modifica l'accordo con l'utente finale", + "admin.edit-user-agreement.breadcrumbs": "Modifica User Agreement", // "admin.edit-user-agreement.confirm.title": "Force acceptance", "admin.edit-user-agreement.confirm.title": "Forza l'accettazione", // "admin.edit-user-agreement.confirm.info":"Do you want to force all users to accept the new user agreement?", - "admin.edit-user-agreement.confirm.info": "Si desidera forzare tutti gli utenti ad accettare il nuovo accordo con l'utente finale?", + "admin.edit-user-agreement.confirm.info": "Si desidera forzare tutti gli utenti ad accettare il nuovo User Agreement?", // "admin.edit-user-agreement.confirm.cancel":"Cancel", "admin.edit-user-agreement.confirm.cancel": "Annulla", @@ -111,19 +111,19 @@ "admin.edit-user-agreement.save-button": "Salva", // "admin.edit-user-agreement.header": "Edit User Agreement", - "admin.edit-user-agreement.header": "Modifica accordo con l'utente finale", + "admin.edit-user-agreement.header": "Modifica lo User Agreement", // "admin.edit-user-agreement.success": "User agreement successfully updated", - "admin.edit-user-agreement.success": "Accordo con l'utente finale aggiornato correttamente", + "admin.edit-user-agreement.success": "User Agreement aggiornato correttamente", // "admin.edit-user-agreement.error": "An error occurred while updating the user agreement", - "admin.edit-user-agreement.error": "Si è verificato un errore durante l'aggiornamento dell'accordo con l'utente finale", + "admin.edit-user-agreement.error": "Si è verificato un errore durante l'aggiornamento dell'User Agreement", // "admin.edit-user-agreement.title": "Edit User Agreement", - "admin.edit-user-agreement.title": "Modifica accordo con l'utente finale", + "admin.edit-user-agreement.title": "Modifica User Agreement", // "admin.edit-user-agreement.markdown": "End User Agreement text supports Markdown language.", - "admin.edit-user-agreement.markdown": "L'accordo con l'utente finale supporta il formato Markdown.", + "admin.edit-user-agreement.markdown": "Lo Lo User Agreement supporta il formato Markdown.", // "admin.edit-cms-metadata.title": "Edit CMS Metadata", "admin.edit-cms-metadata.title": "Modifica metadati CMS", @@ -174,7 +174,7 @@ "admin.institution.new.submit": "Invia", // "admin.institution.new.success": "The new institution was successfully created", - "admin.institution.new.success": "La nuova istituzione è stata creata con successo", + "admin.institution.new.success": "La nuova istituzione è stata creata", // "admin.institution.new.title": "Create Institution", "admin.institution.new.title": "Crea istituzione", @@ -207,19 +207,19 @@ // "admin.registries.bitstream-formats.breadcrumbs": "Format registry", - "admin.registries.bitstream-formats.breadcrumbs": "Formattare il registro", + "admin.registries.bitstream-formats.breadcrumbs": "Registro dei formati", // "admin.registries.bitstream-formats.create.breadcrumbs": "Bitstream format", - "admin.registries.bitstream-formats.create.breadcrumbs": "Formato del bitstream", + "admin.registries.bitstream-formats.create.breadcrumbs": "Formato dei bitstream", // "admin.registries.bitstream-formats.create.failure.content": "An error occurred while creating the new bitstream format.", "admin.registries.bitstream-formats.create.failure.content": "Si è verificato un errore durante la creazione del nuovo formato bitstream.", // "admin.registries.bitstream-formats.create.failure.head": "Failure", - "admin.registries.bitstream-formats.create.failure.head": "Fallimento", + "admin.registries.bitstream-formats.create.failure.head": "Errore", // "admin.registries.bitstream-formats.create.head": "Create Bitstream format", - "admin.registries.bitstream-formats.create.head": "Crea formato Bitstream", + "admin.registries.bitstream-formats.create.head": "Crea un formato Bitstream", // "admin.registries.bitstream-formats.create.new": "Add a new bitstream format", "admin.registries.bitstream-formats.create.new": "Aggiungere un nuovo formato bitstream", @@ -228,22 +228,22 @@ "admin.registries.bitstream-formats.create.success.content": "Il nuovo formato bitstream è stato creato correttamente.", // "admin.registries.bitstream-formats.create.success.head": "Success", - "admin.registries.bitstream-formats.create.success.head": "Successo", + "admin.registries.bitstream-formats.create.success.head": "Operazione riuscita", // "admin.registries.bitstream-formats.delete.failure.amount": "Failed to remove {{ amount }} format(s)", - "admin.registries.bitstream-formats.delete.failure.amount": "Impossibile rimuovere i formati {{ amount }}", + "admin.registries.bitstream-formats.delete.failure.amount": "Impossibile cancellare i formati {{ amount }}", // "admin.registries.bitstream-formats.delete.failure.head": "Failure", - "admin.registries.bitstream-formats.delete.failure.head": "Fallimento", + "admin.registries.bitstream-formats.delete.failure.head": "Errore", // "admin.registries.bitstream-formats.delete.success.amount": "Successfully removed {{ amount }} format(s)", - "admin.registries.bitstream-formats.delete.success.amount": "Rimosso con successo i formati {{ amount }}", + "admin.registries.bitstream-formats.delete.success.amount": "Rimossi con successo i formati {{ amount }}", // "admin.registries.bitstream-formats.delete.success.head": "Success", - "admin.registries.bitstream-formats.delete.success.head": "Successo", + "admin.registries.bitstream-formats.delete.success.head": "Operazione riuscita", // "admin.registries.bitstream-formats.description": "This list of bitstream formats provides information about known formats and their support level.", - "admin.registries.bitstream-formats.description": "Questo elenco di formati bitstream fornisce informazioni sui formati noti e sul loro livello di supporto.", + "admin.registries.bitstream-formats.description": "Questo elenco di formati bitstream fornisce informazioni sui formati gestiti dal sistema e sul loro livello di supporto.", // "admin.registries.bitstream-formats.edit.breadcrumbs": "Bitstream format", "admin.registries.bitstream-formats.edit.breadcrumbs": "Formato bitstream", @@ -261,22 +261,22 @@ "admin.registries.bitstream-formats.edit.extensions.label": "Estensione", // "admin.registries.bitstream-formats.edit.extensions.placeholder": "Enter a file extension without the dot", - "admin.registries.bitstream-formats.edit.extensions.placeholder": "Inserisci un'estensione di file senza il punto", + "admin.registries.bitstream-formats.edit.extensions.placeholder": "Inserisci l'estensione del file senza il punto", // "admin.registries.bitstream-formats.edit.failure.content": "An error occurred while editing the bitstream format.", "admin.registries.bitstream-formats.edit.failure.content": "Si è verificato un errore durante la modifica del formato bitstream.", // "admin.registries.bitstream-formats.edit.failure.head": "Failure", - "admin.registries.bitstream-formats.edit.failure.head": "Fallimento", + "admin.registries.bitstream-formats.edit.failure.head": "Errore", // "admin.registries.bitstream-formats.edit.head": "Bitstream format: {{ format }}", "admin.registries.bitstream-formats.edit.head": "Formato Bitstream: {{ format }}", // "admin.registries.bitstream-formats.edit.internal.hint": "Formats marked as internal are hidden from the user, and used for administrative purposes.", - "admin.registries.bitstream-formats.edit.internal.hint": "I formati contrassegnati come interni sono nascosti all'utente e utilizzati per scopi amministrativi.", + "admin.registries.bitstream-formats.edit.internal.hint": "I formati contrassegnati come internal sono nascosti all'utente e utilizzati per scopi amministrativi.", // "admin.registries.bitstream-formats.edit.internal.label": "Internal", - "admin.registries.bitstream-formats.edit.internal.label": "Interno", + "admin.registries.bitstream-formats.edit.internal.label": "Internal", // "admin.registries.bitstream-formats.edit.mimetype.hint": "The MIME type associated with this format, does not have to be unique.", "admin.registries.bitstream-formats.edit.mimetype.hint": "Non è necessario che il tipo MIME associato a questo formato sia univoco.", @@ -294,7 +294,7 @@ "admin.registries.bitstream-formats.edit.success.content": "Il formato bitstream è stato modificato correttamente.", // "admin.registries.bitstream-formats.edit.success.head": "Success", - "admin.registries.bitstream-formats.edit.success.head": "Successo", + "admin.registries.bitstream-formats.edit.success.head": "Operazione riuscita", // "admin.registries.bitstream-formats.edit.supportLevel.hint": "The level of support your institution pledges for this format.", "admin.registries.bitstream-formats.edit.supportLevel.hint": "Il livello di supporto che la vostra istituzione si impegna a dare a questo formato.", @@ -303,19 +303,19 @@ "admin.registries.bitstream-formats.edit.supportLevel.label": "Livello di supporto", // "admin.registries.bitstream-formats.head": "Bitstream Format Registry", - "admin.registries.bitstream-formats.head": "Registro di sistema del formato Bitstream", + "admin.registries.bitstream-formats.head": "Registro di sistema dei formati Bitstream", // "admin.registries.bitstream-formats.no-items": "No bitstream formats to show.", "admin.registries.bitstream-formats.no-items": "Nessun formato bitstream da mostrare.", // "admin.registries.bitstream-formats.table.delete": "Delete selected", - "admin.registries.bitstream-formats.table.delete": "Elimina selezionato", + "admin.registries.bitstream-formats.table.delete": "Elimina bitstream selezionato", // "admin.registries.bitstream-formats.table.deselect-all": "Deselect all", "admin.registries.bitstream-formats.table.deselect-all": "Deseleziona tutto", // "admin.registries.bitstream-formats.table.internal": "internal", - "admin.registries.bitstream-formats.table.internal": "interno", + "admin.registries.bitstream-formats.table.internal": "internal", // "admin.registries.bitstream-formats.table.mimetype": "MIME Type", "admin.registries.bitstream-formats.table.mimetype": "Tipo MIME", @@ -332,7 +332,7 @@ "admin.registries.bitstream-formats.table.supportLevel.KNOWN": "Conosciuto", // "admin.registries.bitstream-formats.table.supportLevel.SUPPORTED": "Supported", - "admin.registries.bitstream-formats.table.supportLevel.SUPPORTED": "Sostenuto", + "admin.registries.bitstream-formats.table.supportLevel.SUPPORTED": "Supportato", // "admin.registries.bitstream-formats.table.supportLevel.UNKNOWN": "Unknown", "admin.registries.bitstream-formats.table.supportLevel.UNKNOWN": "Sconosciuto", @@ -341,7 +341,7 @@ "admin.registries.bitstream-formats.table.supportLevel.head": "Livello di supporto", // "admin.registries.bitstream-formats.title": "Bitstream Format Registry", - "admin.registries.bitstream-formats.title": "Registro di sistema del formato Bitstream", + "admin.registries.bitstream-formats.title": "Registro dei formati Bitstream", @@ -349,7 +349,7 @@ "admin.registries.metadata.breadcrumbs": "Registro dei metadati", // "admin.registries.metadata.description": "The metadata registry maintains a list of all metadata fields available in the repository. These fields may be divided amongst multiple schemas. However, DSpace requires the qualified Dublin Core schema.", - "admin.registries.metadata.description": "Il Registro dei metadati mantiene un elenco di tutti i campi di metadati disponibili nel repository. Questi campi possono essere suddivisi tra più schemi. Tuttavia, DSpace richiede lo schema Dublin Core qualificato.", + "admin.registries.metadata.description": "Il Registro dei metadati mantiene un elenco di tutti i campi di metadati disponibili nel repository. Questi campi possono essere suddivisi tra più schemi. Tuttavia, la piattaforma richiede lo schema Dublin Core qualificato.", // "admin.registries.metadata.form.create": "Create metadata schema", "admin.registries.metadata.form.create": "Creare un Metadata Schema", @@ -370,7 +370,7 @@ "admin.registries.metadata.schemas.no-items": "Nessun Metadata Schema da mostrare.", // "admin.registries.metadata.schemas.table.delete": "Delete selected", - "admin.registries.metadata.schemas.table.delete": "Elimina selezionato", + "admin.registries.metadata.schemas.table.delete": "Elimina schema selezionato", // "admin.registries.metadata.schemas.table.id": "ID", "admin.registries.metadata.schemas.table.id": "ID", @@ -396,13 +396,13 @@ "admin.registries.schema.description": "Questo è il Metadata Schema per \"{{namespace}}.", // "admin.registries.schema.fields.head": "Schema metadata fields", - "admin.registries.schema.fields.head": "Campi dei metadati dello schema", + "admin.registries.schema.fields.head": "Campi del metadata schema", // "admin.registries.schema.fields.no-items": "No metadata fields to show.", - "admin.registries.schema.fields.no-items": "Nessun campo di metadati da mostrare.", + "admin.registries.schema.fields.no-items": "Nessun campo da mostrare.", // "admin.registries.schema.fields.table.delete": "Delete selected", - "admin.registries.schema.fields.table.delete": "Elimina selezionato", + "admin.registries.schema.fields.table.delete": "Elimina campo selezionato", // "admin.registries.schema.fields.table.field": "Field", "admin.registries.schema.fields.table.field": "Campo", @@ -413,13 +413,13 @@ "admin.registries.schema.fields.table.scopenote": "Nota di ambito", // "admin.registries.schema.form.create": "Create metadata field", - "admin.registries.schema.form.create": "Creare un campo di metadati", + "admin.registries.schema.form.create": "Creare un campo", // "admin.registries.schema.form.edit": "Edit metadata field", - "admin.registries.schema.form.edit": "Modificare il campo dei metadati", + "admin.registries.schema.form.edit": "Modifica il campo ", // "admin.registries.schema.form.element": "Element", - "admin.registries.schema.form.element": "Elemento", + "admin.registries.schema.form.element": "Element", // "admin.registries.schema.form.qualifier": "Qualifier", "admin.registries.schema.form.qualifier": "Qualifier", @@ -446,25 +446,25 @@ "admin.registries.schema.notification.failure": "Errore", // "admin.registries.schema.notification.field.created": "Successfully created metadata field \"{{field}}\"", - "admin.registries.schema.notification.field.created": "Campo dei metadati creato correttamente \"{{field}}\"", + "admin.registries.schema.notification.field.created": "Campo \"{{field}}\" creato correttamente", // "admin.registries.schema.notification.field.deleted.failure": "Failed to delete {{amount}} metadata fields", - "admin.registries.schema.notification.field.deleted.failure": "Impossibile eliminare i campi di metadati {{amount}}", + "admin.registries.schema.notification.field.deleted.failure": "Impossibile eliminare {{amount}} campo/i", // "admin.registries.schema.notification.field.deleted.success": "Successfully deleted {{amount}} metadata fields", "admin.registries.schema.notification.field.deleted.success": "Campi di metadati {{amount}} eliminati correttamente", // "admin.registries.schema.notification.field.edited": "Successfully edited metadata field \"{{field}}\"", - "admin.registries.schema.notification.field.edited": "Campo dei metadati modificato correttamente \"{{field}}\"", + "admin.registries.schema.notification.field.edited": "Campo \"{{field}}\" modificato correttamente", // "admin.registries.schema.notification.success": "Success", - "admin.registries.schema.notification.success": "Successo", + "admin.registries.schema.notification.success": "Operazione riuscita", // "admin.registries.schema.return": "Back", "admin.registries.schema.return": "Indietro", // "admin.registries.schema.title": "Metadata Schema Registry", - "admin.registries.schema.title": "Registro del Metadata Schema", + "admin.registries.schema.title": "Registro dei Metadata Schema", From 37d593f4a2cce30b1fabaac45f349b2a62d9014a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 4 Oct 2023 18:55:38 +0200 Subject: [PATCH 144/195] [DSC-1120] set Chrome version for e2e tests --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3b7aff6897..30c5c9c5dbb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: DSPACE_UI_HOST: 127.0.0.1 # When Chrome version is specified, we pin to a specific version of Chrome # Comment this out to use the latest release - #CHROME_VERSION: "90.0.4430.212-1" + CHROME_VERSION: "116.0.5845.179" strategy: # Create a matrix of Node versions to test against (in parallel) matrix: From ab5aa7a64aff8f6fc80a615d35b4487879cf4c9a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 4 Oct 2023 19:18:00 +0200 Subject: [PATCH 145/195] [DSC-1120] set Chrome version for e2e tests --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 30c5c9c5dbb..dabc0b428e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: DSPACE_UI_HOST: 127.0.0.1 # When Chrome version is specified, we pin to a specific version of Chrome # Comment this out to use the latest release - CHROME_VERSION: "116.0.5845.179" + CHROME_VERSION: "116.0.5845.187-1" strategy: # Create a matrix of Node versions to test against (in parallel) matrix: From 9a874fafe1fae1354b394e33ae47e1fba9ecfc0d Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 4 Oct 2023 20:07:39 +0200 Subject: [PATCH 146/195] Release DSpace-CRIS 2023.01.01 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c85516b4020..7a9e5ed2edb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dspace-angular", - "version": "2023.01.01-SNAPSHOT", + "version": "2023.01.01" "scripts": { "ng": "ng", "config:watch": "nodemon", From 4cd0bfa78554029951f6045ed08df7200014262d Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 4 Oct 2023 20:10:17 +0200 Subject: [PATCH 147/195] Prepare next development iteration --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a9e5ed2edb..0f3b83c87de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dspace-angular", - "version": "2023.01.01" + "version": "2023.02.00-SNAPSHOT" "scripts": { "ng": "ng", "config:watch": "nodemon", From 81a41dd07a6db75aff41dd6a9446cfd406759f9c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 5 Oct 2023 10:57:55 +0200 Subject: [PATCH 148/195] [DSC-1120] fix package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f3b83c87de..a80ad477a35 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dspace-angular", - "version": "2023.02.00-SNAPSHOT" + "version": "2023.02.00-SNAPSHOT", "scripts": { "ng": "ng", "config:watch": "nodemon", From 7d59f3c87fa6d7acdc734a20a5a1058568841141 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 11:10:20 +0200 Subject: [PATCH 149/195] [CST-10703] unit tests --- .../core/eperson/eperson-data.service.spec.ts | 16 +++ ...-email-confirmation-page-routing.module.ts | 4 +- .../external-login-page-routing.module.ts | 4 +- .../external-login-page.component.ts | 10 +- ...review-account-info-page-routing.module.ts | 18 +-- ...review-account-info-page.component.spec.ts | 1 - ...ogin-review-account-info-page.component.ts | 11 +- .../helpers/review-account.guard.spec.ts | 105 +++++++------- .../review-account-info.component.spec.ts | 57 ++------ .../confirm-email.component.spec.ts | 135 ++++++++++++++++-- .../confirm-email/confirm-email.component.ts | 15 +- .../provide-email.component.spec.ts | 1 - .../provide-email/provide-email.component.ts | 8 +- .../external-log-in.component.html | 2 +- .../external-log-in.component.spec.ts | 35 ++--- .../registration-data.resolver.spec.ts | 33 ++++- .../services/external-login.service.spec.ts | 62 +++++++- 17 files changed, 347 insertions(+), 170 deletions(-) diff --git a/src/app/core/eperson/eperson-data.service.spec.ts b/src/app/core/eperson/eperson-data.service.spec.ts index b4b939eebf4..30ec39aa0d3 100644 --- a/src/app/core/eperson/eperson-data.service.spec.ts +++ b/src/app/core/eperson/eperson-data.service.spec.ts @@ -28,6 +28,7 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { createPaginatedList, createRequestEntry$ } from '../../shared/testing/utils.test'; import { CoreState } from '../core-state.model'; import { FindListOptions } from '../data/find-list-options.model'; +import { RemoteData } from '../data/remote-data'; describe('EPersonDataService', () => { let service: EPersonDataService; @@ -314,6 +315,21 @@ describe('EPersonDataService', () => { }); }); + describe('mergeEPersonDataWithToken', () => { + const uuid = '1234-5678-9012-3456'; + const token = 'abcd-efgh-ijkl-mnop'; + const metadataKey = 'eperson.firstname'; + beforeEach(() => { + spyOn(service, 'mergeEPersonDataWithToken').and.returnValue(createSuccessfulRemoteDataObject$(EPersonMock)); + }); + + it('should merge EPerson data with token', () => { + service.mergeEPersonDataWithToken(uuid, token, metadataKey).subscribe((result: RemoteData) => { + expect(result.hasSucceeded).toBeTrue(); + }); + expect(service.mergeEPersonDataWithToken).toHaveBeenCalledWith(uuid, token, metadataKey); + }); + }); }); class DummyChangeAnalyzer implements ChangeAnalyzer { diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts index e242d9ea1a9..0033d1620e5 100644 --- a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page-routing.module.ts @@ -12,6 +12,6 @@ const routes: Routes = [ @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + exports: [RouterModule], }) -export class ExternalLoginEmailConfirmationPageRoutingModule { } +export class ExternalLoginEmailConfirmationPageRoutingModule {} diff --git a/src/app/external-login-page/external-login-page-routing.module.ts b/src/app/external-login-page/external-login-page-routing.module.ts index f7425b63d82..21bbb7ee920 100644 --- a/src/app/external-login-page/external-login-page-routing.module.ts +++ b/src/app/external-login-page/external-login-page-routing.module.ts @@ -17,6 +17,6 @@ const routes: Routes = [ @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], - providers: [] + providers: [], }) -export class ExternalLoginPageRoutingModule { } +export class ExternalLoginPageRoutingModule {} diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index fd76a047ba5..27e08e4e5ae 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -2,9 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { hasNoValue } from '../shared/empty.util'; import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; -import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; import { AlertType } from '../shared/alert/aletr-type'; -import { Observable, first, map, of, tap } from 'rxjs'; +import { Observable, first, map, tap } from 'rxjs'; @Component({ templateUrl: './external-login-page.component.html', @@ -42,12 +41,5 @@ export class ExternalLoginPageComponent implements OnInit { first(), tap((data) => this.hasErrors = hasNoValue(data.registrationData)), map((data) => data.registrationData)); - - // TODO: remove this line (temporary) - // this.registrationData$ = of( - // mockRegistrationDataModel - // ); - // this.hasErrors = false; - // this.token = '1234567890'; } } diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts index b83816ece02..1119c9fc49b 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts @@ -4,18 +4,18 @@ import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-re import { RegistrationDataResolver } from '../shared/external-log-in-complete/resolvers/registration-data.resolver'; import { ReviewAccountGuard } from './helpers/review-account.guard'; - const routes: Routes = [ { - path: '', - pathMatch: 'full', - component: ExternalLoginReviewAccountInfoPageComponent, - canActivate: [ReviewAccountGuard], - resolve: { registrationData: RegistrationDataResolver } -},]; + path: '', + pathMatch: 'full', + component: ExternalLoginReviewAccountInfoPageComponent, + canActivate: [ReviewAccountGuard], + resolve: { registrationData: RegistrationDataResolver }, + }, +]; @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + exports: [RouterModule], }) -export class ExternalLoginReviewAccountInfoRoutingModule { } +export class ExternalLoginReviewAccountInfoRoutingModule {} diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts index 7622f081d0d..ca65204e8dd 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts @@ -1,7 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; import { of } from 'rxjs'; -import { AlertType } from '../shared/alert/aletr-type'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts index 62da6995fa1..6e0cab84594 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts @@ -1,10 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { AlertType } from '../shared/alert/aletr-type'; -import { Observable, first, map, of, tap } from 'rxjs'; +import { Observable, first, map, tap } from 'rxjs'; import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; import { ActivatedRoute } from '@angular/router'; import { hasNoValue } from '../shared/empty.util'; -import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; @Component({ templateUrl: './external-login-review-account-info-page.component.html', @@ -36,17 +35,9 @@ export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { this.token = this.arouter.snapshot.queryParams.token; } - ngOnInit(): void { this.registrationData$ = this.arouter.data.pipe(first(), tap((data) => this.hasErrors = hasNoValue(data?.registrationData)), map((data) => data.registrationData)); - - // TODO: remove this line (temporary) - // this.registrationData$ = of( - // mockRegistrationDataModel - // ); - // this.hasErrors = false; - // this.token = '1234567890'; } } diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts index 61cf3f178cc..03e8d53343c 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts @@ -1,79 +1,78 @@ import { TestBed } from '@angular/core/testing'; import { ReviewAccountGuard } from './review-account.guard'; import { ActivatedRoute, convertToParamMap, Params, Router } from '@angular/router'; -import { of as observableOf } from 'rxjs'; -import { AuthService } from 'src/app/core/auth/auth.service'; -import { EpersonRegistrationService } from 'src/app/core/data/eperson-registration.service'; -import { EPerson } from 'src/app/core/eperson/models/eperson.model'; -import { Registration } from 'src/app/core/shared/registration.model'; -import { RouterMock } from 'src/app/shared/mocks/router.mock'; -import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils'; +import { of as observableOf, of } from 'rxjs'; +import { AuthService } from '../../core/auth/auth.service'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RouterMock } from '../../shared/mocks/router.mock'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; +import { AuthMethodType } from '../../core/auth/models/auth.method-type'; describe('ReviewAccountGuard', () => { let guard: ReviewAccountGuard; + let epersonRegistrationService: any; + let authService: any; + const route = new RouterMock(); - const registrationWithGroups = Object.assign(new Registration(), + const registrationMock = Object.assign(new RegistrationData(), { email: 'test@email.org', - token: 'test-token', + registrationType: AuthMethodType.Validation + }); - const epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { - searchRegistrationByToken: createSuccessfulRemoteDataObject$(registrationWithGroups) - }); - const authService = { - getAuthenticatedUserFromStore: () => observableOf(ePerson), - setRedirectUrl: () => { - return true; - } - } as any; - const ePerson = Object.assign(new EPerson(), { - id: 'test-eperson', - uuid: 'test-eperson' - }); + beforeEach(() => { const paramObject: Params = {}; paramObject.token = '1234'; + epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { + searchRegistrationByToken: createSuccessfulRemoteDataObject$(registrationMock) + }); + authService = { + isAuthenticated: () => observableOf(true) + } as any; TestBed.configureTestingModule({ - providers: [{provide: Router, useValue: route}, - { - provide: ActivatedRoute, - useValue: { - queryParamMap: observableOf(convertToParamMap(paramObject)) - }, + providers: [{ provide: Router, useValue: route }, + { + provide: ActivatedRoute, + useValue: { + queryParamMap: observableOf(convertToParamMap(paramObject)) }, - {provide: EpersonRegistrationService, useValue: epersonRegistrationService}, - {provide: AuthService, useValue: authService} + }, + { provide: EpersonRegistrationService, useValue: epersonRegistrationService }, + { provide: AuthService, useValue: authService } ] }); - guard = TestBed.get(ReviewAccountGuard); + guard = TestBed.inject(ReviewAccountGuard); }); it('should be created', () => { expect(guard).toBeTruthy(); }); - describe('based on the response of "searchByToken have', () => { - it('can activate must return true when registration data includes groups', () => { - (guard.canActivate({ params: { token: '123456789' } } as any, {} as any) as any) - .subscribe( - (canActivate) => { - expect(canActivate).toEqual(true); - } - ); - }); - it('can activate must return false when registration data includes groups', () => { - const registrationWithDifferentUsedFromLoggedInt = Object.assign(new Registration(), - { - email: 't1@email.org', - token: 'test-token', - }); - epersonRegistrationService.searchRegistrationByToken.and.returnValue(observableOf(registrationWithDifferentUsedFromLoggedInt)); - (guard.canActivate({ params: { token: '123456789' } } as any, {} as any) as any) - .subscribe( - (canActivate) => { - expect(canActivate).toEqual(false); - } - ); + + it('can activate must return true when registration type is validation', () => { + (guard.canActivate({ params: { token: 'valid token' } } as any, {} as any) as any) + .subscribe( + (canActivate) => { + expect(canActivate).toEqual(true); + } + ); + }); + + it('should navigate to 404 if the registration search fails', () => { + epersonRegistrationService.searchRegistrationByToken.and.returnValue(createFailedRemoteDataObject$()); + (guard.canActivate({ params: { token: 'invalid-token' } } as any, {} as any) as any).subscribe((result) => { + expect(result).toBeFalse(); + expect(route.navigate).toHaveBeenCalledWith(['/404']); }); + }); + it('should navigate to 404 if the registration type is not validation and the user is not authenticated', () => { + registrationMock.registrationType = AuthMethodType.Password; + epersonRegistrationService.searchRegistrationByToken.and.returnValue(createSuccessfulRemoteDataObject$(registrationMock)); + spyOn(authService, 'isAuthenticated').and.returnValue(of(false)); + (guard.canActivate({ params: { token: 'invalid-token' } } as any, {} as any) as any).subscribe((result) => { + expect(route.navigate).toHaveBeenCalledWith(['/404']); + }); }); }); diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts index 7e1d3f08de3..70185c37543 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts @@ -100,7 +100,6 @@ describe('ReviewAccountInfoComponent', () => { registrationDataMock ); component.registrationToken = 'test-token'; - spyOn(router, 'navigate'); fixture.detectChanges(); }); @@ -108,12 +107,6 @@ describe('ReviewAccountInfoComponent', () => { expect(component).toBeTruthy(); }); - it('should call getEPersonData when ngOnInit is called', () => { - spyOn(component, 'getEPersonData'); - component.ngOnInit(); - expect(component.getEPersonData).toHaveBeenCalled(); - }); - it('should prepare data to compare', () => { component.ngOnInit(); const dataToCompare = component.dataToCompare; @@ -125,16 +118,6 @@ describe('ReviewAccountInfoComponent', () => { expect(dataToCompare[0].receivedValue).toBe('test@test.com'); }); - it('should get EPerson data', fakeAsync(() => { - spyOn(ePersonDataServiceStub, 'findById').and.returnValue( - of({ payload: mockEPerson } as RemoteData) - ); - component.getEPersonData(); - tick(); - expect(ePersonDataServiceStub.findById).toHaveBeenCalledWith(registrationDataMock.user); - expect(component.epersonCurrentData).toEqual(EPersonMock); - })); - it('should update dataToCompare when overrideValue is changed', () => { component.onOverrideChange(true, 'email'); expect(component.dataToCompare[0].overrideValue).toBe(true); @@ -173,30 +156,6 @@ describe('ReviewAccountInfoComponent', () => { expect(router.navigate).toHaveBeenCalledWith(['/profile']); })); - it('should merge EPerson data with token when overrideValue is false', fakeAsync(() => { - spyOn(ePersonDataServiceStub, 'mergeEPersonDataWithToken').and.returnValue( - of({ hasSucceeded: true }) - ); - component.mergeEPersonDataWithToken(); - tick(); - expect(ePersonDataServiceStub.mergeEPersonDataWithToken).toHaveBeenCalledTimes(1); - expect(router.navigate).toHaveBeenCalledWith(['/profile']); - })); - - - it('should unsubscribe from subscriptions when ngOnDestroy is called', () => { - const subscription1 = jasmine.createSpyObj('Subscription', [ - 'unsubscribe', - ]); - const subscription2 = jasmine.createSpyObj('Subscription', [ - 'unsubscribe', - ]); - component.subs = [subscription1, subscription2]; - component.ngOnDestroy(); - expect(subscription1.unsubscribe).toHaveBeenCalled(); - expect(subscription2.unsubscribe).toHaveBeenCalled(); - }); - it('should display registration data', () => { const registrationTypeElement: HTMLElement = fixture.nativeElement.querySelector('tbody tr:first-child th'); const netIdElement: HTMLElement = fixture.nativeElement.querySelector('tbody tr:first-child td'); @@ -214,10 +173,9 @@ describe('ReviewAccountInfoComponent', () => { const firstDataLabel: HTMLElement = firstDataRow.querySelector('th'); const firstDataReceivedValue: HTMLElement = firstDataRow.querySelectorAll('td')[0]; const firstDataOverrideSwitch: HTMLElement = firstDataRow.querySelector('ui-switch'); - expect(firstDataLabel.textContent.trim()).toBe('Lastname'); expect(firstDataReceivedValue.textContent.trim()).toBe('Doe'); - expect(firstDataOverrideSwitch).not.toBeNull(); + expect(firstDataOverrideSwitch).toBeNull(); }); it('should trigger onSave() when the button is clicked', () => { @@ -227,6 +185,19 @@ describe('ReviewAccountInfoComponent', () => { expect(component.onSave).toHaveBeenCalled(); }); + it('should unsubscribe from subscriptions when ngOnDestroy is called', () => { + const subscription1 = jasmine.createSpyObj('Subscription', [ + 'unsubscribe', + ]); + const subscription2 = jasmine.createSpyObj('Subscription', [ + 'unsubscribe', + ]); + component.subs = [subscription1, subscription2]; + component.ngOnDestroy(); + expect(subscription1.unsubscribe).toHaveBeenCalled(); + expect(subscription2.unsubscribe).toHaveBeenCalled(); + }); + afterEach(() => { fixture.destroy(); }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts index f26593040f9..1a43180bbc0 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -2,46 +2,159 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfirmEmailComponent } from './confirm-email.component'; import { FormBuilder } from '@angular/forms'; -import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; import { CommonModule } from '@angular/common'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; -import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; +import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { ExternalLoginService } from '../../services/external-login.service'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { EPersonDataService } from 'src/app/core/eperson/eperson-data.service'; +import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; +import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; +import { RegistrationData } from '../../models/registration-data.model'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils'; +import { EPerson } from 'src/app/core/eperson/models/eperson.model'; +import { Router } from '@angular/router'; +import { RouterMock } from 'src/app/shared/mocks/router.mock'; +import { of } from 'rxjs'; describe('ConfirmEmailComponent', () => { let component: ConfirmEmailComponent; let fixture: ComponentFixture; + let externalLoginServiceSpy: jasmine.SpyObj; + let epersonDataServiceSpy: jasmine.SpyObj; + let notificationServiceSpy: jasmine.SpyObj; + let authServiceSpy: jasmine.SpyObj; + let router; + const translateServiceStub = { + get: () => of(''), + onLangChange: new EventEmitter(), + onTranslationChange: new EventEmitter(), + onDefaultLangChange: new EventEmitter() + }; beforeEach(async () => { + externalLoginServiceSpy = jasmine.createSpyObj('ExternalLoginService', [ + 'patchUpdateRegistration', + ]); + epersonDataServiceSpy = jasmine.createSpyObj('EPersonDataService', [ + 'createEPersonForToken', + ]); + notificationServiceSpy = jasmine.createSpyObj('NotificationsService', [ + 'error', + ]); + authServiceSpy = jasmine.createSpyObj('AuthService', ['setRedirectUrl']); + router = new RouterMock(); await TestBed.configureTestingModule({ - declarations: [ ConfirmEmailComponent ], + declarations: [ConfirmEmailComponent], providers: [ FormBuilder, - { provide: EpersonRegistrationService, useValue: {} }, - { provide: ExternalLoginService, useValue: {} }, + { provide: ExternalLoginService, useValue: externalLoginServiceSpy }, + { provide: EPersonDataService, useValue: epersonDataServiceSpy }, + { provide: NotificationsService, useValue: notificationServiceSpy }, + { provide: AuthService, useValue: authServiceSpy }, + { provide: Router, useValue: router }, + { provide: TranslateService, useValue: translateServiceStub } ], imports: [ CommonModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, - useClass: TranslateLoaderMock - } + useClass: TranslateLoaderMock, + }, }), ], - schemas: [NO_ERRORS_SCHEMA] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(ConfirmEmailComponent); component = fixture.componentInstance; + component.registrationData = Object.assign(new RegistrationData(), { + id: '123', + email: 'test@example.com', + registrationMetadata: {}, + registrationType: AuthMethodType.Orcid, + }); + component.token = 'test-token'; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + describe('submitForm', () => { + it('should call postCreateAccountFromToken if email is confirmed', () => { + component.emailForm.setValue({ email: 'test@example.com' }); + spyOn(component as any, 'postCreateAccountFromToken'); + component.submitForm(); + expect((component as any).postCreateAccountFromToken).toHaveBeenCalledWith( + 'test-token', + component.registrationData + ); + }); + + it('should call patchUpdateRegistration if email is not confirmed', () => { + component.emailForm.setValue({ email: 'new-email@example.com' }); + spyOn(component as any, 'patchUpdateRegistration'); + component.submitForm(); + expect((component as any).patchUpdateRegistration).toHaveBeenCalledWith([ + 'new-email@example.com', + ]); + }); + + it('should not call any methods if form is invalid', () => { + component.emailForm.setValue({ email: 'invalid-email' }); + spyOn(component as any, 'postCreateAccountFromToken'); + spyOn(component as any, 'patchUpdateRegistration'); + component.submitForm(); + expect((component as any).postCreateAccountFromToken).not.toHaveBeenCalled(); + expect((component as any).patchUpdateRegistration).not.toHaveBeenCalled(); + }); + }); + + describe('postCreateAccountFromToken', () => { + it('should call epersonDataService.createEPersonForToken with correct arguments', () => { + epersonDataServiceSpy.createEPersonForToken.and.returnValue(createSuccessfulRemoteDataObject$(new EPerson())); + (component as any).postCreateAccountFromToken( + 'test-token', + component.registrationData + ); + expect(epersonDataServiceSpy.createEPersonForToken).toHaveBeenCalledWith( + jasmine.any(Object), + 'test-token' + ); + }); + + it('should show error notification if user creation fails', () => { + epersonDataServiceSpy.createEPersonForToken.and.returnValue( + createFailedRemoteDataObject$() + ); + (component as any).postCreateAccountFromToken( + 'test-token', + component.registrationData + ); + expect(notificationServiceSpy.error).toHaveBeenCalled(); + }); + + it('should redirect to login page if user creation succeeds', () => { + epersonDataServiceSpy.createEPersonForToken.and.returnValue( + createSuccessfulRemoteDataObject$(new EPerson()) + ); + (component as any).postCreateAccountFromToken( + 'test-token', + component.registrationData + ); + expect((component as any).router.navigate).toHaveBeenCalledWith(['/login'], { + queryParams: { authMethod: 'orcid' }, + }); + }); + }); + + afterEach(() => { + fixture.destroy(); + }); }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 45683aaee5b..449870f420b 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -53,6 +53,12 @@ export class ConfirmEmailComponent implements OnDestroy { }); } + + /** + * Submits the email form and performs appropriate actions based on the form's validity and user input. + * If the form is valid and the confirmed email matches the registration email, calls the postCreateAccountFromToken method with the token and registration data. + * If the form is valid but the confirmed email does not match the registration email, calls the patchUpdateRegistration method with the confirmed email. + */ submitForm() { this.emailForm.markAllAsTouched(); if (this.emailForm.valid) { @@ -65,6 +71,11 @@ export class ConfirmEmailComponent implements OnDestroy { } } + /** + * Sends a PATCH request to update the user's registration with the given values. + * @param values - The values to update the user's registration with. + * @returns An Observable that emits the updated registration data. + */ private patchUpdateRegistration(values: string[]) { this.subs.push( this.externalLoginService.patchUpdateRegistration(values, 'email', this.registrationData.id, this.token, 'replace') @@ -111,8 +122,8 @@ export class ConfirmEmailComponent implements OnDestroy { this.translate.get('external-login-page.provide-email.create-account.notifications.error.content') ); } else if (rd.hasSucceeded) { - // redirect to ORCID login page - // set Redirect URL to User profile + // redirect to login page with authMethod query param, so that the login page knows which authentication method to use + // set Redirect URL to User profile, so the user is redirected to the profile page after logging in this.router.navigate(['/login'], { queryParams: { authMethod: registrationData.registrationType } }); this.authService.setRedirectUrl('/review-account'); } diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts index 71d56e73cd1..3ccb0a189a5 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts @@ -6,7 +6,6 @@ import { CommonModule } from '@angular/common'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { EpersonRegistrationService } from '../../../../core/data/eperson-registration.service'; import { ExternalLoginService } from '../../services/external-login.service'; describe('ProvideEmailComponent', () => { diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts index 95e7a41ae91..92faf10e756 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts @@ -25,7 +25,9 @@ export class ProvideEmailComponent implements OnDestroy { * The token from the URL */ @Input() token: string; - + /** + * The subscriptions to unsubscribe from + */ subs: Subscription[] = []; constructor( @@ -37,6 +39,10 @@ export class ProvideEmailComponent implements OnDestroy { }); } + /** + * Updates the user's email in the registration data. + * @returns void + */ submitForm() { this.emailForm.markAllAsTouched(); if (this.emailForm.valid) { diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html index bc96b37844d..2bd3b736774 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html @@ -21,7 +21,7 @@

{{ 'external-login.confirmation.header' | translate}}

or

-
diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts index cbd8beeb1bb..f34a7883d1b 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts @@ -18,7 +18,7 @@ import { RegistrationData } from '../models/registration-data.model'; describe('ExternalLogInComponent', () => { let component: ExternalLogInComponent; let fixture: ComponentFixture; - let compiledTemplate: HTMLElement; + let modalService: NgbModal = jasmine.createSpyObj('modalService', ['open']); const registrationDataMock = { id: '3', @@ -53,13 +53,7 @@ describe('ExternalLogInComponent', () => { { provide: TranslateService, useValue: translateServiceStub }, { provide: Injector, useValue: {} }, { provide: AuthService, useValue: new AuthServiceMock() }, - { - provide: NgbModal, useValue: { - open: () => { - /*comment*/ - } - } - }, + { provide: NgbModal, useValue: modalService }, FormBuilder ], imports: [ @@ -80,7 +74,6 @@ describe('ExternalLogInComponent', () => { component = fixture.componentInstance; component.registrationData = Object.assign(new RegistrationData, registrationDataMock); component.registrationType = registrationDataMock.registrationType; - compiledTemplate = fixture.nativeElement; fixture.detectChanges(); }); @@ -88,31 +81,27 @@ describe('ExternalLogInComponent', () => { expect(component).toBeTruthy(); }); + beforeEach(() => { + component.registrationData = Object.assign(new RegistrationData(), registrationDataMock, { email: 'user@institution.edu' }); + fixture.detectChanges(); + }); + it('should set registrationType and informationText correctly when email is present', () => { expect(component.registrationType).toBe(registrationDataMock.registrationType); expect(component.informationText).toBeDefined(); }); it('should render the template to confirm email when registrationData has email', () => { - component.registrationData = Object.assign(new RegistrationData(), registrationDataMock, { email: 'email@domain.com' }); - fixture.detectChanges(); - const selector = compiledTemplate.querySelector('ds-confirm-email'); - expect(selector).toBeTruthy(); - }); - - it('should display provide email component if email is not provided', () => { - component.registrationData.email = null; - fixture.detectChanges(); + const selector = fixture.nativeElement.querySelector('ds-confirm-email'); const provideEmail = fixture.nativeElement.querySelector('ds-provide-email'); - expect(provideEmail).toBeTruthy(); + expect(selector).toBeTruthy(); + expect(provideEmail).toBeNull(); }); it('should display login modal when connect to existing account button is clicked', () => { - const button = fixture.nativeElement.querySelector('button'); + const button = fixture.nativeElement.querySelector('button.btn-primary'); button.click(); - fixture.detectChanges(); - const modal = fixture.nativeElement.querySelector('.modal'); - expect(modal).toBeTruthy(); + expect(modalService.open).toHaveBeenCalled(); }); it('should render the template with the translated informationText', () => { diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts index 24d40f11533..a5ad2df9d43 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts +++ b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts @@ -1,16 +1,47 @@ import { TestBed } from '@angular/core/testing'; import { RegistrationDataResolver } from './registration-data.resolver'; +import { EpersonRegistrationService } from 'src/app/core/data/eperson-registration.service'; +import { Registration } from 'src/app/core/shared/registration.model'; +import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { RegistrationData } from '../models/registration-data.model'; +import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; describe('RegistrationDataResolver', () => { let resolver: RegistrationDataResolver; + let epersonRegistrationServiceSpy: jasmine.SpyObj; + const registrationMock = Object.assign(new Registration(), { + email: 'test@user.com', + }); beforeEach(() => { - TestBed.configureTestingModule({}); + const spy = jasmine.createSpyObj('EpersonRegistrationService', ['searchByTokenAndUpdateData']); + + TestBed.configureTestingModule({ + providers: [ + RegistrationDataResolver, + { provide: EpersonRegistrationService, useValue: spy } + ] + }); resolver = TestBed.inject(RegistrationDataResolver); + epersonRegistrationServiceSpy = TestBed.inject(EpersonRegistrationService) as jasmine.SpyObj; }); it('should be created', () => { expect(resolver).toBeTruthy(); }); + + it('should resolve registration data based on a token', () => { + const token = 'abc123'; + const registrationRD$ = createSuccessfulRemoteDataObject$(registrationMock); + epersonRegistrationServiceSpy.searchByTokenAndUpdateData.and.returnValue(registrationRD$); + + const route = new ActivatedRouteSnapshot(); + route.params = { token: token }; + const state = {} as RouterStateSnapshot; + + resolver.resolve(route, state).subscribe((result: RegistrationData) => { + expect(result).toBeDefined(); + }); + }); }); diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts b/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts index 7d1d8ac6a2c..541e71e6a5a 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts @@ -1,16 +1,76 @@ import { TestBed } from '@angular/core/testing'; import { ExternalLoginService } from './external-login.service'; +import { TranslateService } from '@ngx-translate/core'; +import { of as observableOf } from 'rxjs'; +import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Registration } from '../../../core/shared/registration.model'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { RouterMock } from '../../mocks/router.mock'; +import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Router } from '@angular/router'; describe('ExternalLoginService', () => { let service: ExternalLoginService; + let epersonRegistrationService; + let router: any; + let notificationService; + let translate; + + const values = ['value1', 'value2']; + const field = 'field1'; + const registrationId = 'registrationId1'; + const token = 'token1'; + const operation = 'add'; beforeEach(() => { - TestBed.configureTestingModule({}); + epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { + patchUpdateRegistration: createSuccessfulRemoteDataObject$(new Registration) + }); + router = new RouterMock(); + notificationService = new NotificationsServiceStub(); + translate = jasmine.createSpyObj('TranslateService', ['get']); + + TestBed.configureTestingModule({ + providers: [ + ExternalLoginService, + { provide: EpersonRegistrationService, useValue: epersonRegistrationService }, + { provide: Router, useValue: router }, + { provide: NotificationsService, useValue: notificationService }, + { provide: TranslateService, useValue: translate }, + ], + schemas: [NO_ERRORS_SCHEMA] + }); service = TestBed.inject(ExternalLoginService); }); it('should be created', () => { expect(service).toBeTruthy(); }); + + it('should call epersonRegistrationService.patchUpdateRegistration with the correct parameters', () => { + epersonRegistrationService.patchUpdateRegistration.and.returnValue(observableOf({} as RemoteData)); + service.patchUpdateRegistration(values, field, registrationId, token, operation); + expect(epersonRegistrationService.patchUpdateRegistration).toHaveBeenCalledWith(values, field, registrationId, token, operation); + }); + + it('should navigate to /email-confirmation if the remote data has succeeded', () => { + const remoteData = { hasSucceeded: true } as RemoteData; + epersonRegistrationService.patchUpdateRegistration.and.returnValue(observableOf(remoteData)); + service.patchUpdateRegistration(values, field, registrationId, token, operation).subscribe(() => { + expect((router as any).navigate).toHaveBeenCalledWith(['/email-confirmation']); + }); + }); + + it('should show an error notification if the remote data has failed', () => { + const remoteData = { hasFailed: true } as RemoteData; + epersonRegistrationService.patchUpdateRegistration.and.returnValue(observableOf(remoteData)); + translate.get.and.returnValue(observableOf('error message')); + service.patchUpdateRegistration(values, field, registrationId, token, operation).subscribe(() => { + expect(notificationService.error).toHaveBeenCalledWith('error message'); + }); + }); }); From 742a078306ff5453a9402ab956d7d10f47176c0f Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 11:22:56 +0200 Subject: [PATCH 150/195] [CST-10703] RegistrationTokenGuard fix --- .../guards/registration-token.guard.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts index 3d4d103b098..0a4a1319344 100644 --- a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts +++ b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts @@ -19,7 +19,7 @@ export class RegistrationTokenGuard implements CanActivate { constructor( private router: Router, private epersonRegistrationService: EpersonRegistrationService - ) {} + ) { } /** * Determines if a user can activate a route based on the registration token. @@ -31,20 +31,22 @@ export class RegistrationTokenGuard implements CanActivate { route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Promise | boolean | Observable { - if (route.params.token) { + if (route.queryParams.token) { return this.epersonRegistrationService .searchRegistrationByToken(route.params.token) .pipe( getFirstCompletedRemoteData(), map( - (data: RemoteData) => - data.hasSucceeded && hasValue(data) - ), - tap((isValid: boolean) => { - if (!isValid) { - this.router.navigate(['/404']); + (data: RemoteData) => { + // TODO: remove console.log + console.log(data, 'RegistrationTokenGuard'); + if (data.hasSucceeded && hasValue(data)) { + return true; + } else { + this.router.navigate(['/404']); + } } - }) + ) ); } else { this.router.navigate(['/404']); From ae875a860e55d16fcf13b864795386624afa3999 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 11:26:25 +0200 Subject: [PATCH 151/195] [CST-10703] fix --- .../external-log-in-complete/guards/registration-token.guard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts index 0a4a1319344..1296385602a 100644 --- a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts +++ b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts @@ -33,7 +33,7 @@ export class RegistrationTokenGuard implements CanActivate { ): Promise | boolean | Observable { if (route.queryParams.token) { return this.epersonRegistrationService - .searchRegistrationByToken(route.params.token) + .searchRegistrationByToken(route.queryParams.token) .pipe( getFirstCompletedRemoteData(), map( From 03ea663604414a697d11acc256b15e52b7152fb5 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 11:28:59 +0200 Subject: [PATCH 152/195] [CST-10703] small fixes --- .../helpers/review-account.guard.ts | 4 ++-- .../resolvers/registration-data.resolver.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.ts index c979e3bfc2c..5a1eb8b1c2d 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.ts +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.ts @@ -36,9 +36,9 @@ export class ReviewAccountGuard implements CanActivate { route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Promise | boolean | Observable { - if (route.params.token) { + if (route.queryParams.token) { return this.epersonRegistrationService - .searchRegistrationByToken(route.params.token) + .searchRegistrationByToken(route.queryParams.token) .pipe( getFirstCompletedRemoteData(), mergeMap( diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts index 6e2a9dc9fc8..82d0bdc68b7 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts +++ b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts @@ -33,7 +33,7 @@ export class RegistrationDataResolver implements Resolve { * @returns An Observable of RegistrationData. */ resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const token = route.params.token; + const token = route.queryParams.token; if (hasValue(token)) { return this.epersonRegistrationService.searchByTokenAndUpdateData(token).pipe( getFirstCompletedRemoteData(), From 9518e3d267296e921874123c67640e1537ce7467 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 12:46:50 +0200 Subject: [PATCH 153/195] [CST-10703] extend Registration model --- src/app/core/auth/models/auth.method-type.ts | 1 - .../auth/models/auth.registration-type.ts | 7 ++ src/app/core/core.module.ts | 2 - src/app/core/shared/registration.model.ts | 31 +++++++ .../external-login-page.component.spec.ts | 4 +- .../external-login-page.component.ts | 9 ++- ...ogin-review-account-info-page.component.ts | 13 +-- .../helpers/review-account.guard.spec.ts | 10 +-- .../helpers/review-account.guard.ts | 20 +++-- .../review-account-info.component.spec.ts | 4 +- .../review-account-info.component.ts | 14 ++-- .../confirm-email.component.spec.ts | 18 ++--- .../confirm-email/confirm-email.component.ts | 6 +- .../external-log-in.methods-decorator.ts | 6 +- .../external-log-in.component.spec.ts | 10 +-- .../external-log-in.component.ts | 8 +- .../external-login-method-entry.component.ts | 6 +- .../guards/registration-token.guard.ts | 4 +- .../models/registration-data.mock.model.ts | 8 +- .../models/registration-data.model.ts | 80 ------------------- .../models/registration-data.resource-type.ts | 9 --- .../orcid-confirmation.component.ts | 10 +-- .../registration-data.resolver.spec.ts | 7 +- .../resolvers/registration-data.resolver.ts | 26 +++--- 24 files changed, 126 insertions(+), 187 deletions(-) create mode 100644 src/app/core/auth/models/auth.registration-type.ts delete mode 100644 src/app/shared/external-log-in-complete/models/registration-data.model.ts delete mode 100644 src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts diff --git a/src/app/core/auth/models/auth.method-type.ts b/src/app/core/auth/models/auth.method-type.ts index e5b4e0a194c..cc19b5ab94c 100644 --- a/src/app/core/auth/models/auth.method-type.ts +++ b/src/app/core/auth/models/auth.method-type.ts @@ -6,5 +6,4 @@ export enum AuthMethodType { X509 = 'x509', Oidc = 'oidc', Orcid = 'orcid', - Validation = 'validation', } diff --git a/src/app/core/auth/models/auth.registration-type.ts b/src/app/core/auth/models/auth.registration-type.ts new file mode 100644 index 00000000000..e43094ed7bc --- /dev/null +++ b/src/app/core/auth/models/auth.registration-type.ts @@ -0,0 +1,7 @@ +export enum AuthRegistrationType { + Password = 'password', + Shibboleth = 'shibboleth', + Oidc = 'oidc', + Orcid = 'ORCID', + Validation = 'validation', +} diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index c75ce8e65c9..1c6b20285a8 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -232,7 +232,6 @@ import { import { ProductDatasetSchemaType } from './metadata/schema-json-ld/schema-types/product/product-dataset-schema-type'; import { PersonSchemaType } from './metadata/schema-json-ld/schema-types/Person/person-schema-type'; import {ItemRequest} from './shared/item-request.model'; -import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -472,7 +471,6 @@ export const models = LoginStatistics, Metric, ItemRequest, - RegistrationData, ]; @NgModule({ diff --git a/src/app/core/shared/registration.model.ts b/src/app/core/shared/registration.model.ts index bc4488964f2..5a3a5d06563 100644 --- a/src/app/core/shared/registration.model.ts +++ b/src/app/core/shared/registration.model.ts @@ -1,11 +1,26 @@ +// eslint-disable-next-line max-classes-per-file import { typedObject } from '../cache/builders/build-decorators'; import { ResourceType } from './resource-type'; import { REGISTRATION } from './registration.resource-type'; import { UnCacheableObject } from './uncacheable-object.model'; +import { MetadataValue } from './metadata.models'; +import { AuthRegistrationType } from '../auth/models/auth.registration-type'; +export class RegistrationDataMetadataMap { + [key: string]: RegistrationDataMetadataValue[]; +} +export class RegistrationDataMetadataValue extends MetadataValue { + overrides?: string; +} @typedObject export class Registration implements UnCacheableObject { static type = REGISTRATION; + + /** + * The unique identifier of this registration data + */ + id: string; + /** * The object type */ @@ -29,8 +44,24 @@ export class Registration implements UnCacheableObject { * The token linked to the registration */ groupNames: string[]; + /** * The token linked to the registration */ groups: string[]; + + /** + * The registration type (e.g. orcid, shibboleth, etc.) + */ + registrationType?: AuthRegistrationType; + + /** + * The netId of the user (e.g. for ORCID - <:orcid>) + */ + netId?: string; + + /** + * The metadata involved during the registration process + */ + registrationMetadata?: RegistrationDataMetadataMap; } diff --git a/src/app/external-login-page/external-login-page.component.spec.ts b/src/app/external-login-page/external-login-page.component.spec.ts index c705b131b3e..8dee4821a7a 100644 --- a/src/app/external-login-page/external-login-page.component.spec.ts +++ b/src/app/external-login-page/external-login-page.component.spec.ts @@ -3,10 +3,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginPageComponent } from './external-login-page.component'; import { ActivatedRoute } from '@angular/router'; import { of } from 'rxjs'; -import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; import { CommonModule } from '@angular/common'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; +import { Registration } from '../core/shared/registration.model'; describe('ExternalLoginPageComponent', () => { let component: ExternalLoginPageComponent; @@ -75,7 +75,7 @@ describe('ExternalLoginPageComponent', () => { }); it('should display the DsExternalLogIn component when there are no errors', () => { - const registrationData = Object.assign(new RegistrationData(), registrationDataMock); + const registrationData = Object.assign(new Registration(), registrationDataMock); component.registrationData$ = of(registrationData); component.token = '1234567890'; component.hasErrors = false; diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index 27e08e4e5ae..1e3862bcd2f 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -1,9 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { hasNoValue } from '../shared/empty.util'; -import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; import { AlertType } from '../shared/alert/aletr-type'; import { Observable, first, map, tap } from 'rxjs'; +import { Registration } from '../core/shared/registration.model'; +import { RemoteData } from '../core/data/remote-data'; @Component({ templateUrl: './external-login-page.component.html', @@ -19,7 +20,7 @@ export class ExternalLoginPageComponent implements OnInit { /** * The registration data of the user. */ - public registrationData$: Observable; + public registrationData$: Observable; /** * The type of alert to show. */ @@ -39,7 +40,7 @@ export class ExternalLoginPageComponent implements OnInit { ngOnInit(): void { this.registrationData$ = this.arouter.data.pipe( first(), - tap((data) => this.hasErrors = hasNoValue(data.registrationData)), - map((data) => data.registrationData)); + tap((data) => this.hasErrors = (data.registrationData as RemoteData).hasFailed), + map((data) => (data.registrationData as RemoteData).payload)); } } diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts index 6e0cab84594..dedb3d9baa1 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts @@ -1,9 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { AlertType } from '../shared/alert/aletr-type'; import { Observable, first, map, tap } from 'rxjs'; -import { RegistrationData } from '../shared/external-log-in-complete/models/registration-data.model'; import { ActivatedRoute } from '@angular/router'; import { hasNoValue } from '../shared/empty.util'; +import { Registration } from '../core/shared/registration.model'; +import { RemoteData } from '../core/data/remote-data'; @Component({ templateUrl: './external-login-review-account-info-page.component.html', @@ -23,7 +24,7 @@ export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { /** * The registration data of the user */ - public registrationData$: Observable; + public registrationData$: Observable; /** * Whether the component has errors */ @@ -36,8 +37,10 @@ export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { } ngOnInit(): void { - this.registrationData$ = this.arouter.data.pipe(first(), - tap((data) => this.hasErrors = hasNoValue(data?.registrationData)), - map((data) => data.registrationData)); + this.registrationData$ = this.arouter.data.pipe( + first(), + tap((data) => this.hasErrors = (data.registrationData as RemoteData).hasFailed), + map((data) => (data.registrationData as RemoteData).payload)); } } + diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts index 03e8d53343c..9004e173204 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts @@ -6,8 +6,8 @@ import { AuthService } from '../../core/auth/auth.service'; import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; import { RouterMock } from '../../shared/mocks/router.mock'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; -import { AuthMethodType } from '../../core/auth/models/auth.method-type'; +import { Registration } from '../../core/shared/registration.model'; +import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; describe('ReviewAccountGuard', () => { let guard: ReviewAccountGuard; @@ -15,10 +15,10 @@ describe('ReviewAccountGuard', () => { let authService: any; const route = new RouterMock(); - const registrationMock = Object.assign(new RegistrationData(), + const registrationMock = Object.assign(new Registration(), { email: 'test@email.org', - registrationType: AuthMethodType.Validation + registrationType: AuthRegistrationType.Validation }); @@ -68,7 +68,7 @@ describe('ReviewAccountGuard', () => { }); it('should navigate to 404 if the registration type is not validation and the user is not authenticated', () => { - registrationMock.registrationType = AuthMethodType.Password; + registrationMock.registrationType = AuthRegistrationType.Password; epersonRegistrationService.searchRegistrationByToken.and.returnValue(createSuccessfulRemoteDataObject$(registrationMock)); spyOn(authService, 'isAuthenticated').and.returnValue(of(false)); (guard.canActivate({ params: { token: 'invalid-token' } } as any, {} as any) as any).subscribe((result) => { diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.ts index 5a1eb8b1c2d..8ae1bca6f25 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.ts +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.ts @@ -7,14 +7,13 @@ import { } from '@angular/router'; import isEqual from 'lodash/isEqual'; import { Observable, catchError, mergeMap, of, tap } from 'rxjs'; -import { AuthService } from 'src/app/core/auth/auth.service'; -import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; -import { EpersonRegistrationService } from 'src/app/core/data/eperson-registration.service'; -import { RemoteData } from 'src/app/core/data/remote-data'; -import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; -import { Registration } from 'src/app/core/shared/registration.model'; -import { hasValue } from 'src/app/shared/empty.util'; -import { RegistrationData } from 'src/app/shared/external-log-in-complete/models/registration-data.model'; +import { AuthService } from '../../core/auth/auth.service'; +import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RemoteData } from '../../core/data/remote-data'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { Registration } from '../../core/shared/registration.model'; +import { hasValue } from '../../shared/empty.util'; @Injectable({ providedIn: 'root', @@ -43,10 +42,9 @@ export class ReviewAccountGuard implements CanActivate { getFirstCompletedRemoteData(), mergeMap( (data: RemoteData) => { - if (data.hasSucceeded && hasValue(data)) { - const registrationData = Object.assign(new RegistrationData(), data.payload); + if (data.hasSucceeded && hasValue(data.payload)) { // is the registration type validation (account valid) - if (isEqual(registrationData.registrationType, AuthMethodType.Validation)) { + if (hasValue(data.payload.registrationType) && isEqual(data.payload.registrationType, AuthRegistrationType.Validation)) { return of(true); } else { return this.authService.isAuthenticated(); diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts index 70185c37543..b7663674972 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts @@ -19,9 +19,9 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { Router } from '@angular/router'; import { RouterMock } from '../../shared/mocks/router.mock'; -import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; import { EventEmitter } from '@angular/core'; import { CompareValuesPipe } from '../helpers/compare-values.pipe'; +import { Registration } from '../../core/shared/registration.model'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; @@ -96,7 +96,7 @@ describe('ReviewAccountInfoComponent', () => { fixture = TestBed.createComponent(ReviewAccountInfoComponent); component = fixture.componentInstance; component.registrationData = Object.assign( - new RegistrationData(), + new Registration(), registrationDataMock ); component.registrationToken = 'test-token'; diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index ea6b341710e..81c237b7fc2 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -7,15 +7,15 @@ import { } from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { RegistrationData } from '../../shared/external-log-in-complete/models/registration-data.model'; import { Observable, Subscription, filter, from, switchMap, take } from 'rxjs'; -import { RemoteData } from '../..//core/data/remote-data'; -import { ConfirmationModalComponent } from '../..//shared/confirmation-modal/confirmation-modal.component'; +import { RemoteData } from '../../core/data/remote-data'; +import { ConfirmationModalComponent } from '../../shared/confirmation-modal/confirmation-modal.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { hasValue } from '../..//shared/empty.util'; +import { hasValue } from '../../shared/empty.util'; import { TranslateService } from '@ngx-translate/core'; -import { NotificationsService } from '../..//shared/notifications/notifications.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; import { Router } from '@angular/router'; +import { Registration } from '../../core/shared/registration.model'; export interface ReviewAccountInfoData { label: string; @@ -39,7 +39,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { /** * User data from the registration token */ - @Input() registrationData: RegistrationData; + @Input() registrationData: Registration; /** * Text to display when the value is not applicable @@ -143,7 +143,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { } if (response.hasFailed) { - this.notificationService.success( + this.notificationService.error( this.translateService.get( 'review-account-info.merge-data.notification.error' ) diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts index 1a43180bbc0..c9c741672dd 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -7,16 +7,16 @@ import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-transla import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { ExternalLoginService } from '../../services/external-login.service'; -import { AuthService } from 'src/app/core/auth/auth.service'; -import { EPersonDataService } from 'src/app/core/eperson/eperson-data.service'; -import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; -import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; -import { RegistrationData } from '../../models/registration-data.model'; -import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils'; -import { EPerson } from 'src/app/core/eperson/models/eperson.model'; +import { AuthService } from '../../../../core/auth/auth.service'; +import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { Router } from '@angular/router'; -import { RouterMock } from 'src/app/shared/mocks/router.mock'; +import { RouterMock } from '../../../../shared/mocks/router.mock'; import { of } from 'rxjs'; +import { Registration } from '../../../../core/shared/registration.model'; describe('ConfirmEmailComponent', () => { let component: ConfirmEmailComponent; @@ -72,7 +72,7 @@ describe('ConfirmEmailComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ConfirmEmailComponent); component = fixture.componentInstance; - component.registrationData = Object.assign(new RegistrationData(), { + component.registrationData = Object.assign(new Registration(), { id: '123', email: 'test@example.com', registrationMetadata: {}, diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 449870f420b..ba5c3d3d138 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -2,7 +2,6 @@ import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/c import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../../core/shared/operators'; -import { RegistrationData } from '../../models/registration-data.model'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { hasValue } from '../../../../shared/empty.util'; import { EPerson } from '../../../../core/eperson/models/eperson.model'; @@ -13,6 +12,7 @@ import isEqual from 'lodash/isEqual'; import { AuthService } from '../../../../core/auth/auth.service'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; +import { Registration } from '../../../../core/shared/registration.model'; @Component({ selector: 'ds-confirm-email', @@ -28,7 +28,7 @@ export class ConfirmEmailComponent implements OnDestroy { /** * The registration data object */ - @Input() registrationData: RegistrationData; + @Input() registrationData: Registration; /** * The token to be used to confirm the registration @@ -99,7 +99,7 @@ export class ConfirmEmailComponent implements OnDestroy { */ private postCreateAccountFromToken( token: string, - registrationData: RegistrationData + registrationData: Registration ) { const metadataValues = {}; for (const [key, value] of Object.entries(registrationData.registrationMetadata)) { diff --git a/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts b/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts index 933edf24640..ce90aea0a3f 100644 --- a/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts +++ b/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts @@ -1,4 +1,4 @@ -import { AuthMethodType } from '../../core/auth/models/auth.method-type'; +import { AuthRegistrationType } from 'src/app/core/auth/models/auth.registration-type'; /** * Map to store the external login confirmation component for the given auth method type @@ -9,7 +9,7 @@ const authMethodsMap = new Map(); * @param authMethodType the type of the external login method */ export function renderExternalLoginConfirmationFor( - authMethodType: AuthMethodType + authMethodType: AuthRegistrationType ) { return function decorator(objectElement: any) { if (!objectElement) { @@ -23,7 +23,7 @@ export function renderExternalLoginConfirmationFor( * @param authMethodType the type of the external login method */ export function getExternalLoginConfirmationType( - authMethodType: AuthMethodType + authMethodType: AuthRegistrationType ) { return authMethodsMap.get(authMethodType); } diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts index f34a7883d1b..9e3b74f2bfe 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts @@ -12,8 +12,8 @@ import { AuthService } from '../../../core/auth/auth.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { AuthServiceMock } from '../../mocks/auth.service.mock'; import { MetadataValue } from '../../../core/shared/metadata.models'; -import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; -import { RegistrationData } from '../models/registration-data.model'; +import { Registration } from '../../../core/shared/registration.model'; +import { AuthRegistrationType } from '../../../core/auth/models/auth.registration-type'; describe('ExternalLogInComponent', () => { let component: ExternalLogInComponent; @@ -24,7 +24,7 @@ describe('ExternalLogInComponent', () => { id: '3', email: 'user@institution.edu', user: '028dcbb8-0da2-4122-a0ea-254be49ca107', - registrationType: AuthMethodType.Orcid, + registrationType: AuthRegistrationType.Orcid, netId: '0000-1111-2222-3333', registrationMetadata: { 'eperson.firstname': [ @@ -72,7 +72,7 @@ describe('ExternalLogInComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ExternalLogInComponent); component = fixture.componentInstance; - component.registrationData = Object.assign(new RegistrationData, registrationDataMock); + component.registrationData = Object.assign(new Registration, registrationDataMock); component.registrationType = registrationDataMock.registrationType; fixture.detectChanges(); }); @@ -82,7 +82,7 @@ describe('ExternalLogInComponent', () => { }); beforeEach(() => { - component.registrationData = Object.assign(new RegistrationData(), registrationDataMock, { email: 'user@institution.edu' }); + component.registrationData = Object.assign(new Registration(), registrationDataMock, { email: 'user@institution.edu' }); fixture.detectChanges(); }); diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts index 4e08d633a21..0a077405429 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts @@ -6,12 +6,12 @@ import { Injector, } from '@angular/core'; import { getExternalLoginConfirmationType } from '../external-log-in.methods-decorator'; -import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; -import { RegistrationData } from '../models/registration-data.model'; import { hasValue } from '../../empty.util'; import { TranslateService } from '@ngx-translate/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { AuthService } from '../../../core/auth/auth.service'; +import { Registration } from '../../../core/shared/registration.model'; +import { AuthRegistrationType } from '../../../core/auth/models/auth.registration-type'; @Component({ selector: 'ds-external-log-in', @@ -23,11 +23,11 @@ export class ExternalLogInComponent implements OnInit { /** * The type of registration type to be confirmed */ - registrationType: AuthMethodType; + registrationType: AuthRegistrationType; /** * The registration data object */ - @Input() registrationData: RegistrationData; + @Input() registrationData: Registration; /** * The token to be used to confirm the registration * @memberof ExternalLogInComponent diff --git a/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts b/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts index 5d03342e0f4..47158274b86 100644 --- a/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts +++ b/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { RegistrationData } from './models/registration-data.model'; +import { Registration } from '../../core/shared/registration.model'; /** * This component renders a form to complete the registration process @@ -12,10 +12,10 @@ export abstract class ExternalLoginMethodEntryComponent { /** * The registration data object */ - public registratioData: RegistrationData; + public registratioData: Registration; constructor( - @Inject('registrationDataProvider') protected injectedRegistrationDataObject: RegistrationData, + @Inject('registrationDataProvider') protected injectedRegistrationDataObject: Registration, ) { this.registratioData = injectedRegistrationDataObject; } diff --git a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts index 1296385602a..ec37d9d44e7 100644 --- a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts +++ b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts @@ -5,7 +5,7 @@ import { Router, RouterStateSnapshot, } from '@angular/router'; -import { Observable, map, of, tap } from 'rxjs'; +import { Observable, map, of } from 'rxjs'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { RemoteData } from '../../../core/data/remote-data'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; @@ -30,7 +30,7 @@ export class RegistrationTokenGuard implements CanActivate { canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot - ): Promise | boolean | Observable { + ): Observable { if (route.queryParams.token) { return this.epersonRegistrationService .searchRegistrationByToken(route.queryParams.token) diff --git a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts index fa40cb91c3b..6dc1eb28632 100644 --- a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts +++ b/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts @@ -1,9 +1,9 @@ -import { AuthMethodType } from 'src/app/core/auth/models/auth.method-type'; -import { RegistrationData } from './registration-data.model'; +import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; import { MetadataValue } from '../../../core/shared/metadata.models'; +import { Registration } from '../../../core/shared/registration.model'; -export const mockRegistrationDataModel: RegistrationData = Object.assign( - new RegistrationData(), +export const mockRegistrationDataModel: Registration = Object.assign( + new Registration(), { id: '3', email: 'user@institution.edu', diff --git a/src/app/shared/external-log-in-complete/models/registration-data.model.ts b/src/app/shared/external-log-in-complete/models/registration-data.model.ts deleted file mode 100644 index 9714d81111b..00000000000 --- a/src/app/shared/external-log-in-complete/models/registration-data.model.ts +++ /dev/null @@ -1,80 +0,0 @@ -// eslint-disable-next-line max-classes-per-file -import { CacheableObject } from '../../../core/cache/cacheable-object.model'; -import { typedObject } from '../../../core/cache/builders/build-decorators'; -import { REGISTRATION_DATA } from './registration-data.resource-type'; -import { autoserialize, deserialize } from 'cerialize'; -import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; -import { ResourceType } from '../../../core/shared/resource-type'; -import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; -import { HALLink } from '../../../core/shared/hal-link.model'; -import { MetadataValue } from '../../../core/shared/metadata.models'; - -export class RegistrationDataMetadataMap { - [key: string]: RegistrationDataMetadataValue[]; -} - -export class RegistrationDataMetadataValue extends MetadataValue { - @autoserialize - overrides?: string; -} - -/** - * Object that represents the authenticated status of a user - */ -@typedObject -export class RegistrationData implements CacheableObject { - - static type = REGISTRATION_DATA; - - /** - * The unique identifier of this registration data - */ - @autoserialize - id: string; - - /** - * The type for this RegistrationData - */ - @excludeFromEquals - @autoserialize - type: ResourceType; - - /** - * The registered email address - */ - @autoserialize - email: string; - - /** - * The registered user identifier - */ - @autoserialize - user: string; - - /** - * The registration type (e.g. orcid, shibboleth, etc.) - */ - @autoserialize - registrationType?: AuthMethodType; - - /** - * The netId of the user (e.g. for ORCID - <:orcid>) - */ - @autoserialize - netId?: string; - - - /** - * The metadata involved during the registration process - */ - @autoserialize - registrationMetadata?: RegistrationDataMetadataMap; - - /** - * The {@link HALLink}s for this RegistrationData - */ - @deserialize - _links: { - self: HALLink; - }; -} diff --git a/src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts b/src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts deleted file mode 100644 index 2a387c3ca38..00000000000 --- a/src/app/shared/external-log-in-complete/models/registration-data.resource-type.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ResourceType } from '../../../core/shared/resource-type'; - -/** - * The resource type for RegistrationData - * - * Needs to be in a separate file to prevent circular - * dependencies in webpack. - */ -export const REGISTRATION_DATA = new ResourceType('registration'); diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts index 0c93b668ec7..3cd3b875dc0 100644 --- a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts +++ b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit, ChangeDetectionStrategy, Inject } from '@angular/core'; import { renderExternalLoginConfirmationFor } from '../../external-log-in.methods-decorator'; -import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { RegistrationData } from '../../models/registration-data.model'; import { ExternalLoginMethodEntryComponent } from '../../external-login-method-entry.component'; +import { Registration } from '../../../../core/shared/registration.model'; +import { AuthRegistrationType } from '../../../../core/auth/models/auth.registration-type'; @Component({ selector: 'ds-orcid-confirmation', @@ -11,7 +11,7 @@ import { ExternalLoginMethodEntryComponent } from '../../external-login-method-e styleUrls: ['./orcid-confirmation.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -@renderExternalLoginConfirmationFor(AuthMethodType.Orcid) +@renderExternalLoginConfirmationFor(AuthRegistrationType.Orcid) export class OrcidConfirmationComponent extends ExternalLoginMethodEntryComponent implements OnInit { /** @@ -20,11 +20,11 @@ export class OrcidConfirmationComponent extends ExternalLoginMethodEntryComponen public form: FormGroup; /** - * @param injectedRegistrationDataObject RegistrationData object provided + * @param injectedRegistrationDataObject Registration object provided * @param formBuilder To build the form */ constructor( - @Inject('registrationDataProvider') protected injectedRegistrationDataObject: RegistrationData, + @Inject('registrationDataProvider') protected injectedRegistrationDataObject: Registration, private formBuilder: FormBuilder ) { super(injectedRegistrationDataObject); diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts index a5ad2df9d43..a33434062b5 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts +++ b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts @@ -1,10 +1,9 @@ import { TestBed } from '@angular/core/testing'; import { RegistrationDataResolver } from './registration-data.resolver'; -import { EpersonRegistrationService } from 'src/app/core/data/eperson-registration.service'; -import { Registration } from 'src/app/core/shared/registration.model'; +import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; +import { Registration } from '../../../core/shared/registration.model'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { RegistrationData } from '../models/registration-data.model'; import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; describe('RegistrationDataResolver', () => { @@ -40,7 +39,7 @@ describe('RegistrationDataResolver', () => { route.params = { token: token }; const state = {} as RouterStateSnapshot; - resolver.resolve(route, state).subscribe((result: RegistrationData) => { + resolver.resolve(route, state).subscribe((result: Registration) => { expect(result).toBeDefined(); }); }); diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts index 82d0bdc68b7..5513a847a43 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts +++ b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts @@ -4,13 +4,12 @@ import { RouterStateSnapshot, ActivatedRouteSnapshot, } from '@angular/router'; -import { Observable, map } from 'rxjs'; -import { EpersonRegistrationService } from 'src/app/core/data/eperson-registration.service'; +import { Observable } from 'rxjs'; +import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { hasValue } from '../../empty.util'; -import { Registration } from 'src/app/core/shared/registration.model'; -import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; -import { RemoteData } from 'src/app/core/data/remote-data'; -import { RegistrationData } from '../models/registration-data.model'; +import { Registration } from '../../../core/shared/registration.model'; +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { RemoteData } from '../../../core/data/remote-data'; @Injectable({ providedIn: 'root', @@ -18,7 +17,7 @@ import { RegistrationData } from '../models/registration-data.model'; /** * Resolver for retrieving registration data based on a token. */ -export class RegistrationDataResolver implements Resolve { +export class RegistrationDataResolver implements Resolve> { /** * Constructor for RegistrationDataResolver. @@ -30,20 +29,13 @@ export class RegistrationDataResolver implements Resolve { * Resolves registration data based on a token. * @param route The ActivatedRouteSnapshot containing the token parameter. * @param state The RouterStateSnapshot. - * @returns An Observable of RegistrationData. + * @returns An Observable of Registration. */ - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { const token = route.queryParams.token; if (hasValue(token)) { - return this.epersonRegistrationService.searchByTokenAndUpdateData(token).pipe( + return this.epersonRegistrationService.searchRegistrationByToken(token).pipe( getFirstCompletedRemoteData(), - map((registrationRD: RemoteData) => { - if (registrationRD.hasSucceeded && hasValue(registrationRD.payload)) { - return Object.assign(new RegistrationData(), registrationRD.payload); - } else { - return null; - } - }) ); } } From f472fcfa15e7ca874cd89754623d3ce59fa43307 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 5 Oct 2023 14:02:43 +0200 Subject: [PATCH 154/195] [DSC-1277] CRIS/GLAM translation sync 2 --- src/assets/i18n/it.json5 | 102 +++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 0aaff9e328d..670b44fccfd 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -469,31 +469,31 @@ // "admin.access-control.epeople.actions.delete": "Delete EPerson", - "admin.access-control.epeople.actions.delete": "Eliminare EPerson", + "admin.access-control.epeople.actions.delete": "Eliminare l'operatore", // "admin.access-control.epeople.actions.impersonate": "Impersonate EPerson", - "admin.access-control.epeople.actions.impersonate": "Impersonare EPerson", + "admin.access-control.epeople.actions.impersonate": "Loggati come ", // "admin.access-control.epeople.actions.reset": "Reset password", "admin.access-control.epeople.actions.reset": "Reimposta password", // "admin.access-control.epeople.actions.stop-impersonating": "Stop impersonating EPerson", - "admin.access-control.epeople.actions.stop-impersonating": "Smetti di impersonare EPerson", + "admin.access-control.epeople.actions.stop-impersonating": "Smetti di loggarti come", // "admin.access-control.epeople.breadcrumbs": "EPeople", - "admin.access-control.epeople.breadcrumbs": "EPeople", + "admin.access-control.epeople.breadcrumbs": "Operatori", // "admin.access-control.epeople.title": "EPeople", - "admin.access-control.epeople.title": "EPeople", + "admin.access-control.epeople.title": "Operatori", // "admin.access-control.epeople.head": "EPeople", - "admin.access-control.epeople.head": "EPeople", + "admin.access-control.epeople.head": "Operatori", // "admin.access-control.epeople.search.head": "Search", "admin.access-control.epeople.search.head": "Ricerca", // "admin.access-control.epeople.button.see-all": "Browse All", - "admin.access-control.epeople.button.see-all": "Sfoglia tutto", + "admin.access-control.epeople.button.see-all": "Sfoglia", // "admin.access-control.epeople.search.scope.metadata": "Metadata", "admin.access-control.epeople.search.scope.metadata": "Metadati", @@ -505,10 +505,10 @@ "admin.access-control.epeople.search.button": "Ricerca", // "admin.access-control.epeople.search.placeholder": "Search people...", - "admin.access-control.epeople.search.placeholder": "Cerca persone...", + "admin.access-control.epeople.search.placeholder": "Cerca operatori...", // "admin.access-control.epeople.button.add": "Add EPerson", - "admin.access-control.epeople.button.add": "Aggiungi EPerson", + "admin.access-control.epeople.button.add": "Aggiungi operatori", // "admin.access-control.epeople.table.id": "ID", "admin.access-control.epeople.table.id": "ID", @@ -520,7 +520,7 @@ "admin.access-control.epeople.table.email": "E-mail (esatta)", // "admin.access-control.epeople.table.edit": "Edit", - "admin.access-control.epeople.table.edit": "Editare", + "admin.access-control.epeople.table.edit": "Modifica", // "admin.access-control.epeople.table.edit.buttons.edit": "Edit \"{{name}}\"", "admin.access-control.epeople.table.edit.buttons.edit": "Modifica \"{{name}}\"", @@ -529,25 +529,25 @@ "admin.access-control.epeople.table.edit.buttons.edit-disabled": "Non sei autorizzato a modificare questo gruppo", // "admin.access-control.epeople.table.edit.buttons.remove": "Delete \"{{name}}\"", - "admin.access-control.epeople.table.edit.buttons.remove": "Eliminare \"{{name}}\"", + "admin.access-control.epeople.table.edit.buttons.remove": "Elimina \"{{name}}\"", // "admin.access-control.epeople.no-items": "No EPeople to show.", - "admin.access-control.epeople.no-items": "Nessuna EPersona da mostrare.", + "admin.access-control.epeople.no-items": "Nessun Operatore da mostrare.", // "admin.access-control.epeople.form.create": "Create EPerson", - "admin.access-control.epeople.form.create": "Crea EPersona", + "admin.access-control.epeople.form.create": "Crea Operatore", // "admin.access-control.epeople.form.edit": "Edit EPerson", - "admin.access-control.epeople.form.edit": "Modifica EPersona", + "admin.access-control.epeople.form.edit": "Modifica Operatore", // "admin.access-control.epeople.form.firstName": "First name", - "admin.access-control.epeople.form.firstName": "Nome di battesimo", + "admin.access-control.epeople.form.firstName": "Nome", // "admin.access-control.epeople.form.lastName": "Last name", "admin.access-control.epeople.form.lastName": "Cognome", // "admin.access-control.epeople.form.email": "E-mail", - "admin.access-control.epeople.form.email": "Posta elettronica", + "admin.access-control.epeople.form.email": "E-mail", // "admin.access-control.epeople.form.emailHint": "Must be valid e-mail address", "admin.access-control.epeople.form.emailHint": "Deve essere un indirizzo e-mail valido", @@ -562,28 +562,28 @@ "admin.access-control.epeople.form.return": "Indietro", // "admin.access-control.epeople.form.notification.created.success": "Successfully created EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.created.success": "EPerson creato con successo \"{{name}}\"", + "admin.access-control.epeople.form.notification.created.success": "Operatore creato con successo \"{{name}}\"", // "admin.access-control.epeople.form.notification.created.failure": "Failed to create EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.created.failure": "Impossibile creare EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.created.failure": "Impossibile creare l'operatore \"{{name}}\"", // "admin.access-control.epeople.form.notification.created.failure.emailInUse": "Failed to create EPerson \"{{name}}\", email \"{{email}}\" already in use.", - "admin.access-control.epeople.form.notification.created.failure.emailInUse": "Impossibile creare EPerson \"{{name}}\", email \"{{email}}\" già in uso.", + "admin.access-control.epeople.form.notification.created.failure.emailInUse": "Impossibile creare l'operatore \"{{name}}\", email \"{{email}}\" già presente.", // "admin.access-control.epeople.form.notification.edited.failure.emailInUse": "Failed to edit EPerson \"{{name}}\", email \"{{email}}\" already in use.", - "admin.access-control.epeople.form.notification.edited.failure.emailInUse": "Impossibile modificare EPerson \"{{name}}\", email \"{{email}}\" già in uso.", + "admin.access-control.epeople.form.notification.edited.failure.emailInUse": "Impossibile modificare l'operatore \"{{name}}\", email \"{{email}}\" già presenteo.", // "admin.access-control.epeople.form.notification.edited.success": "Successfully edited EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.edited.success": "EPerson modificato con successo \"{{name}}\"", + "admin.access-control.epeople.form.notification.edited.success": "Operatore modificato con successo \"{{name}}\"", // "admin.access-control.epeople.form.notification.edited.failure": "Failed to edit EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.edited.failure": "Impossibile modificare EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.edited.failure": "Impossibile modificare Operatore \"{{name}}\"", // "admin.access-control.epeople.form.notification.deleted.success": "Successfully deleted EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.deleted.success": "EPerson eliminato con successo \"{{name}}\"", + "admin.access-control.epeople.form.notification.deleted.success": "operatore eliminato con successo \"{{name}}\"", // "admin.access-control.epeople.form.notification.deleted.failure": "Failed to delete EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.deleted.failure": "Impossibile eliminare EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.deleted.failure": "Impossibile eliminare l'operatore\"{{name}}\"", // "admin.access-control.epeople.form.groupsEPersonIsMemberOf": "Member of these groups:", "admin.access-control.epeople.form.groupsEPersonIsMemberOf": "Membro dei seguenti gruppi:", @@ -604,20 +604,20 @@ "admin.access-control.epeople.form.table.collectionOrCommunity": "collection/Community", // "admin.access-control.epeople.form.memberOfNoGroups": "This EPerson is not a member of any groups", - "admin.access-control.epeople.form.memberOfNoGroups": "Questo EPerson non è membro di alcun gruppo", + "admin.access-control.epeople.form.memberOfNoGroups": "Questo operatore non è membro di alcun gruppo", // "admin.access-control.epeople.form.goToGroups": "Add to groups", "admin.access-control.epeople.form.goToGroups": "Aggiungi ai gruppi", // "admin.access-control.epeople.notification.deleted.failure": "Failed to delete EPerson: \"{{name}}\"", - "admin.access-control.epeople.notification.deleted.failure": "Impossibile eliminare l'EPerson: \"{{name}}\"", + "admin.access-control.epeople.notification.deleted.failure": "Impossibile eliminare l'operatore: \"{{name}}\"", // "admin.access-control.epeople.notification.deleted.success": "Successfully deleted EPerson: \"{{name}}\"", - "admin.access-control.epeople.notification.deleted.success": "EPerson eliminata con successo: \"{{name}}\"", + "admin.access-control.epeople.notification.deleted.success": "Operatore eliminato con successo: \"{{name}}\"", // "admin.access-control.groups.badge.disabled": "Disabled", - "admin.access-control.groups.badge.disabled": "Disabile", + "admin.access-control.groups.badge.disabled": "Disabilitato", // "admin.access-control.groups.badge.enabled": "Enabled", "admin.access-control.groups.badge.enabled": "Abilitato", @@ -662,10 +662,10 @@ "admin.access-control.groups.button.add": "Aggiungi gruppo", // "admin.access-control.groups.search.head": "Search groups", - "admin.access-control.groups.search.head": "Gruppi di ricerca", + "admin.access-control.groups.search.head": "Ricerca gruppi", // "admin.access-control.groups.button.see-all": "Browse all", - "admin.access-control.groups.button.see-all": "Sfoglia tutti", + "admin.access-control.groups.button.see-all": "Sfoglia", // "admin.access-control.groups.search.button": "Search", "admin.access-control.groups.search.button": "Ricerca", @@ -686,7 +686,7 @@ "admin.access-control.groups.table.members": "Membri", // "admin.access-control.groups.table.edit": "Edit", - "admin.access-control.groups.table.edit": "Editare", + "admin.access-control.groups.table.edit": "Modifica", // "admin.access-control.groups.table.edit.buttons.disable": "Disable \"{{name}}\"", "admin.access-control.groups.table.edit.buttons.disable": "Disabilita \"{{name}}\"", @@ -695,7 +695,7 @@ "admin.access-control.groups.table.edit.buttons.edit": "Modifica \"{{name}}\"", // "admin.access-control.groups.table.type": "Type", - "admin.access-control.groups.table.type": "Digitare", + "admin.access-control.groups.table.type": "type", // "admin.access-control.groups.table.status": "Status", "admin.access-control.groups.table.status": "Stato", @@ -713,7 +713,7 @@ "admin.access-control.groups.notification.deleted.failure.content": "Causa: \"{{cause}}\"", // "admin.access-control.groups.notification.edit.success": "Successfully edited group \"{{name}}\"", - "admin.access-control.groups.notification.edit.success": "Gruppo modificato correttamente \"{{name}}\"", + "admin.access-control.groups.notification.edit.success": "Gruppo \"{{name}}\" modificato correttamente", // "admin.access-control.groups.notification.edit.failure": "Failed to edit group \"{{name}}\"", "admin.access-control.groups.notification.edit.failure": "Impossibile modificare il gruppo \"{{name}}\"", @@ -748,10 +748,10 @@ "admin.access-control.groups.form.groupStatus.enabled": "Abilitato", // "admin.access-control.groups.form.groupStatus.disabled": "Disabled", - "admin.access-control.groups.form.groupStatus.disabled": "Disabile", + "admin.access-control.groups.form.groupStatus.disabled": "Disabilitato", // "admin.access-control.groups.form.groupType": "Type", - "admin.access-control.groups.form.groupType": "Digitare", + "admin.access-control.groups.form.groupType": "Type", // "admin.access-control.groups.form.groupType.institutional": "Institutional Role type", "admin.access-control.groups.form.groupType.institutional": "Tipo di ruolo istituzionale", @@ -772,13 +772,13 @@ "admin.access-control.groups.form.notification.created.failure": "Impossibile creare il gruppo \"{{name}}\"", // "admin.access-control.groups.form.notification.created.failure.groupNameInUse": "Failed to create Group with name: \"{{name}}\", make sure the name is not already in use.", - "admin.access-control.groups.form.notification.created.failure.groupNameInUse": "Impossibile creare il Gruppo con nome: \"{{name}}\", assicurarsi che il nome non sia già in uso.", + "admin.access-control.groups.form.notification.created.failure.groupNameInUse": "Impossibile creare il Gruppo con nome: \"{{name}}\", assicurarsi che il nome non sia già presente.", // "admin.access-control.groups.form.notification.edited.failure": "Failed to edit Group \"{{name}}\"", "admin.access-control.groups.form.notification.edited.failure": "Impossibile modificare il gruppo \"{{name}}\"", // "admin.access-control.groups.form.notification.edited.failure.groupNameInUse": "Name \"{{name}}\" already in use!", - "admin.access-control.groups.form.notification.edited.failure.groupNameInUse": "Nome \"{{name}}\" già in uso!", + "admin.access-control.groups.form.notification.edited.failure.groupNameInUse": "Nome \"{{name}}\" già presente!", // "admin.access-control.groups.form.notification.edited.success": "Successfully edited Group \"{{name}}\"", "admin.access-control.groups.form.notification.edited.success": "Gruppo modificato correttamente \"{{name}}\"", @@ -796,7 +796,7 @@ "admin.access-control.groups.form.delete-group.modal.cancel": "Annulla", // "admin.access-control.groups.form.delete-group.modal.confirm": "Delete", - "admin.access-control.groups.form.delete-group.modal.confirm": "Cancellare", + "admin.access-control.groups.form.delete-group.modal.confirm": "Cancella", // "admin.access-control.groups.form.notification.deleted.success": "Successfully deleted group \"{{ name }}\"", "admin.access-control.groups.form.notification.deleted.success": "Gruppo eliminato con successo \"{{ name }}\"", @@ -808,10 +808,10 @@ "admin.access-control.groups.form.notification.deleted.failure.content": "Causa: \"{{ cause }}\"", // "admin.access-control.groups.form.members-list.head": "EPeople", - "admin.access-control.groups.form.members-list.head": "EPeople", + "admin.access-control.groups.form.members-list.head": "Operatore", // "admin.access-control.groups.form.members-list.search.head": "Add EPeople", - "admin.access-control.groups.form.members-list.search.head": "Aggiungi EPeople", + "admin.access-control.groups.form.members-list.search.head": "Aggiungi Operatore", // "admin.access-control.groups.form.members-list.button.see-all": "Browse All", "admin.access-control.groups.form.members-list.button.see-all": "Sfoglia tutto", @@ -847,31 +847,31 @@ "admin.access-control.groups.form.members-list.table.edit": "Rimuovi / Aggiungi", // "admin.access-control.groups.form.members-list.table.edit.buttons.remove": "Remove member with name \"{{name}}\"", - "admin.access-control.groups.form.members-list.table.edit.buttons.remove": "Rimuovi membro con nome \"{{name}}\"", + "admin.access-control.groups.form.members-list.table.edit.buttons.remove": "Rimuovi utente con nome \"{{name}}\"", // "admin.access-control.groups.form.members-list.notification.success.addMember": "Successfully added member: \"{{name}}\"", "admin.access-control.groups.form.members-list.notification.success.addMember": "Membro aggiunto con successo: \"{{name}}\"", // "admin.access-control.groups.form.members-list.notification.failure.addMember": "Failed to add member: \"{{name}}\"", - "admin.access-control.groups.form.members-list.notification.failure.addMember": "Impossibile aggiungere il membro: \"{{name}}\"", + "admin.access-control.groups.form.members-list.notification.failure.addMember": "Impossibile aggiungere l'utente: \"{{name}}\"", // "admin.access-control.groups.form.members-list.notification.success.deleteMember": "Successfully deleted member: \"{{name}}\"", - "admin.access-control.groups.form.members-list.notification.success.deleteMember": "Membro eliminato con successo: \"{{name}}\"", + "admin.access-control.groups.form.members-list.notification.success.deleteMember": "Utente eliminato con successo: \"{{name}}\"", // "admin.access-control.groups.form.members-list.notification.failure.deleteMember": "Failed to delete member: \"{{name}}\"", - "admin.access-control.groups.form.members-list.notification.failure.deleteMember": "Impossibile eliminare il membro: \"{{name}}\"", + "admin.access-control.groups.form.members-list.notification.failure.deleteMember": "Impossibile eliminare l'utente: \"{{name}}\"", // "admin.access-control.groups.form.members-list.table.edit.buttons.add": "Add member with name \"{{name}}\"", - "admin.access-control.groups.form.members-list.table.edit.buttons.add": "Aggiungi membro con nome \"{{name}}\"", + "admin.access-control.groups.form.members-list.table.edit.buttons.add": "Aggiungi l'utente con nome \"{{name}}\"", // "admin.access-control.groups.form.members-list.notification.failure.noActiveGroup": "No current active group, submit a name first.", "admin.access-control.groups.form.members-list.notification.failure.noActiveGroup": "Nessun gruppo corrente attivo, invia prima un nome.", // "admin.access-control.groups.form.members-list.no-members-yet": "No members in group yet, search and add.", - "admin.access-control.groups.form.members-list.no-members-yet": "Ancora nessun membro del gruppo, cercali e aggiungili.", + "admin.access-control.groups.form.members-list.no-members-yet": "Ancora nessun utente del gruppo, cercali e aggiungili.", // "admin.access-control.groups.form.members-list.no-items": "No EPeople found in that search", - "admin.access-control.groups.form.members-list.no-items": "Nessun EPeople trovato in quella ricerca", + "admin.access-control.groups.form.members-list.no-items": "Nessun operatore trovato nella ricerca", // "admin.access-control.groups.form.subgroups-list.notification.failure": "Something went wrong: \"{{cause}}\"", "admin.access-control.groups.form.subgroups-list.notification.failure": "Qualcosa è andato storto: \"{{cause}}\"", @@ -910,7 +910,7 @@ "admin.access-control.groups.form.subgroups-list.table.edit.buttons.add": "Aggiungi sottogruppo con nome \"{{name}}\"", // "admin.access-control.groups.form.subgroups-list.table.edit.currentGroup": "Current group", - "admin.access-control.groups.form.subgroups-list.table.edit.currentGroup": "Gruppo attuale", + "admin.access-control.groups.form.subgroups-list.table.edit.currentGroup": "Gruppo corrente", // "admin.access-control.groups.form.subgroups-list.notification.success.addSubgroup": "Successfully added subgroup: \"{{name}}\"", "admin.access-control.groups.form.subgroups-list.notification.success.addSubgroup": "Sottogruppo aggiunto con successo: \"{{name}}\"", @@ -925,16 +925,16 @@ "admin.access-control.groups.form.subgroups-list.notification.failure.deleteSubgroup": "Impossibile eliminare il sottogruppo: \"{{name}}\"", // "admin.access-control.groups.form.subgroups-list.notification.failure.noActiveGroup": "No current active group, submit a name first.", - "admin.access-control.groups.form.subgroups-list.notification.failure.noActiveGroup": "Al momento nessun gruppo attivo, invia prima un nome.", + "admin.access-control.groups.form.subgroups-list.notification.failure.noActiveGroup": "Al momento nessun gruppo attivo, invia prima un operatore.", // "admin.access-control.groups.form.subgroups-list.notification.failure.subgroupToAddIsActiveGroup": "This is the current group, can't be added.", "admin.access-control.groups.form.subgroups-list.notification.failure.subgroupToAddIsActiveGroup": "Questo è il gruppo corrente, non può essere aggiunto.", // "admin.access-control.groups.form.subgroups-list.no-items": "No groups found with this in their name or this as UUID", - "admin.access-control.groups.form.subgroups-list.no-items": "Nessun gruppo trovato con questo nel loro nome o questo come UUID", + "admin.access-control.groups.form.subgroups-list.no-items": "Nessun gruppo trovato con questo nome o questo come UUID", // "admin.access-control.groups.form.subgroups-list.no-subgroups-yet": "No subgroups in group yet.", - "admin.access-control.groups.form.subgroups-list.no-subgroups-yet": "Nessun sottogruppo ancora nel gruppo.", + "admin.access-control.groups.form.subgroups-list.no-subgroups-yet": "Nessun sottogruppo nel gruppo.", // "admin.access-control.groups.form.return": "Back", "admin.access-control.groups.form.return": "Indietro", From 1c18e1fa605cf5d941bdccbb243478f08c0708f8 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 5 Oct 2023 14:11:53 +0200 Subject: [PATCH 155/195] [DSC-1277] CRIS/GLAM translation sync 3 --- src/assets/i18n/it.json5 | 72 ++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 670b44fccfd..b0bc4d529b1 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -971,16 +971,16 @@ "admin.search.breadcrumbs": "Ricerca amministrativa", // "admin.search.collection.edit": "Edit", - "admin.search.collection.edit": "Editare", + "admin.search.collection.edit": "Modifica", // "admin.search.community.edit": "Edit", - "admin.search.community.edit": "Editare", + "admin.search.community.edit": "Modifica", // "admin.search.item.delete": "Delete", - "admin.search.item.delete": "Cancellare", + "admin.search.item.delete": "Cancella", // "admin.search.item.edit": "Edit", - "admin.search.item.edit": "Editare", + "admin.search.item.edit": "Modifica", // "admin.search.item.make-private": "Make non-discoverable", "admin.search.item.make-private": "Rendi privato", @@ -992,10 +992,10 @@ "admin.search.item.move": "Sposta", // "admin.search.item.reinstate": "Reinstate", - "admin.search.item.reinstate": "Reintegrare", + "admin.search.item.reinstate": "Reintegra", // "admin.search.item.withdraw": "Withdraw", - "admin.search.item.withdraw": "Ritirare", + "admin.search.item.withdraw": "Ritira", // "admin.search.title": "Administrative Search", "admin.search.title": "Ricerca amministrativa", @@ -1007,22 +1007,22 @@ // "admin.workflow.breadcrumbs": "Administer Workflow", - "admin.workflow.breadcrumbs": "Amministrare il flusso di lavoro", + "admin.workflow.breadcrumbs": "Amministrazione Workflow", // "admin.workflow.title": "Administer Workflow", - "admin.workflow.title": "Amministrare il flusso di lavoro", + "admin.workflow.title": "Amministrazione Workflow", // "admin.workflow.item.workflow": "Workflow", - "admin.workflow.item.workflow": "Flusso di lavoro", + "admin.workflow.item.workflow": "Workflow", // "admin.workflow.item.workspace": "Workspace", "admin.workflow.item.workspace": "Workspace", // "admin.workflow.item.delete": "Delete", - "admin.workflow.item.delete": "Cancellare", + "admin.workflow.item.delete": "Cancella", // "admin.workflow.item.send-back": "Send back", - "admin.workflow.item.send-back": "Rinviare", + "admin.workflow.item.send-back": "Rinvia", // "admin.workflow.item.policies": "Policies", "admin.workflow.item.policies": "Policy", @@ -1033,19 +1033,19 @@ // "admin.metadata-import.breadcrumbs": "Import Metadata", - "admin.metadata-import.breadcrumbs": "Importare metadati", + "admin.metadata-import.breadcrumbs": "Importa metadati", // "admin.batch-import.breadcrumbs": "Import Batch", "admin.batch-import.breadcrumbs": "Batch Import", // "admin.metadata-import.title": "Import Metadata", - "admin.metadata-import.title": "Importare metadati", + "admin.metadata-import.title": "Importa metadati", // "admin.batch-import.title": "Import Batch", "admin.batch-import.title": "Batch Import", // "admin.metadata-import.page.header": "Import Metadata", - "admin.metadata-import.page.header": "Importare metadati", + "admin.metadata-import.page.header": "Importa metadati", // "admin.batch-import.page.header": "Import Batch", "admin.batch-import.page.header": "Batch Import", @@ -1054,34 +1054,34 @@ "admin.metadata-import.page.help": "È possibile trascinare o ricercare qui i file CSV che contengono le informazioni di import metadata", // "admin.batch-import.page.help": "Select the Collection to import into. Then, drop or browse to a Simple Archive Format (SAF) zip file that includes the Items to import", - "admin.batch-import.page.help": "Selezionare la Collection in cui effettuare l'import. Quindi, rilasciare o sfogliare un file zip Simple Archive Format (SAF) che include gli elementi da importare", + "admin.batch-import.page.help": "Seleziona la Collezione in cui importare la risorsa. Trascina o seleziona il file zip Simple Archive Format (SAF) che include gli elementi da importare", // "admin.metadata-import.page.dropMsg": "Drop a metadata CSV to import", - "admin.metadata-import.page.dropMsg": "Rilasciare un CSV di metadati da importare", + "admin.metadata-import.page.dropMsg": "Rilascia un CSV di metadati da importare", // "admin.batch-import.page.dropMsg": "Drop a batch ZIP to import", "admin.batch-import.page.dropMsg": "Rilasciare un batch ZIP da importare", // "admin.metadata-import.page.dropMsgReplace": "Drop to replace the metadata CSV to import", - "admin.metadata-import.page.dropMsgReplace": "Rilasciare per sostituire i metadati CSV da importare", + "admin.metadata-import.page.dropMsgReplace": "Rilascia un nuovo file CSV per sostituire il file corrente da importare", // "admin.batch-import.page.dropMsgReplace": "Drop to replace the batch ZIP to import", - "admin.batch-import.page.dropMsgReplace": "Rilasciare per sostituire il batch ZIP da importare", + "admin.batch-import.page.dropMsgReplace": "Rilascia un nuovo file ZIP per sostituire il file corrente da importare", // "admin.metadata-import.page.button.return": "Back", "admin.metadata-import.page.button.return": "Indietro", // "admin.metadata-import.page.button.proceed": "Proceed", - "admin.metadata-import.page.button.proceed": "Procedere", + "admin.metadata-import.page.button.proceed": "Procedi", // "admin.metadata-import.page.button.select-collection": "Select Collection", - "admin.metadata-import.page.button.select-collection": "Selezionare una Collection", + "admin.metadata-import.page.button.select-collection": "Selezionare una Collezione", // "admin.metadata-import.page.error.addFile": "Select file first!", "admin.metadata-import.page.error.addFile": "Seleziona prima il file!", // "admin.batch-import.page.error.addFile": "Select Zip file first!", - "admin.batch-import.page.error.addFile": "Seleziona prima il file ZIP!", + "admin.batch-import.page.error.addFile": "Seleziona prima il file ZIP", // "admin.metadata-import.page.validateOnly": "Validate Only", "admin.metadata-import.page.validateOnly": "Solo Validazione", @@ -1188,11 +1188,11 @@ "admin.batch-import.page.validateOnly.hint": "Una volta selezionato, il file ZIP caricato verrà convalidato. Si riceverà un rapporto sulle modifiche rilevate, ma non verrà salvata alcuna modifica.", // "admin.batch-import.page.remove": "remove", - "admin.batch-import.page.remove": "rimuovere", + "admin.batch-import.page.remove": "Elimina", // "alert.close.aria": "Close", - "alert.close.aria": "Chiudere", + "alert.close.aria": "Chiudi", // "alert.toggle.hide": "Show less", "alert.toggle.hide": "Mostra meno", @@ -1205,10 +1205,10 @@ "auth.errors.invalid-user": "Indirizzo e-mail o password non validi.", // "auth.messages.expired": "Your session has expired. Please log in again.", - "auth.messages.expired": "La sessione è scaduta. Effettua nuovamente l'accesso.", + "auth.messages.expired": "La sessione è scaduta. Effettua nuovamente il log in.", // "auth.messages.token-refresh-failed": "Refreshing your session token failed. Please log in again.", - "auth.messages.token-refresh-failed": "Aggiornamento del token di sessione non riuscito. Effettua nuovamente l'accesso.", + "auth.messages.token-refresh-failed": "Aggiornamento del token di sessione non riuscita. Effettua nuovamente il log in.", @@ -1219,14 +1219,14 @@ "bitstream.download.page.back": "Indietro", // "bitstream.download.page.close": "Close", - "bitstream.download.page.close": "Chiudere", + "bitstream.download.page.close": "Chiudi", // "bitstream.edit.authorizations.link": "Edit bitstream's Policies", - "bitstream.edit.authorizations.link": "Modificare i criteri di bitstream", + "bitstream.edit.authorizations.link": "Modifica le policies di bitstream", // "bitstream.edit.authorizations.title": "Edit bitstream's Policies", - "bitstream.edit.authorizations.title": "Modificare i criteri di bitstream", + "bitstream.edit.authorizations.title": "Modifica le policies di bitstream", // "bitstream.edit.return": "Back", "bitstream.edit.return": "Indietro", @@ -1262,10 +1262,10 @@ "bitstream.edit.form.primaryBitstream.label": "Primary bitstream", // "bitstream.edit.form.fileType.label": "File type", - "bitstream.edit.form.fileType.label": "Tipologi File", + "bitstream.edit.form.fileType.label": "Tipologia del File", // "bitstream.edit.form.fileType.hint": "Personal picture, logo, main article, etc.", - "bitstream.edit.form.fileType.hint": "Immagine personale, logo, articolo principale, ecc.", + "bitstream.edit.form.fileType.hint": "ersonal picture, logo, main article, ecc.", // "bitstream.edit.form.selectedFormat.hint": "If the format is not in the above list, select \"format not in list\" above and describe it under \"Describe new format\".", "bitstream.edit.form.selectedFormat.hint": "Se il formato non è presente nell'elenco precedente, selezionare \"formato non presente in elenco\" sopra e descriverlo in \"Descrivi nuovo formato.", @@ -1283,13 +1283,13 @@ "bitstream.edit.form.iiifLabel.label": "Etichetta IIIF", // "bitstream.edit.form.iiifLabel.hint": "Canvas label for this image. If not provided default label will be used.", - "bitstream.edit.form.iiifLabel.hint": "Etichetta canvas per questa immagine. Se non viene fornita, verrà utilizzata l'etichetta predefinita.", + "bitstream.edit.form.iiifLabel.hint": "Etichetta di cornice per questa immagine. Se non viene fornita, verrà utilizzata l'etichetta predefinita.", // "bitstream.edit.form.iiifToc.label": "IIIF Table of Contents", - "bitstream.edit.form.iiifToc.label": "Sommario IIIF", + "bitstream.edit.form.iiifToc.label": "Indice IIIF", // "bitstream.edit.form.iiifToc.hint": "Adding text here makes this the start of a new table of contents range.", - "bitstream.edit.form.iiifToc.hint": "L'aggiunta di testo qui rende questo l'inizio di un nuovo intervallo di sommario.", + "bitstream.edit.form.iiifToc.hint": "L'aggiunta di testo qui rende questo l'inizio di un nuovo intervallo dell'indice.", // "bitstream.edit.form.iiifWidth.label": "IIIF Canvas Width", "bitstream.edit.form.iiifWidth.label": "Larghezza area di lavoro IIIF", @@ -1396,7 +1396,7 @@ "browse.comcol.by.rpname": "Per nome", // "browse.comcol.by.subject": "By Subject", - "browse.comcol.by.subject": "Per argomento", + "browse.comcol.by.subject": "Per soggetto", // "browse.comcol.by.title": "By Title", "browse.comcol.by.title": "Per titolo", @@ -1406,7 +1406,7 @@ // "browse.comcol.head": "Browse", - "browse.comcol.head": "Sfogliare", + "browse.comcol.head": "Sfoglia", // "browse.empty": "No items to show.", "browse.empty": "Nessun item da mostrare.", @@ -1445,7 +1445,7 @@ "browse.metadata.subject": "Soggetto", // "browse.metadata.type": "Type", - "browse.metadata.type": "Digitare", + "browse.metadata.type": "Tipo", // "browse.metadata.title": "Title", "browse.metadata.title": "Titolo", From b62aaad77a426b67cf4977540a33b7fb897b0337 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 14:39:01 +0200 Subject: [PATCH 156/195] [CST-10703] confirm email fixes --- src/app/core/data/eperson-registration.service.ts | 3 ++- .../confirm-email/confirm-email.component.ts | 9 ++++----- .../provide-email/provide-email.component.ts | 5 +---- .../services/external-login.service.ts | 4 ++-- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/app/core/data/eperson-registration.service.ts b/src/app/core/data/eperson-registration.service.ts index 259b5f66c50..43bd957f928 100644 --- a/src/app/core/data/eperson-registration.service.ts +++ b/src/app/core/data/eperson-registration.service.ts @@ -16,6 +16,7 @@ import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpHeaders } from '@angular/common/http'; import { HttpParams } from '@angular/common/http'; import { Operation } from 'fast-json-patch'; +import { NoContent } from '../shared/NoContent.model'; @Injectable({ providedIn: 'root', }) @@ -157,7 +158,7 @@ export class EpersonRegistrationService{ * @param updateValue Flag to indicate if the email should be updated or added * @returns Remote Data state of the patch request */ - patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operator: 'add' | 'replace'): Observable> { + patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operator: 'add' | 'replace'): Observable> { const requestId = this.requestService.generateRequestId(); const href$ = this.getRegistrationEndpoint().pipe( diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index ba5c3d3d138..696d5d265e1 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -80,10 +80,7 @@ export class ConfirmEmailComponent implements OnDestroy { this.subs.push( this.externalLoginService.patchUpdateRegistration(values, 'email', this.registrationData.id, this.token, 'replace') .pipe(getRemoteDataPayload()) - .subscribe((update) => { - // TODO: remove this line (temporary) - console.log('Email update:', update); - })); + .subscribe()); } /** @@ -104,14 +101,16 @@ export class ConfirmEmailComponent implements OnDestroy { const metadataValues = {}; for (const [key, value] of Object.entries(registrationData.registrationMetadata)) { if (hasValue(value[0]?.value) && key !== 'email') { - metadataValues[key] = value[0]; + metadataValues[key] = value; } } const eperson = new EPerson(); eperson.email = registrationData.email; + eperson.netid = registrationData.netId; eperson.metadata = metadataValues; eperson.canLogIn = true; eperson.requireCertificate = false; + eperson.selfRegistered = true; this.subs.push( this.epersonDataService.createEPersonForToken(eperson, token).pipe( getFirstCompletedRemoteData(), diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts index 92faf10e756..5efbd0f7d62 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts @@ -48,10 +48,7 @@ export class ProvideEmailComponent implements OnDestroy { if (this.emailForm.valid) { const email = this.emailForm.get('email').value; this.subs.push(this.externalLoginService.patchUpdateRegistration([email], 'email', this.registrationId, this.token, 'add') - .subscribe((rd: RemoteData) => { - // TODO: remove this line (temporary) - console.log('Email update:', rd); - })); + .subscribe()); } } diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/shared/external-log-in-complete/services/external-login.service.ts index 1ec730516c4..3d3f97571c9 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.ts @@ -6,7 +6,7 @@ import { RemoteData } from '../../../core/data/remote-data'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { NotificationsService } from '../../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { Registration } from 'src/app/core/shared/registration.model'; +import { NoContent } from '../../../core/shared/NoContent.model'; @Injectable({ providedIn: 'root' @@ -29,7 +29,7 @@ export class ExternalLoginService { * @param token the registration token * @param operation operation to be performed */ - patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operation: 'add' | 'replace'): Observable>{ + patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operation: 'add' | 'replace'): Observable>{ const updatedValues = values.map((value) => value); return this.epersonRegistrationService.patchUpdateRegistration(updatedValues, field, registrationId, token, operation).pipe( getFirstCompletedRemoteData(), From 236338f5e665932842843d5339609dbe460aeffe Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 15:04:23 +0200 Subject: [PATCH 157/195] [CST-10703] changed how to get token parameter --- src/app/app-routing.module.ts | 4 ++-- .../external-login-page/external-login-page.component.ts | 4 ++-- .../external-login-review-account-info-page.component.ts | 3 ++- .../helpers/review-account.guard.ts | 4 ++-- .../provide-email/provide-email.component.ts | 2 -- .../guards/registration-token.guard.ts | 6 ++---- .../resolvers/registration-data.resolver.ts | 2 +- 7 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index fb4d658d8cb..a652eadde9e 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -172,12 +172,12 @@ import { RedirectService } from './redirect/redirect.service'; .then((m) => m.LoginPageModule) }, { - path: 'external-login', + path: 'external-login/:token', loadChildren: () => import('./external-login-page/external-login-page.module') .then((m) => m.ExternalLoginPageModule) }, { - path: 'review-account', + path: 'review-account/:token', loadChildren: () => import('./external-login-review-account-info/external-login-review-account-info-page.module') .then((m) => m.ExternalLoginReviewAccountInfoModule) }, diff --git a/src/app/external-login-page/external-login-page.component.ts b/src/app/external-login-page/external-login-page.component.ts index 1e3862bcd2f..d717bce739b 100644 --- a/src/app/external-login-page/external-login-page.component.ts +++ b/src/app/external-login-page/external-login-page.component.ts @@ -33,8 +33,8 @@ export class ExternalLoginPageComponent implements OnInit { constructor( private arouter: ActivatedRoute ) { - this.token = this.arouter.snapshot.queryParams.token; - this.hasErrors = hasNoValue(this.arouter.snapshot.queryParams.token); + this.token = this.arouter.snapshot.params.token; + this.hasErrors = hasNoValue(this.arouter.snapshot.params.token); } ngOnInit(): void { diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts index dedb3d9baa1..a42c9e05cfd 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts @@ -33,7 +33,8 @@ export class ExternalLoginReviewAccountInfoPageComponent implements OnInit { constructor( private arouter: ActivatedRoute ) { - this.token = this.arouter.snapshot.queryParams.token; + this.token = this.arouter.snapshot.params.token; + this.hasErrors = hasNoValue(this.arouter.snapshot.params.token); } ngOnInit(): void { diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.ts index 8ae1bca6f25..03caf6d63ed 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.ts +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.ts @@ -35,9 +35,9 @@ export class ReviewAccountGuard implements CanActivate { route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Promise | boolean | Observable { - if (route.queryParams.token) { + if (route.params.token) { return this.epersonRegistrationService - .searchRegistrationByToken(route.queryParams.token) + .searchRegistrationByToken(route.params.token) .pipe( getFirstCompletedRemoteData(), mergeMap( diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts index 5efbd0f7d62..a63486dea60 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts @@ -1,8 +1,6 @@ import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { Registration } from '../../../../core/shared/registration.model'; import { Subscription } from 'rxjs'; import { hasValue } from '../../../../shared/empty.util'; diff --git a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts index ec37d9d44e7..d565dfbd91a 100644 --- a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts +++ b/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts @@ -31,15 +31,13 @@ export class RegistrationTokenGuard implements CanActivate { route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable { - if (route.queryParams.token) { + if (route.params.token) { return this.epersonRegistrationService - .searchRegistrationByToken(route.queryParams.token) + .searchRegistrationByToken(route.params.token) .pipe( getFirstCompletedRemoteData(), map( (data: RemoteData) => { - // TODO: remove console.log - console.log(data, 'RegistrationTokenGuard'); if (data.hasSucceeded && hasValue(data)) { return true; } else { diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts index 5513a847a43..6a9b6b8b31b 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts +++ b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts @@ -32,7 +32,7 @@ export class RegistrationDataResolver implements Resolve> { - const token = route.queryParams.token; + const token = route.params.token; if (hasValue(token)) { return this.epersonRegistrationService.searchRegistrationByToken(token).pipe( getFirstCompletedRemoteData(), From 51830cff8b3010484e010d760b237eedcdac2439 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 15:36:50 +0200 Subject: [PATCH 158/195] [CST-10703] minor fix --- .../confirm-email/confirm-email.component.ts | 10 ++++++++-- src/app/shared/log-in/log-in.component.ts | 2 +- src/assets/i18n/en.json5 | 2 ++ src/assets/i18n/it.json5 | 4 ++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 696d5d265e1..1e2191db664 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../../core/shared/operators'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; -import { hasValue } from '../../../../shared/empty.util'; +import { hasNoValue, hasValue } from '../../../../shared/empty.util'; import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; @@ -98,6 +98,12 @@ export class ConfirmEmailComponent implements OnDestroy { token: string, registrationData: Registration ) { + + if (hasNoValue(this.registrationData.netId)) { + this.notificationService.error(this.translate.get('external-login-page.confirm-email.create-account.notifications.error.no-netId')); + return; + } + const metadataValues = {}; for (const [key, value] of Object.entries(registrationData.registrationMetadata)) { if (hasValue(value[0]?.value) && key !== 'email') { @@ -124,7 +130,7 @@ export class ConfirmEmailComponent implements OnDestroy { // redirect to login page with authMethod query param, so that the login page knows which authentication method to use // set Redirect URL to User profile, so the user is redirected to the profile page after logging in this.router.navigate(['/login'], { queryParams: { authMethod: registrationData.registrationType } }); - this.authService.setRedirectUrl('/review-account'); + this.authService.setRedirectUrl('/profile'); } })); } diff --git a/src/app/shared/log-in/log-in.component.ts b/src/app/shared/log-in/log-in.component.ts index 0322079475f..30dc535d313 100644 --- a/src/app/shared/log-in/log-in.component.ts +++ b/src/app/shared/log-in/log-in.component.ts @@ -97,7 +97,7 @@ export class LogInComponent implements OnInit, OnDestroy { } // if there is an external login method the user should follow, filter the auth methods to only show that one if (hasValue(this.externalLoginMethod)) { - this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType === this.externalLoginMethod); + this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType === this.externalLoginMethod.toLocaleLowerCase()); } }); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 94a153d1772..e1d53265164 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7231,4 +7231,6 @@ "external-login-page.provide-email.create-account.notifications.error.header": "Something went wrong", "external-login-page.provide-email.create-account.notifications.error.content": "Please check again your email address and try again.", + + "external-login-page.confirm-email.create-account.notifications.error.no-netId": "Something went wrong with this email account. Try again or use a different method to login.", } diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index fd5fb8ca8b5..db38d085fe5 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -11343,4 +11343,8 @@ // "external-login-page.provide-email.create-account.notifications.error.content": "Please check again your email address and try again.", // TODO New key - Add a translation "external-login-page.provide-email.create-account.notifications.error.content": "Please check again your email address and try again.", + + // "external-login-page.confirm-email.create-account.notifications.error.no-netId": "Something went wrong with this email account. Try again or use a different method to login.", + // TODO New key - Add a translation + "external-login-page.confirm-email.create-account.notifications.error.no-netId": "Something went wrong with this email account. Try again or use a different method to login.", } From 85c002cd2d01fc782c5956198def7be0dbe326b5 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 5 Oct 2023 15:53:43 +0200 Subject: [PATCH 159/195] [DSC-1277] CRIS/GLAM translation sync 4 --- src/assets/i18n/it.json5 | 114 +++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index b0bc4d529b1..84fee4e2926 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -1481,7 +1481,7 @@ "browse.metadata.rpname.breadcrumbs": "Sfoglia per Nome", // "browse.metadata.subject.breadcrumbs": "Browse by Subject", - "browse.metadata.subject.breadcrumbs": "Sfoglia per Argomento", + "browse.metadata.subject.breadcrumbs": "Sfoglia per Soggetto", // "browse.metadata.type.breadcrumbs": "Browse by Type", "browse.metadata.type.breadcrumbs": "Sfoglia per Tipo", @@ -1493,13 +1493,13 @@ "pagination.next.button": "Avanti", // "pagination.previous.button": "Previous", - "pagination.previous.button": "Precedente", + "pagination.previous.button": "Indietro", // "pagination.next.button.disabled.tooltip": "No more pages of results", - "pagination.next.button.disabled.tooltip": "Non ci sono ulteriori pagine di risultati", + "pagination.next.button.disabled.tooltip": "Non ci sono ulteriori pagine", // "browse.startsWith": ", starting with {{ startsWith }}", - "browse.startsWith": ", inizia per {{ startsWith }}", + "browse.startsWith": ", a partire da {{ startsWith }}", // "browse.startsWith.choose_start": "(Choose start)", "browse.startsWith.choose_start": "(Scegli start)", @@ -1508,7 +1508,7 @@ "browse.startsWith.choose_year": "(Scegli l'anno)", // "browse.startsWith.choose_year.label": "Choose the issue year", - "browse.startsWith.choose_year.label": "Scegli l'anno di pubblicazione", + "browse.startsWith.choose_year.label": "Scegli l'anno", // "browse.startsWith.jump": "Filter results by year or month", "browse.startsWith.jump": "Filtra i risultati per anno o mese", @@ -1544,7 +1544,7 @@ "browse.startsWith.months.none": "(Scegli il mese)", // "browse.startsWith.months.none.label": "Choose the issue month", - "browse.startsWith.months.none.label": "Scegli il mese di pubblicazione", + "browse.startsWith.months.none.label": "Scegli il mese", // "browse.startsWith.months.november": "November", "browse.startsWith.months.november": "Novembre", @@ -1556,7 +1556,7 @@ "browse.startsWith.months.september": "Settembre", // "browse.startsWith.submit": "Browse", - "browse.startsWith.submit": "Sfogliare", + "browse.startsWith.submit": "Sfoglia", // "browse.startsWith.type_date": "Filter results by date", "browse.startsWith.type_date": "Filtra per data", @@ -1586,31 +1586,31 @@ "bulk-import.back": "Indietro", // "bulk-import.breadcrumbs" : "Bulk import", - "bulk-import.breadcrumbs" : "Bulk import", + "bulk-import.breadcrumbs" : "Import massivo", // "bulk-import.collection-name" : "Collection", "bulk-import.collection-name" : "Collezione", // "bulk-import.error": "An error occurred while creating the import process", - "bulk-import.error": "Si è verificato un errore durante la creazione del processo di importazione", + "bulk-import.error": "Si è verificato un errore durante la creazione del processo di import", // "bulk-import.file" : "Source file", - "bulk-import.file" : "Source file", + "bulk-import.file" : "File sorgente", // "bulk-import.header" : "Bulk import", - "bulk-import.header" : "Bulk import", + "bulk-import.header" : "Import massivo", // "bulk-import.processing": "Processing...", - "bulk-import.processing": "Elaborazione...", + "bulk-import.processing": "Elaborazione in corso...", // "bulk-import.success": "The import process was successfully created", - "bulk-import.success": "Il processo di importazione è stato creato correttamente", + "bulk-import.success": "Il processo di import è stato creato correttamente", // "bulk-import.submit": "Start import", - "bulk-import.submit": "Avvia importazione", + "bulk-import.submit": "Avvia import", // "bulk-import.title" : "Bulk import", - "bulk-import.title" : "Bulk import", + "bulk-import.title" : "Import massivo", @@ -1631,13 +1631,13 @@ // "collection.create.head": "Create a Collection", - "collection.create.head": "Creare una collection", + "collection.create.head": "Crea una collection", // "collection.create.notifications.success": "Successfully created the Collection", "collection.create.notifications.success": "Collection creata con successo", // "collection.create.sub-head": "Create a Collection for Community {{ parent }}", - "collection.create.sub-head": "Creare una collection per la Community {{ parent }}", + "collection.create.sub-head": "Crea una collection per la Community {{ parent }}", // "collection.curate.header": "Curate Collection: {{collection}}", "collection.curate.header": "Curate della collezione: {{collection}}", @@ -1646,10 +1646,10 @@ "collection.delete.cancel": "Annulla", // "collection.delete.confirm": "Confirm", - "collection.delete.confirm": "Confermare", + "collection.delete.confirm": "Conferma", // "collection.delete.processing": "Deleting", - "collection.delete.processing": "Eliminazione", + "collection.delete.processing": "Eliminazione in corso", // "collection.delete.head": "Delete Collection", "collection.delete.head": "Elimina collection", @@ -1692,7 +1692,7 @@ "collection.edit.item-mapper.confirm": "Mappare gli item selezionati", // "collection.edit.item-mapper.description": "This is the item mapper tool that allows collection administrators to map items from other collections into this collection. You can search for items from other collections and map them, or browse the list of currently mapped items.", - "collection.edit.item-mapper.description": "Si tratta dello strumento di mapping degli item che consente agli amministratori della collection di mappare gli item di altre collections in questa collection. È possibile cercare item di altre collections e mapparli o sfogliare l'elenco degli item attualmente mappati.", + "collection.edit.item-mapper.description": "Questo è lo strumento di mapping degli item che consente agli amministratori della collection di mappare gli item di altre collections su questa collection. È possibile cercare item di altre collectioni e mapparli oppure sfogliare l'elenco degli item attualmente mappati.", // "collection.edit.item-mapper.head": "Item Mapper - Map Items from Other Collections", "collection.edit.item-mapper.head": "Item Mapper - Mappa item di altre collections", @@ -1716,7 +1716,7 @@ "collection.edit.item-mapper.notifications.unmap.error.content": "Si sono verificati errori per la rimozione dei mapping degli item {{amount}}.", // "collection.edit.item-mapper.notifications.unmap.error.head": "Remove mapping errors", - "collection.edit.item-mapper.notifications.unmap.error.head": "Rimuovere gli errori di mappatura", + "collection.edit.item-mapper.notifications.unmap.error.head": "Rimuovi gli errori di mappatura", // "collection.edit.item-mapper.notifications.unmap.success.content": "Successfully removed the mappings of {{amount}} items.", "collection.edit.item-mapper.notifications.unmap.success.content": "Rimosse con successo le mappature degli item {{amount}}.", @@ -1738,10 +1738,10 @@ // "collection.edit.logo.delete.title": "Delete logo", - "collection.edit.logo.delete.title": "Annulla eliminazione", + "collection.edit.logo.delete.title": "Cancella Logo", // "collection.edit.logo.delete-undo.title": "Undo delete", - "collection.edit.logo.delete-undo.title": "Elimina logo", + "collection.edit.logo.delete-undo.title": "Annula la cancellazione", // "collection.edit.logo.label": "Collection logo", "collection.edit.logo.label": "Logo della collection", @@ -1756,16 +1756,16 @@ "collection.edit.logo.notifications.delete.success.title": "Logo cancellato", // "collection.edit.logo.notifications.delete.success.content": "Successfully deleted the collection's logo", - "collection.edit.logo.notifications.delete.success.content": "Eliminato con successo il logo della collection", + "collection.edit.logo.notifications.delete.success.content": "Il logo della collezione è stato eliminato", // "collection.edit.logo.notifications.delete.error.title": "Error deleting logo", "collection.edit.logo.notifications.delete.error.title": "Errore durante l'eliminazione del logo", // "collection.edit.logo.upload": "Drop a Collection Logo to upload", - "collection.edit.logo.upload": "Rilascia un logo della collection da caricare", + "collection.edit.logo.upload": "Trascina qui il logo da caricare per la collezione", // "collection.edit.notifications.success": "Successfully edited the Collection", - "collection.edit.notifications.success": "Modificata correttamente la collection", + "collection.edit.notifications.success": "Modificata correttamente la collezione", // "collection.edit.return": "Back", "collection.edit.return": "Indietro", @@ -1776,13 +1776,13 @@ "collection.edit.tabs.curate.head": "Curate", // "collection.edit.tabs.curate.title": "Collection Edit - Curate", - "collection.edit.tabs.curate.title": "Modifica collection - Curate", + "collection.edit.tabs.curate.title": "Modifica la collection - Curate", // "collection.edit.tabs.authorizations.head": "Authorizations", "collection.edit.tabs.authorizations.head": "Autorizzazioni", // "collection.edit.tabs.authorizations.title": "Collection Edit - Authorizations", - "collection.edit.tabs.authorizations.title": "Collection Edit - Autorizzazioni", + "collection.edit.tabs.authorizations.title": "Modifica la collezione - Autorizzazioni", // "collection.edit.item.authorizations.load-bundle-button": "Load more bundles", "collection.edit.item.authorizations.load-bundle-button": "Carica più bundles", @@ -1791,19 +1791,19 @@ "collection.edit.item.authorizations.load-more-button": "Carica più risorse", // "collection.edit.item.authorizations.show-bitstreams-button": "Show bitstream policies for bundle", - "collection.edit.item.authorizations.show-bitstreams-button": "Mostra le policy del bitstream per il bundle", + "collection.edit.item.authorizations.show-bitstreams-button": "Mostra le policiy dei bitstream per i bundle", // "collection.edit.tabs.metadata.head": "Edit Metadata", - "collection.edit.tabs.metadata.head": "Modifica metadati", + "collection.edit.tabs.metadata.head": "Modifica i metadati", // "collection.edit.tabs.metadata.title": "Collection Edit - Metadata", - "collection.edit.tabs.metadata.title": "Modifica collection - Metadati", + "collection.edit.tabs.metadata.title": "Modifica la collezione - Metadati", // "collection.edit.tabs.roles.head": "Assign Roles", "collection.edit.tabs.roles.head": "Assegna ruoli", // "collection.edit.tabs.roles.title": "Collection Edit - Roles", - "collection.edit.tabs.roles.title": "Modifica collection - Ruoli", + "collection.edit.tabs.roles.title": "Modifica la collezione - Ruoli", // "collection.edit.tabs.source.external": "This collection harvests its content from an external source", "collection.edit.tabs.source.external": "Questa collection raccoglie il suo contenuto da una fonte esterna", @@ -1812,13 +1812,13 @@ "collection.edit.tabs.source.form.adminEmail": "Indirizzo dell'amministratore", // "collection.edit.tabs.source.form.errors.oaiSource.required": "You must provide a set id of the target collection.", - "collection.edit.tabs.source.form.errors.oaiSource.required": "È necessario fornire un ID impostato della collection di destinazione.", + "collection.edit.tabs.source.form.errors.oaiSource.required": "È necessario fornire un ID della collezione di destinazione.", // "collection.edit.tabs.source.form.harvestType": "Content being harvested", - "collection.edit.tabs.source.form.harvestType": "Contenuto che è stato harvestato", + "collection.edit.tabs.source.form.harvestType": "Dati esterni harvestati", // "collection.edit.tabs.source.form.head": "Configure an external source", - "collection.edit.tabs.source.form.head": "Configurare un'origine esterna", + "collection.edit.tabs.source.form.head": "Configura un'origine esterna", // "collection.edit.tabs.source.form.metadataConfigId": "Metadata Format", "collection.edit.tabs.source.form.metadataConfigId": "Formato dei metadati", @@ -1830,10 +1830,10 @@ "collection.edit.tabs.source.form.oaiSource": "OAI Provider", // "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_BITSTREAMS": "Harvest metadata and bitstreams (requires ORE support)", - "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_BITSTREAMS": "Harvest metadati e flussi di bit (richiede il supporto ORE)", + "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_BITSTREAMS": "Harvest dei metadata e bitstream (richiede il supporto OAI-ORE)", // "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_REF": "Harvest metadata and references to bitstreams (requires ORE support)", - "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_REF": "Harvest di metadati e riferimenti a bitstream (richiede il supporto ORE)", + "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_REF": "Harvest di metadati e bitstream (richiede il supporto OAI-ORE)", // "collection.edit.tabs.source.form.options.harvestType.METADATA_ONLY": "Harvest metadata only", "collection.edit.tabs.source.form.options.harvestType.METADATA_ONLY": "Harvesta solo metadati", @@ -1845,7 +1845,7 @@ "collection.edit.tabs.source.form.postTransform": "Nome post trasformazione", // "collection.edit.tabs.source.form.forceSynchronization": "Force Synchronization", - "collection.edit.tabs.source.form.forceSynchronization": "Forza sincronizzazione", + "collection.edit.tabs.source.form.forceSynchronization": "Forza la sincronizzazione", // "collection.edit.tabs.source.form.recordValidationEnabled": "Record validation", "collection.edit.tabs.source.form.recordValidationEnabled": "Convalida dei record", @@ -1883,7 +1883,7 @@ // "collection.edit.template.add-button": "Add", - "collection.edit.template.add-button": "Aggiungere", + "collection.edit.template.add-button": "Aggiungi", // "collection.edit.template.breadcrumbs": "Item template", "collection.edit.template.breadcrumbs": "Modello di Item", @@ -1892,38 +1892,38 @@ "collection.edit.template.cancel": "Annulla", // "collection.edit.template.delete-button": "Delete", - "collection.edit.template.delete-button": "Cancellare", + "collection.edit.template.delete-button": "Cancella", // "collection.edit.template.edit-button": "Edit", - "collection.edit.template.edit-button": "Editare", + "collection.edit.template.edit-button": "Modifica", // "collection.edit.template.error": "An error occurred retrieving the template item", - "collection.edit.template.error": "Si è verificato un errore durante il recupero del modello dell' Item", + "collection.edit.template.error": "Si è verificato un errore durante il recupero del template dell'Item", // "collection.edit.template.head": "Edit Template Item for Collection \"{{ collection }}\"", - "collection.edit.template.head": "Modifica item modello per la collection \"{{ collection }}\"", + "collection.edit.template.head": "Modifica il template dell'item per la collezione \"{{ collection }}\"", // "collection.edit.template.label": "Template item", - "collection.edit.template.label": "Template per Item", + "collection.edit.template.label": "Template dell'Item", // "collection.edit.template.loading": "Loading template item...", - "collection.edit.template.loading": "Caricamento del Template per Item...", + "collection.edit.template.loading": "Caricamento del Template per Item in corso...", // "collection.edit.template.notifications.delete.error": "Failed to delete the item template", - "collection.edit.template.notifications.delete.error": "Impossibile eliminare il Template dell' Item", + "collection.edit.template.notifications.delete.error": "Impossibile eliminare il template dell'Item", // "collection.edit.template.notifications.delete.success": "Successfully deleted the item template", - "collection.edit.template.notifications.delete.success": "Eliminato correttamente il Template dell' Item", + "collection.edit.template.notifications.delete.success": "Eliminato correttamente il template dell'Item", // "collection.edit.template.title": "Edit Template Item", - "collection.edit.template.title": "Modifica Template dell' Item", + "collection.edit.template.title": "Modifica il template dell'Item", // "collection-export.success": "Export of the collection's items started successfully", "collection-export.success": "L'esportazione degli item della collection è iniziata correttamente", // "collection-export.error": "An error occurs starting the collection's items export", - "collection-export.error": "Si verifica un errore durante l'avvio dell'esportazione degli item della collection", + "collection-export.error": "Si verifica un errore nell'export degli item della collezione", // "collection.form.abstract": "Short Description", @@ -1936,7 +1936,7 @@ "collection.form.errors.title.required": "Inserisci il nome di una collection", // "collection.form.license": "License", - "collection.form.license": "Licenza", + "collection.form.license": "Licenza di deposito", // "collection.form.provenance": "Provenance", "collection.form.provenance": "Provenienza", @@ -1954,10 +1954,10 @@ "collection.form.title": "Nome", // "collection.form.entityType": "Entity Type", - "collection.form.entityType": "Tipo di entità", + "collection.form.entityType": "Entità", // "collection.form.errors.entityType.required": "Please choose an entity type for this collection", - "collection.form.errors.entityType.required": "Scegli un tipo di entità per questa collection", + "collection.form.errors.entityType.required": "Scegli un'entità per questa collection", // "collection.form.errors.submissionDefinition.required": "Please choose a submission definition for this collection", "collection.form.errors.submissionDefinition.required": "Scegli una definizione di submission per questa collection", @@ -1971,12 +1971,12 @@ // "collection.listelement.badge": "Collection", - "collection.listelement.badge": "collection", + "collection.listelement.badge": "Collezione", // "collection.page.browse.recent.head": "Recent Submissions", - "collection.page.browse.recent.head": "submissions recenti", + "collection.page.browse.recent.head": "Inserimenti recenti", // "collection.page.browse.recent.empty": "No items to show", "collection.page.browse.recent.empty": "Nessun item da mostrare", @@ -1985,13 +1985,13 @@ "collection.page.edit": "Modifica questa collection", // "collection.page.export": "Export items from this collection", - "collection.page.export": "Esportare item da questa collection", + "collection.page.export": "Esporta gli item da questa collezione", // "collection.page.handle": "Permanent URI for this collection", "collection.page.handle": "URI permanente per questa collection", // "collection.page.license": "License", - "collection.page.license": "Licenza", + "collection.page.license": "Licenza di deposito", // "collection.page.news": "News", "collection.page.news": "News", @@ -1999,7 +1999,7 @@ // "collection.select.confirm": "Confirm selected", - "collection.select.confirm": "Conferma selezionato", + "collection.select.confirm": "Conferma", // "collection.select.empty": "No collections to show", "collection.select.empty": "Nessuna collection da mostrare", From f89461c0cd75414b4ea17b9d84052f04e0642fb0 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 5 Oct 2023 16:17:51 +0200 Subject: [PATCH 160/195] [DSC-1277] CRIS/GLAM translation sync 5 --- src/assets/i18n/it.json5 | 84 ++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 84fee4e2926..e454ed44643 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -2011,39 +2011,39 @@ // "collection.source.controls.head": "Harvest Controls", "collection.source.controls.head": "Controlli del Harvester", // "collection.source.controls.test.submit.error": "Something went wrong with initiating the testing of the settings", - "collection.source.controls.test.submit.error": "Qualcosa è andato storto con l'avvio del test delle impostazioni", + "collection.source.controls.test.submit.error": "Qualcosa è andato storto durante l'avvio del test delle impostazioni", // "collection.source.controls.test.failed": "The script to test the settings has failed", "collection.source.controls.test.failed": "Lo script per testare le impostazioni non è riuscito", // "collection.source.controls.test.completed": "The script to test the settings has successfully finished", - "collection.source.controls.test.completed": "Lo script per testare le impostazioni è stato completato correttamente", + "collection.source.controls.test.completed": "La procedura per testare le impostazioni è stato completato correttamente", // "collection.source.controls.test.submit": "Test configuration", "collection.source.controls.test.submit": "Configurazione di prova", // "collection.source.controls.test.running": "Testing configuration...", - "collection.source.controls.test.running": "Configurazione di test...", + "collection.source.controls.test.running": "Configurazione di prova...", // "collection.source.controls.import.submit.success": "The import has been successfully initiated", - "collection.source.controls.import.submit.success": "L'importazione è stata avviata correttamente", + "collection.source.controls.import.submit.success": "L'import è stato avviato correttamente", // "collection.source.controls.import.submit.error": "Something went wrong with initiating the import", - "collection.source.controls.import.submit.error": "Qualcosa è andato storto con l'avvio dell'importazione", + "collection.source.controls.import.submit.error": "Qualcosa è andato storto nell'import", // "collection.source.controls.import.submit": "Import now", "collection.source.controls.import.submit": "Importa ora", // "collection.source.controls.import.running": "Importing...", - "collection.source.controls.import.running": "Importazione...", + "collection.source.controls.import.running": "Import in corso...", // "collection.source.controls.import.failed": "An error occurred during the import", - "collection.source.controls.import.failed": "Si è verificato un errore durante l'importazione", + "collection.source.controls.import.failed": "Si è verificato un errore durante l'import ", // "collection.source.controls.import.completed": "The import completed", - "collection.source.controls.import.completed": "L'importazione completata", + "collection.source.controls.import.completed": "L'import è completato", // "collection.source.controls.reset.submit.success": "The reset and reimport has been successfully initiated", - "collection.source.controls.reset.submit.success": "Il ripristino e la reimportazione sono stati avviati correttamente", + "collection.source.controls.reset.submit.success": "Il reset e il reimport sono stati avviati correttamente", // "collection.source.controls.reset.submit.error": "Something went wrong with initiating the reset and reimport", - "collection.source.controls.reset.submit.error": "Qualcosa è andato storto con l'avvio del ripristino e della reimportazione", + "collection.source.controls.reset.submit.error": "Qualcosa è andato storto durante l'avvio del reset e reimport", // "collection.source.controls.reset.failed": "An error occurred during the reset and reimport", - "collection.source.controls.reset.failed": "Si è verificato un errore durante il ripristino e la reimportazione", + "collection.source.controls.reset.failed": "Si è verificato un errore durante il reimport", // "collection.source.controls.reset.completed": "The reset and reimport completed", - "collection.source.controls.reset.completed": "Il ripristino e la reimportazione completati", + "collection.source.controls.reset.completed": "Reset e reimport completati", // "collection.source.controls.reset.submit": "Reset and reimport", - "collection.source.controls.reset.submit": "Reimpostazione e reimportazione", + "collection.source.controls.reset.submit": "Reset e reimport", // "collection.source.controls.reset.running": "Resetting and reimporting...", - "collection.source.controls.reset.running": "Reimpostazione e reimportazione...", + "collection.source.controls.reset.running": "Reset e reimport...", // "collection.source.controls.harvest.status": "Harvest status:", "collection.source.controls.harvest.status": "Stato dell'harvest:", // "collection.source.controls.harvest.start": "Harvest start time:", @@ -2065,10 +2065,10 @@ // "communityList.breadcrumbs": "Community List", - "communityList.breadcrumbs": "Elenco della Community", + "communityList.breadcrumbs": "Elenco Community", // "communityList.tabTitle": "Community List", - "communityList.tabTitle": "Elenco della Community", + "communityList.tabTitle": "Elenco Community", // "communityList.title": "List of Communities", "communityList.title": "Elenco delle Community", @@ -2097,7 +2097,7 @@ "community.delete.confirm": "Conferma", // "community.delete.processing": "Deleting...", - "community.delete.processing": "Eliminazione...", + "community.delete.processing": "Eliminazione in corso...", // "community.delete.head": "Delete Community", "community.delete.head": "Elimina la community", @@ -2146,7 +2146,7 @@ "community.edit.logo.notifications.delete.error.title": "Errore durante l'eliminazione del logo", // "community.edit.logo.upload": "Drop a Community Logo to upload", - "community.edit.logo.upload": "Rilascia un logo della community da caricare", + "community.edit.logo.upload": "Trascina qui il logo da caricare per la community", @@ -2205,7 +2205,7 @@ "comcol-role.edit.create.error.title": "Non è stato possibile creare un gruppo per il ruolo '{{ role }}'", // "comcol-role.edit.restrict": "Restrict", - "comcol-role.edit.restrict": "Restringi", + "comcol-role.edit.restrict": "Limita", // "comcol-role.edit.delete": "Delete", "comcol-role.edit.delete": "Cancella", @@ -2222,58 +2222,58 @@ // "comcol-role.edit.community-admin.description": "Community administrators can create sub-communities or collections, and manage or assign management for those sub-communities or collections. In addition, they decide who can submit items to any sub-collections, edit item metadata (after submission), and add (map) existing items from other collections (subject to authorization).", - "comcol-role.edit.community-admin.description": "Gli amministratori della community possono creare sotto-community o collections e gestire o assegnare la loro gestione. Inoltre, decidono chi può depositare item in qualsiasi sotto-collections, modificare i metadati degli item (dopo l'invio) e aggiungere (mappare) item esistenti da altre collections (previa autorizzazione).", + "comcol-role.edit.community-admin.description": "Gli amministratori della community possono creare sotto-community o collections e gestirne o assegnarne l'amministrazione ad altri gruppi. Inoltre, decidono chi può inserire gli item in qualsiasi sotto-collezione, modificare i metadati degli item e aggiungere (mappare) item esistenti da altre collezioni (previa autorizzazione).", // "comcol-role.edit.collection-admin.description": "Collection administrators decide who can submit items to the collection, edit item metadata (after submission), and add (map) existing items from other collections to this collection (subject to authorization for that collection).", - "comcol-role.edit.collection-admin.description": "Gli amministratori della collection decidono chi può depositare item nella collection, modificare i metadati dell'item (dopo l'invio) e aggiungere (mappare) item da altre collections a questa collection (soggetto all'autorizzazione per tale collection).", + "comcol-role.edit.collection-admin.description": "Gli amministratori della collezione decidono chi può inserire item nella collection, modificare i metadati dell'item e aggiungere (mappare) item da altre collezioni.", // "comcol-role.edit.submitters.name": "Submitters", - "comcol-role.edit.submitters.name": "Submitters", + "comcol-role.edit.submitters.name": "Catalogatori", // "comcol-role.edit.submitters.description": "The E-People and Groups that have permission to submit new items to this collection.", - "comcol-role.edit.submitters.description": "E-People e Gruppi che dispongono dell'autorizzazione per depositare nuovi item in questa collection.", + "comcol-role.edit.submitters.description": "Operatori e Gruppi che dispongono dell'autorizzazione per inserire nuovi item in questa collezione.", // "comcol-role.edit.item_read.name": "Default item read access", - "comcol-role.edit.item_read.name": "Accesso di sola lettura per gli item", + "comcol-role.edit.item_read.name": "Accesso di sola lettura per l'item", // "comcol-role.edit.item_read.description": "E-People and Groups that can read new items submitted to this collection. Changes to this role are not retroactive. Existing items in the system will still be viewable by those who had read access at the time of their addition.", - "comcol-role.edit.item_read.description": "E-People e gruppi in grado di leggere i nuovi item depositati in questa collection. Le modifiche a questo ruolo non sono retroattive. Gli item esistenti nel sistema saranno ancora visualizzabili da coloro che avevano accesso in sola lettura al momento della loro archiviazione.", + "comcol-role.edit.item_read.description": "Operatori e gruppi abilitati a leggere i nuovi item inseriti. Le modifiche a questo ruolo non sono retroattive. Gli item esistenti nel sistema saranno ancora visualizzabili da coloro che avevano accesso in sola lettura al momento della loro archiviazione.", // "comcol-role.edit.item_read.anonymous-group": "Default read for incoming items is currently set to Anonymous.", - "comcol-role.edit.item_read.anonymous-group": "L'accesso di sola lettura per gli item depositati è attualmente impostata su Anonimo.", + "comcol-role.edit.item_read.anonymous-group": "L'accesso di sola lettura per gli item inseriti è attualmente impostata su Anonymous.", // "comcol-role.edit.bitstream_read.name": "Default bitstream read access", "comcol-role.edit.bitstream_read.name": "Accesso di sola lettura per il bitstream", // "comcol-role.edit.bitstream_read.description": "Community administrators can create sub-communities or collections, and manage or assign management for those sub-communities or collections. In addition, they decide who can submit items to any sub-collections, edit item metadata (after submission), and add (map) existing items from other collections (subject to authorization).", - "comcol-role.edit.bitstream_read.description": "Gli amministratori della community possono creare sotto-community o collections e gestire o assegnare la loro gestione. Inoltre, decidono chi può depositare item in qualsiasi sotto-collections, modificare i metadati degli item (dopo l'invio) e aggiungere (mappare) item esistenti da altre collections (previa autorizzazione).", + "comcol-role.edit.bitstream_read.description": "Gli amministratori della community possono creare sotto-community o collezioni e gestirne o assegnarne l'amministrazione. Inoltre, decidono chi può depositare item in qualsiasi sotto-collezione, modificare i metadati degli item e aggiungere (mappare) item esistenti da altre collezioni(previa autorizzazione).", // "comcol-role.edit.bitstream_read.anonymous-group": "Default read for incoming bitstreams is currently set to Anonymous.", - "comcol-role.edit.bitstream_read.anonymous-group": "L'accesso di sola lettura per i bitstreams depositati è attualmente impostata su Anonimo.", + "comcol-role.edit.bitstream_read.anonymous-group": "L'accesso di sola lettura per i bitstreams inseriti è attualmente impostata su Anonymous.", // "comcol-role.edit.editor.name": "Editors", - "comcol-role.edit.editor.name": "Editor", + "comcol-role.edit.editor.name": "Validatori", // "comcol-role.edit.editor.description": "Editors are able to edit the metadata of incoming submissions, and then accept or reject them.", - "comcol-role.edit.editor.description": "Gli editor possono modificare i metadati delle submissions depositate e quindi accettarli o rifiutarli.", + "comcol-role.edit.editor.description": "I validatori possono modificare i metadati delle submissions depositate e quindi accettarli o rifiutarli.", // "comcol-role.edit.finaleditor.name": "Final editors", - "comcol-role.edit.finaleditor.name": "Editor finali", + "comcol-role.edit.finaleditor.name": "Validatori di secondo livello", // "comcol-role.edit.finaleditor.description": "Final editors are able to edit the metadata of incoming submissions, but will not be able to reject them.", - "comcol-role.edit.finaleditor.description": "Gli editor finali sono in grado di modificare i metadati delle submissions depositate, ma non saranno in grado di rifiutarli.", + "comcol-role.edit.finaleditor.description": " I validatori di secondo livello sono in grado di modificare i metadati delle catalogazioni inserite, ma non potranno rifiutarle.", // "comcol-role.edit.reviewer.name": "Reviewers", "comcol-role.edit.reviewer.name": "Revisori", // "comcol-role.edit.reviewer.description": "Reviewers are able to accept or reject incoming submissions. However, they are not able to edit the submission's metadata.", - "comcol-role.edit.reviewer.description": "I revisori sono in grado di accettare o rifiutare le submissions depositate. Tuttavia, non sono in grado di modificare i metadati della submission.", + "comcol-role.edit.reviewer.description": "I revisori sono in grado di accettare o rifiutare le catalogazioni inserite. Tuttavia, non sono in grado di modificarne i metadati.", // "comcol-role.edit.scorereviewers.name": "Score Reviewers", @@ -2917,7 +2917,7 @@ "confirmation-modal.export-metadata.confirm": "Export", // "confirmation-modal.export-batch.header": "Export batch (ZIP) for {{ dsoName }}", - "confirmation-modal.export-batch.header": "Esporta batch (ZIP) per {{ dsoName }}", + "confirmation-modal.export-batch.header": "Export Batch (ZIP) da {{ dsoName }}", // "confirmation-modal.export-batch.info": "Are you sure you want to export batch (ZIP) for {{ dsoName }}", "confirmation-modal.export-batch.info": "Sei sicuro di voler esportare batch (ZIP) per {{ dsoName }}?", @@ -2926,7 +2926,7 @@ "confirmation-modal.export-batch.cancel": "Annulla", // "confirmation-modal.export-batch.confirm": "Export", - "confirmation-modal.export-batch.confirm": "Esporta", + "confirmation-modal.export-batch.confirm": "Export", // "confirmation-modal.delete-eperson.header": "Delete EPerson \"{{ dsoName }}\"", "confirmation-modal.delete-eperson.header": "Elimina EPerson \"{{ dsoName }}\"", @@ -3065,7 +3065,7 @@ "error.validation.groupExists": "Gruppo già esistente", // "error.validation.fundingInvestigatorOrLeadOrganizationRequired" : "At least one investigator or one lead organization is required", - "error.validation.fundingInvestigatorOrLeadOrganizationRequired" : "È richiesto almeno un ricercatore o una unità principale", + "error.validation.fundingInvestigatorOrLeadOrganizationRequired" : "È necessario inserire almeno un revisore o un'organizzazione dirigente", // "error.validation.notRepeatable": "This field is not repeatable, please choose only one value and discard the others.", "error.validation.notRepeatable": "Questo campo non è ripetibile, scegliere un solo valore e scartare gli altri.", @@ -3089,7 +3089,7 @@ "explore.facet-section.title" : "Scopri", // "explore.index.all" : "All", - "explore.index.all" : "Tutti", + "explore.index.all" : "Tutto", // "explore.index.author" : "Author", "explore.index.author" : "Autore", @@ -3113,17 +3113,17 @@ "explore.index.metric.scopus.citation" : "Più citati", // "explore.index.chart.pie.itemtype_filter" : "Item Type", - "explore.index.chart.pie.itemtype_filter" : "Tipo di item", + "explore.index.chart.pie.itemtype_filter" : "Tipologia di Item", // "explore.index.chart.bar.dateIssued.year": "Year", "explore.index.chart.bar.dateIssued.year": "Anno", // "explore.index.dateIssued" : "Date issued", - "explore.index.dateIssued" : "Data di inserimento", + "explore.index.dateIssued" : "Data puntuale", // "explore.index.dateissued" : "Date issued", - "explore.index.dateissued" : "Data di inserimento", + "explore.index.dateissued" : "Data puntuale", // "explore.index.dc.date.accessioned" : "Recent Additions", "explore.index.dc.date.accessioned" : "Aggiunte recenti", @@ -3153,7 +3153,7 @@ "explore.index.funding" : "Finanziamento", // "explore.index.givenName" : "Given name", - "explore.index.givenName" : "Nome", + "explore.index.givenName" : "Nome puntuale", // "explore.index.has_content_in_original_bundle" : "Has content in original bundle", "explore.index.has_content_in_original_bundle" : "Sono presenti dei contenuti nel bundle original", @@ -3207,7 +3207,7 @@ "explore.index.itemidentifier" : "Identificativo dell'item", // "explore.index.jobTitle" : "Job title", - "explore.index.jobTitle" : "Ruolo", + "explore.index.jobTitle" : "Titolo", // "explore.index.knowsLanguage" : "Knows languages", "explore.index.knowsLanguage" : "Lingue conosciute", From ef123bf5e9aff278a960a78490a7fa63bee59184 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 16:20:50 +0200 Subject: [PATCH 161/195] [CST-10703] minor fix --- src/app/core/auth/models/auth.registration-type.ts | 5 +---- .../review-account-info/review-account-info.component.ts | 1 - .../confirm-email/confirm-email.component.ts | 3 ++- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/app/core/auth/models/auth.registration-type.ts b/src/app/core/auth/models/auth.registration-type.ts index e43094ed7bc..aa9f9d88b67 100644 --- a/src/app/core/auth/models/auth.registration-type.ts +++ b/src/app/core/auth/models/auth.registration-type.ts @@ -1,7 +1,4 @@ export enum AuthRegistrationType { - Password = 'password', - Shibboleth = 'shibboleth', - Oidc = 'oidc', Orcid = 'ORCID', - Validation = 'validation', + Validation = 'VALIDATION', } diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index 81c237b7fc2..fc065fdf1d0 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -165,7 +165,6 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { const dataToCompare: ReviewAccountInfoData[] = []; Object.entries(this.registrationData.registrationMetadata).forEach( ([key, value]) => { - console.log(key, value); dataToCompare.push({ label: key.split('.')?.[1] ?? key.split('.')?.[0], currentValue: value[0]?.overrides ?? '', diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 1e2191db664..df57939021b 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -98,7 +98,8 @@ export class ConfirmEmailComponent implements OnDestroy { token: string, registrationData: Registration ) { - + // check if the netId is present + // in order to create an account, the netId is required (since the user is created without a password) if (hasNoValue(this.registrationData.netId)) { this.notificationService.error(this.translate.get('external-login-page.confirm-email.create-account.notifications.error.no-netId')); return; From 8ee09346a64f3b36b306bdd3ab71bbe3fa48f96d Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 16:36:57 +0200 Subject: [PATCH 162/195] [CST-10703] fix --- .../review-account-info/review-account-info.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index fc065fdf1d0..bf5099c3767 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -130,10 +130,8 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); } this.subs.push( - override$.subscribe((response: RemoteData) => { + override$.subscribe((response) => { if (response.hasSucceeded) { - // TODO: remove this line (temporary) - console.log('mergeEPersonDataWithToken', response.payload); this.notificationService.success( this.translateService.get( 'review-account-info.merge-data.notification.success' From f28555a274ee27dbff64dba1a1f97465b5b1817f Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 5 Oct 2023 17:19:57 +0200 Subject: [PATCH 163/195] [DSC-1277] CRIS/GLAM translation sync 6 (last) --- src/assets/i18n/it.json5 | 140 +++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index e454ed44643..aef5c4b4596 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -3630,10 +3630,10 @@ // "health.breadcrumbs": "Health", - "health.breadcrumbs": "Health", + "health.breadcrumbs": "Stato del sistema", // "health-page.heading" : "Health", - "health-page.heading" : "Health", + "health-page.heading" : "Stato del sistema", // "health-page.info-tab" : "Info", "health-page.info-tab" : "Informazioni", @@ -3645,7 +3645,7 @@ "health-page.error.msg": "Il servizio di health check è temporaneamente non disponibile.", // "health-page.property.status": "Status code", - "health-page.property.status": "Codice di stato", + "health-page.property.status": "Status code", // "health-page.section.db.title": "Database", "health-page.section.db.title": "Database", @@ -3684,7 +3684,7 @@ "health-page.status.warning.info": "Sono stati rilevati dei potenziali problemi", // "health-page.title": "Health", - "health-page.title": "Health", + "health-page.title": "Stato del sistema", // "health-page.section.no-issues": "No issues detected", "health-page.section.no-issues": "Nessun problema rilevato", @@ -4785,7 +4785,7 @@ "item.preview.dc.contributor.applicant": "Richiedente", // "item.preview.dc.contributor.editor": "Editors:", - "item.preview.dc.contributor.editor": "Editori:", + "item.preview.dc.contributor.editor": "Editore:", // "item.preview.dc.date.embargoEnd" : "Embargo End", "item.preview.dc.date.embargoEnd" : "Fine embargo", @@ -4797,7 +4797,7 @@ "item.preview.dc.description.abstract": "Abstract:", // "item.preview.dc.description.sponsorship" : "Sponsorship", - "item.preview.dc.description.sponsorship" : "Sponsorship", + "item.preview.dc.description.sponsorship" : "Sponsorizzazione", // "item.preview.dc.identifier.other": "Other identifier:", "item.preview.dc.identifier.other": "Altri identificativi:", @@ -4818,7 +4818,7 @@ "item.preview.dc.rights.uri" : "URI Diritti", // "item.preview.dc.subject": "Subjects:", - "item.preview.dc.subject": "Soggetti:", + "item.preview.dc.subject": "Contenuto:", // "item.preview.dc.title": "Title:", "item.preview.dc.title": "Titolo:", @@ -4878,7 +4878,7 @@ "item.preview.oairecerif.funder" : "Finanziatore", // "item.preview.oairecerif.funding.identifier" : "Grant Number / Funding identifier", - "item.preview.oairecerif.funding.identifier" : "Numero di finanziamento / Identificativo di finanziamento", + "item.preview.oairecerif.funding.identifier" : "Numero di gestione/ Identificativo del fondo", // "item.preview.oairecerif.funding.endDate" : "End Date", "item.preview.oairecerif.funding.endDate" : "Data finale", @@ -6155,13 +6155,13 @@ "nav.statistics.header": "Statistiche", // "nav.stop-impersonating": "Stop impersonating EPerson", - "nav.stop-impersonating": "Smetti di impersonare EPerson", + "nav.stop-impersonating": "Smetti di controllare l'operatore", // "nav.subscriptions" : "Subscriptions", - "nav.subscriptions" : "Subscription", + "nav.subscriptions" : "Sottoscrizioni", // "nav.toggle" : "Toggle navigation", - "nav.toggle" : "Attivare la navigazione", + "nav.toggle" : "Toggle di navigazione", // "nav.user.description" : "User profile bar", "nav.user.description" : "Barra del profilo utente", @@ -6239,10 +6239,10 @@ "openaire.broker.event.action.import": "Importa progetto e accetta suggerimenti", // "openaire.broker.event.table.pidtype": "PID Type:", - "openaire.broker.event.table.pidtype": "Tipo di PID", + "openaire.broker.event.table.pidtype": "Tipologia PID:", // "openaire.broker.event.table.pidvalue": "PID Value:", - "openaire.broker.event.table.pidvalue": "Valore di PID:", + "openaire.broker.event.table.pidvalue": "Valore PID:", // "openaire.broker.event.table.subjectValue": "Subject Value:", "openaire.broker.event.table.subjectValue": "Valore del soggetto:", @@ -6281,7 +6281,7 @@ "openaire.broker.event.table.more": "Mostra di più", // "openaire.broker.event.project.found": "Bound to the local record:", - "openaire.broker.event.project.found": "Legato al record locale:", + "openaire.broker.event.project.found": "Aggancia al record:", // "openaire.broker.event.project.notFound": "No local record found", "openaire.broker.event.project.notFound": "Nessun record locale trovato", @@ -6314,7 +6314,7 @@ "openaire.broker.event.modal.project.publication": "Pubblicazione:", // "openaire.broker.event.modal.project.bountToLocal": "Bound to the local record:", - "openaire.broker.event.modal.project.bountToLocal": "Legato al record locale:", + "openaire.broker.event.modal.project.bountToLocal": "Aggancia al record:", // "openaire.broker.event.modal.project.select": "Project search", "openaire.broker.event.modal.project.select": "Ricerca del progetto", @@ -6474,7 +6474,7 @@ "orgunit.page.id": "ID", // "orgunit.page.titleprefix": "Organizational Unit: ", - "orgunit.page.titleprefix": "Unità organizzativa: ", + "orgunit.page.titleprefix": "Unità Organizzativa: ", @@ -6538,7 +6538,7 @@ "person.search.results.head": "Risultati della ricerca per persona", // "person-relationships.search.results.head": "Person Search Results", - "person-relationships.search.results.head": "Risultati della ricerca per persona", + "person-relationships.search.results.head": "Risultati della ricerca", // "person.search.title": "Person Search", "person.search.title": "Cerca i Ricercatori", @@ -6579,7 +6579,7 @@ "process.new.parameter.type.file": "file", // "process.new.parameter.required.missing": "The following parameters are required but still missing:", - "process.new.parameter.required.missing": "I seguenti parametri mancanti sono obbligatori:", + "process.new.parameter.required.missing": "I seguenti parametri sono obbligatori:", // "process.new.notification.success.title": "Success", "process.new.notification.success.title": "Successo", @@ -6597,7 +6597,7 @@ "process.new.notification.process.processing": "Elaborazione...", // "process.new.notification.process.files": "Output Files: ", - "process.new.notification.process.files": "File di output: ", + "process.new.notification.process.files": "File di Output: ", // "process.new.header": "Create a new process", "process.new.header": "Creare un nuovo processo", @@ -6653,7 +6653,7 @@ "process.detail.status" : "Stato", // "process.detail.create" : "Create similar process", - "process.detail.create" : "Crea un processo analogo", + "process.detail.create" : "Crea un processo simile", // "process.detail.actions": "Actions", "process.detail.actions": "Azioni", @@ -6677,7 +6677,7 @@ "process.detail.delete.success": "Il processo è stato eliminato con successo", // "process.detail.delete.error": "Something went wrong when deleting the process", - "process.detail.delete.error": "Qualcosa è andato storto durante l'elininazione del processo", + "process.detail.delete.error": "Errore durante l'eliminazione del processo", // "process.overview.delete.failed" : "An error occurs deleting the process.", "process.overview.delete.failed" : "Si è verificato un errore durante l'eliminazine del processo.", @@ -6820,10 +6820,10 @@ "profile.card.access-token.copy": "Copia token", // "profile.card.access-token.copy-info": "Make sure to copy your personal access token now. You won’t be able to see it again!", - "profile.card.access-token.copy-info": "Assicurati di copiare il tuo token di accesso personale. Non potrai più farlo in seguito!", + "profile.card.access-token.copy-info": "Assicurati di aver copiato il tuo token di accesso personale. Non potrai più farlo in seguito!", // "profile.card.access-token.info": "Tokens you have generated that can be used to access the DSpace-CRIS API.", - "profile.card.access-token.info": "I token che hai generato che possono essere utilizzati per accedere all'API di DSpace-CRIS", + "profile.card.access-token.info": "Token generati che possono essere usati per accedere alle API di DSpace-CRIS", // "profile.card.access-token.create.error": "An error occurred while generating the token, please try again later.", "profile.card.access-token.create.error": "Si è verificato un errore durante la generazione del token, si prega di riprovare più tardi.", @@ -6853,10 +6853,10 @@ "profile.card.access-token.delete": "Revoca il token", // "profile.card.access-token.no-token-generated": "You don't have personal access token. Generate a personal access token for quick access to the DSpace-CRIS API.", - "profile.card.access-token.no-token-generated": "Non sei in possesso di un token di accesso personale. Genera un token per accedere velocemente all'API di DSpace-CRIS.", + "profile.card.access-token.no-token-generated": "Non si dispone di un token di accesso personale. Generare un token di accesso personale per accedere rapidamente all'API DSpace-CRIS.", // "profile.card.access-token.token-generated": "You have already generated a personal access token.", - "profile.card.access-token.token-generated": "Hai già generato un token di accesso personale.", + "profile.card.access-token.token-generated": "Avete già generato un token di accesso personale.", // "profile.card.identify": "Identify", "profile.card.identify": "Identificare", @@ -6977,7 +6977,7 @@ "project.page.status": "Parole chiave", // "project.page.titleprefix": "Research Project: ", - "project.page.titleprefix": "Progetto di ricerca: ", + "project.page.titleprefix": "Progetti di ricerca: ", // "project.search.results.head": "Project Search Results", "project.search.results.head": "Risultati della ricerca per progetti", @@ -7295,10 +7295,10 @@ "resource-policies.edit.page.failure.content": "Si è verificato un errore durante la modifica della policy di risorsa.", // "resource-policies.edit.page.target-failure.content": "An error occurred while editing the target (ePerson or group) of the resource policy.", - "resource-policies.edit.page.target-failure.content": "Si è verificato un errore durante la modifica dell'obiettivo (ePerson o gruppo) della policy di risorsa.", + "resource-policies.edit.page.target-failure.content": "Si è verificato un errore durante la modifica del target (EPerson o gruppo) del criterio della risorsa.", // "resource-policies.edit.page.other-failure.content": "An error occurred while editing the resource policy. The target (ePerson or group) has been successfully updated.", - "resource-policies.edit.page.other-failure.content": "Si è verificato un errore durante la modifica della policy di risorsa. L'obiettio (ePerson o gruppo) è stato aggiornato con successo.", + "resource-policies.edit.page.other-failure.content": "Si è verificato un errore durante la modifica del criterio della risorsa. Il target (EPerson o gruppo) è stato aggiornato con successo.", // "resource-policies.edit.page.success.content": "Operation successful", "resource-policies.edit.page.success.content": "Operazione riuscita", @@ -7334,16 +7334,16 @@ "resource-policies.form.eperson-group-list.table.headers.name": "Nome", // "resource-policies.form.eperson-group-list.modal.header": "Cannot change type", - "resource-policies.form.eperson-group-list.modal.header": "Impossibile modificare il tipo", + "resource-policies.form.eperson-group-list.modal.header": "Impossibile modificare il tipo.", // "resource-policies.form.eperson-group-list.modal.text1.toGroup": "It is not possible to replace an ePerson with a group.", - "resource-policies.form.eperson-group-list.modal.text1.toGroup": "Impossibile sostituire una ePerson con un gruppo.", + "resource-policies.form.eperson-group-list.modal.text1.toGroup": "Non è possibile sostituire una EPerson con un gruppo.", // "resource-policies.form.eperson-group-list.modal.text1.toEPerson": "It is not possible to replace a group with an ePerson.", - "resource-policies.form.eperson-group-list.modal.text1.toEPerson": "Impossibile sostituire un gruppo con una ePerson.", + "resource-policies.form.eperson-group-list.modal.text1.toEPerson": "Non è possibile sostituire un gruppo con una EPerson.", // "resource-policies.form.eperson-group-list.modal.text2": "Delete the current resource policy and create a new one with the desired type.", - "resource-policies.form.eperson-group-list.modal.text2": "Elimina la policy di risorsa corrente e creane una nuova con il tipo desiderato.", + "resource-policies.form.eperson-group-list.modal.text2": "Eliminare la policy della risorsa corrente e crearne una nuova con il tipo desiderato.", // "resource-policies.form.eperson-group-list.modal.close": "Ok", "resource-policies.form.eperson-group-list.modal.close": "Ok", @@ -7517,16 +7517,16 @@ "search.filters.applied.charts.defaultConfiguration.title": "Output di ricerca", // "search.filters.applied.charts.chart.bar.dateIssued.year.tab" : "Date Issued", - "search.filters.applied.charts.chart.bar.dateIssued.year.tab" : "Data di inserimento", + "search.filters.applied.charts.chart.bar.dateIssued.year.tab" : "Data puntuale", // "search.filters.applied.charts.chart.pie.more-value" : "More", - "search.filters.applied.charts.chart.pie.more-value" : "Più", + "search.filters.applied.charts.chart.pie.more-value" : "Visualizza altri", // "search.filters.applied.charts.chart.pie.itemtype_filter.tab" : "Item Type", - "search.filters.applied.charts.chart.pie.itemtype_filter.tab" : "Tipo di item", + "search.filters.applied.charts.chart.pie.itemtype_filter.tab" : "Tipologia dell'item", // "search.filters.applied.charts.graphitemtype.tab" : "Visualization by Type", - "search.filters.applied.charts.graphitemtype.tab" : "Visualizzazione per tipo", + "search.filters.applied.charts.graphitemtype.tab" : "Visualizzazione per tipologia", // "search.filters.applied.charts.graphpubldate.tab" : "Visualization by Date", "search.filters.applied.charts.graphpubldate.tab" : "Visualizzazione per data", @@ -7655,7 +7655,7 @@ "search.filters.filter.editor.placeholder": "Editore", // "search.filters.filter.editor.label": "Search editor", - "search.filters.filter.editor.label": "Cerca editore", + "search.filters.filter.editor.label": "Ricerca Editore", // "search.filters.filter.withdrawn.head": "Withdrawn", "search.filters.filter.withdrawn.head": "Ritirato", @@ -7691,7 +7691,7 @@ "search.filters.filter.funding.placeholder": "Finanziamento", // "search.filters.filter.funding.label": "Search funding", - "search.filters.filter.funding.label": "Cerca finanziamento", + "search.filters.filter.funding.label": "Ricerca fondi", // "search.filters.filter.has_content_in_original_bundle.head": "Has files", "search.filters.filter.has_content_in_original_bundle.head": "Ha file", @@ -7742,22 +7742,22 @@ "search.filters.filter.language.placeholder": "Lingue", // "search.filters.filter.projectOrgUnits.head" : "Organizations", - "search.filters.filter.projectOrgUnits.head" : "Strutture", + "search.filters.filter.projectOrgUnits.head" : "Organizzazioni", // "search.filters.filter.projectOrgUnits.label" : "Organizations", "search.filters.filter.projectOrgUnits.label" : "Strutture", // "search.filters.filter.projectOrgUnits.placeholder" : "Organizations", - "search.filters.filter.projectOrgUnits.placeholder" : "Strutture", + "search.filters.filter.projectOrgUnits.placeholder" : "Organizzazioni", // "search.filters.filter.personOrgUnits.head" : "Organizations", - "search.filters.filter.personOrgUnits.head" : "Strutture", + "search.filters.filter.personOrgUnits.head" : "Organizzazioni", // "search.filters.filter.personOrgUnits.label" : "Search organizations", - "search.filters.filter.personOrgUnits.label" : "Cerca strutture", + "search.filters.filter.personOrgUnits.label" : "Ricerca Organizzazioni", // "search.filters.filter.personOrgUnits.placeholder" : "Organizations", - "search.filters.filter.personOrgUnits.placeholder" : "Strutture", + "search.filters.filter.personOrgUnits.placeholder" : "Organizzazioni", // "search.filters.filter.namedresourcetype.head": "Status", "search.filters.filter.namedresourcetype.head": "Stato", @@ -7784,7 +7784,7 @@ "search.filters.filter.organization.placeholder": "Struttura", // "search.filters.filter.organization.label": "Search organization", - "search.filters.filter.organization.label": "Cerca struttura", + "search.filters.filter.organization.label": "Ricerca Organizzazioni", // "search.filters.filter.organizationAddressCountry.head": "Country", "search.filters.filter.organizationAddressCountry.head": "Paese", @@ -8014,10 +8014,10 @@ "sorting.dc.title.DESC": "Titolo decrescente", // "sorting.metric.view.ASC" : "Views Ascending", - "sorting.metric.view.ASC" : "Vista in ordine crescente", + "sorting.metric.view.ASC" : "Visualizzazioni in ordine crescente", // "sorting.metric.view.DESC" : "Views Descending", - "sorting.metric.view.DESC" : "Vista in ordine decrescente", + "sorting.metric.view.DESC" : "Visualizzazioni in ordine decrescente", // "sorting.metric.download.ASC" : "Downloads Ascending", "sorting.metric.download.ASC" : "Download in ordine crescente", @@ -8168,16 +8168,16 @@ "statistics.table.downloadReports.title.TopCities": "Più scaricati per città", // "statistics.table.mainReports.header.views": "Views", - "statistics.table.mainReports.header.views": "Visualizzazioni", + "statistics.table.mainReports.header.views": "Visite", // "statistics.table.mainReports.header.bitstream": "File Visits", - "statistics.table.mainReports.header.bitstream": "Visite al file", + "statistics.table.mainReports.header.bitstream": "Visualizzazioni del file", // "statistics.table.mainReports.header.continent": "Continent", "statistics.table.mainReports.header.continent": "Continente", // "statistics.table.mainReports.header.country": "Country", - "statistics.table.mainReports.header.country": "Paese", + "statistics.table.mainReports.header.country": "Nazione", // "statistics.table.mainReports.header.city": "City", "statistics.table.mainReports.header.city": "Città", @@ -8192,7 +8192,7 @@ "statistics.table.downloadReports.header.continent": "Continente", // "statistics.table.downloadReports.header.country": "Country", - "statistics.table.downloadReports.header.country": "Paese", + "statistics.table.downloadReports.header.country": "Nazione", // "statistics.table.downloadReports.header.city": "City", "statistics.table.downloadReports.header.city": "Città", @@ -8594,13 +8594,13 @@ "submission.sections.correction.column.file": "File", // "submission.sections.correction.column.file.info": "Here are the changes related the item's bitstream", - "submission.sections.correction.column.file.info": "Ecco le modifiche relative al bistream dell'item", + "submission.sections.correction.column.file.info": "Elenco delle modifiche relative al bitstream dell'Item", // "submission.sections.correction.column.metadata": "Metadata", "submission.sections.correction.column.metadata": "Metadati", // "submission.sections.correction.column.metadata.info": "Here are the changes related the item's metadata", - "submission.sections.correction.column.metadata.info": "Ecco le modifiche relative ai metadati dell'item", + "submission.sections.correction.column.metadata.info": "Ecco le modifiche relative ai metadati dell'item", // "submission.sections.correction.column.previous-value": "Previous value", "submission.sections.correction.column.previous-value": "Valore precedente", @@ -8609,7 +8609,7 @@ "submission.sections.correction.column.policy": "Policy", // "submission.sections.submit.progressbar.correction": "Correction details", - "submission.sections.submit.progressbar.correction": "Correzione dei dettagli", + "submission.sections.submit.progressbar.correction": "Dettagli della correzione", @@ -8665,10 +8665,10 @@ "submission.sections.detect-duplicate.not-duplicate-help": "Clicca qui se questo non è un duplicato del tuo articolo", // "submission.sections.detect-duplicate.submitter-decision": "Submitter decision:", - "submission.sections.detect-duplicate.submitter-decision": "Decisione del submitter:", + "submission.sections.detect-duplicate.submitter-decision": "Decisione del compilatore:", // "submission.sections.detect-duplicate.submitter-note": "Submitter note:", - "submission.sections.detect-duplicate.submitter-note": "Nota del submitter:", + "submission.sections.detect-duplicate.submitter-note": "Note del compilatore:", @@ -8814,7 +8814,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.new-entity": "Importato e aggiunto con successo volume di journal esterno alla selezione", // "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Select a local match:", - "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Seleziona una corrispondenza locale:", + "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Selezionare una corrispondenza locale:", // "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all", "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deseleziona tutto", @@ -9111,7 +9111,7 @@ "submission.sections.ccLicense.option.select": "Seleziona un'opzione...", // "submission.sections.ccLicense.link": "You’ve selected the following license:", - "submission.sections.ccLicense.link": "Hai selezionato la seguente licenza:", + "submission.sections.ccLicense.link": "Avete selezionato la seguente licenza:", // "submission.sections.ccLicense.confirmation": "I grant the license above", "submission.sections.ccLicense.confirmation": "Concedo la licenza di cui sopra", @@ -9120,7 +9120,7 @@ "submission.sections.general.add-more": "Aggiungi altro", // "submission.sections.general.cannot_deposit": "Deposit cannot be completed due to errors in the form.
Please fill out all required fields to complete the deposit.", - "submission.sections.general.cannot_deposit": "L'immissione non può essere competata a causa di errori nel modulo.
Si prega di compilare tutti i campi obbligatori.", + "submission.sections.general.cannot_deposit": "Il deposito non può essere completato a causa di errori nel modulo.
Si prega di compilare tutti i campi richiesti per completare il deposito.", // "submission.sections.general.collection": "Collection", "submission.sections.general.collection": "Collezione", @@ -9273,13 +9273,13 @@ "submission.sections.submit.progressbar.license": "Licenza di deposito", // "submission.sections.submit.progressbar.sherpapolicy": "Sherpa policies", - "submission.sections.submit.progressbar.sherpapolicy": "Policy di Sherpa", + "submission.sections.submit.progressbar.sherpapolicy": "Sherpa policies", // "submission.sections.submit.progressbar.upload": "Upload files", "submission.sections.submit.progressbar.upload": "Carica file", // "submission.sections.submit.progressbar.sherpaPolicies": "Publisher open access policy information", - "submission.sections.submit.progressbar.sherpaPolicies": "Informazioni sulla policy di open access dell'editore", + "submission.sections.submit.progressbar.sherpaPolicies": "Informazioni sulle policy open access dell'editore", // "submission.sections.submit.progressbar.correction-step": "Corrections", "submission.sections.submit.progressbar.correction-step": "Correzioni", @@ -9287,7 +9287,7 @@ // "submission.sections.sherpa-policy.title-empty": "No publisher policy information available. If your work has an associated ISSN, please enter it above to see any related publisher open access policies.", - "submission.sections.sherpa-policy.title-empty": "Non sono disponibili informazioni sulle policy dell'editore. Se il lavoro ha un ISSN associato, si prega di inserirlo qui sopra per vedere le policy di open access dell'editore.", + "submission.sections.sherpa-policy.title-empty": "Non sono disponibili informazioni sulle policy dell'editore. Se il vostro lavoro ha un ISSN associato, inseritelo qui sopra per vedere le open access policies dell'editore.", // "submission.sections.status.errors.title": "Errors", "submission.sections.status.errors.title": "Errori", @@ -9392,10 +9392,10 @@ "submission.sections.upload.form.until-placeholder": "Fino a quando", // "submission.sections.upload.header.policy.default.nolist": "Uploaded files in the {{collectionName}} collection will be accessible according to the following group(s):", - "submission.sections.upload.header.policy.default.nolist": "I file caricati nella collection {{collectionName}} saranno accessibili in base ai seguenti gruppi:", + "submission.sections.upload.header.policy.default.nolist": "I file caricati nella raccolta {{collectionName}} saranno accessibili ai seguenti gruppi:", // "submission.sections.upload.header.policy.default.withlist": "Please note that uploaded files in the {{collectionName}} collection will be accessible, in addition to what is explicitly decided for the single file, with the following group(s):", - "submission.sections.upload.header.policy.default.withlist": "Si prega di notare che i file caricati nella collection {{collectionName}} saranno accessibili, in aggiunta a quanto esplicitamente deciso per il singolo file, con i seguenti gruppi:", + "submission.sections.upload.header.policy.default.withlist": "Si noti che i file caricati nella raccolta {{collectionName}} saranno accessibili, oltre a quanto esplicitamente deciso per il singolo file, con i seguenti gruppi:", // "submission.sections.upload.info": "Here you will find all the files currently in the item. You can update the file metadata and access conditions or upload additional files by dragging & dropping them anywhere on the page.", "submission.sections.upload.info": "Qui troverai tutti i file attualmente presenti nell'item. È possibile aggiornare i metadati dei file e le condizioni di accesso o caricare file aggiuntivi semplicemente trascinandoli e rilasciandoli ovunque nella pagina", @@ -9507,10 +9507,10 @@ "submission.sections.sherpa.publisher.policy.description": "Le informazioni riportate di seguito sono state reperite tramite Sherpa Romeo. In base alle policy del vostro editore, fornisce consigli sull'eventuale necessità di un embargo e/o su quali file è possibile caricare. In caso di domande, contattare l'amministratore del sito tramite il modulo di feedback nel piè di pagina.", // "submission.sections.sherpa.publisher.policy.openaccess": "Open Access pathways permitted by this journal's policy are listed below by article version. Click on a pathway for a more detailed view", - "submission.sections.sherpa.publisher.policy.openaccess": "I percorsi open access consentiti dalle policy di questa rivista sono elencati di seguito per versione dell'articolo. Clicca su un percorso per vederlo nel dettaglio", + "submission.sections.sherpa.publisher.policy.openaccess": "I percorsi Open Access consentiti dalle policy di questa rivista sono elencati di seguito per versione dell'articolo. Clicca su un percorso per vederlo nel dettaglio", // "submission.sections.sherpa.publisher.policy.more.information": "For more information, please see the following links:", - "submission.sections.sherpa.publisher.policy.more.information": "Per maggiori informazioni si prega di consultare il seguente link:", + "submission.sections.sherpa.publisher.policy.more.information": "Per ulteriori informazioni, consultare i seguenti link:", // "submission.sections.sherpa.publisher.policy.version": "Version", "submission.sections.sherpa.publisher.policy.version": "Versione", @@ -9531,7 +9531,7 @@ "submission.sections.sherpa.publisher.policy.prerequisites": "Prerequisiti", // "submission.sections.sherpa.publisher.policy.location": "Location", - "submission.sections.sherpa.publisher.policy.location": "Località", + "submission.sections.sherpa.publisher.policy.location": "Posizione", // "submission.sections.sherpa.publisher.policy.conditions": "Conditions", "submission.sections.sherpa.publisher.policy.conditions": "Condizioni", @@ -9540,7 +9540,7 @@ "submission.sections.sherpa.publisher.policy.refresh": "Ricarica", // "submission.sections.sherpa.record.information": "Record Information", - "submission.sections.sherpa.record.information": "Informazioni sulla registrazione", + "submission.sections.sherpa.record.information": "Informazioni del record", // "submission.sections.sherpa.record.information.id": "ID", "submission.sections.sherpa.record.information.id": "ID", @@ -9555,7 +9555,7 @@ "submission.sections.sherpa.record.information.uri": "URI", // "submission.sections.sherpa.error.message": "There was an error retrieving sherpa informations", - "submission.sections.sherpa.error.message": "Si è verificato un errore nel recuperare le informazioni da Sherpa", + "submission.sections.sherpa.error.message": "Si è verificato un errore nel recupero delle informazioni da Sherpa", @@ -10562,7 +10562,7 @@ "curation-task.task.iiifpdfmultipages.label": "Creare un PDF multipagina dalle immagini IIIF", // "curation-task.task.iiifclean.label": "Remove all metadata bundles created by a previous upload to the image server", - "curation-task.task.iiifclean.label": "Rimuovere tutti i bundle di metadati generati da precedenti caricamenti sull'Image Server", + "curation-task.task.iiifclean.label": "Rimuovi tutti i bitstream e bundle creati durante il processamento della risorsa IIIF", // "curation-task.task.pdftoimagecmyk.label": "Extract images from PDF (CMYK)", "curation-task.task.pdftoimagecmyk.label": "Estrarre le immagini da PDF (CMYK)", @@ -10652,7 +10652,7 @@ // "admin.system-wide-alert.breadcrumbs": "System-wide Alerts", "admin.system-wide-alert.breadcrumbs": "Allarmi di sistema", - // "admin.system-wide-alert.title": "System-wide Alerts" + // "admin.system-wide-alert.title": "System-wide Alerts", "admin.system-wide-alert.title": "Allarmi di sistema", From 875f154e4ed86c7e978245797590c53469ce0ebc Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 5 Oct 2023 18:09:54 +0200 Subject: [PATCH 164/195] [CST-10703] unit tests --- .../external-login-page.component.spec.ts | 9 +-------- ...-review-account-info-page.component.spec.ts | 12 +----------- .../helpers/review-account.guard.spec.ts | 2 +- .../confirm-email.component.spec.ts | 1 + .../orcid-confirmation.component.spec.ts | 18 ++++++------------ .../registration-data.resolver.spec.ts | 12 ++++++------ 6 files changed, 16 insertions(+), 38 deletions(-) diff --git a/src/app/external-login-page/external-login-page.component.spec.ts b/src/app/external-login-page/external-login-page.component.spec.ts index 8dee4821a7a..3ea6724278a 100644 --- a/src/app/external-login-page/external-login-page.component.spec.ts +++ b/src/app/external-login-page/external-login-page.component.spec.ts @@ -32,7 +32,7 @@ describe('ExternalLoginPageComponent', () => { provide: ActivatedRoute, useValue: { snapshot: { - queryParams: { + params: { token: '1234567890', }, }, @@ -67,13 +67,6 @@ describe('ExternalLoginPageComponent', () => { expect(component.token).toEqual('1234567890'); }); - it('should set the hasErrors flag if the token is not present', () => { - const activatedRoute = TestBed.inject(ActivatedRoute); - activatedRoute.snapshot.queryParams.token = undefined; - fixture.detectChanges(); - expect(component.hasErrors).toBeTrue(); - }); - it('should display the DsExternalLogIn component when there are no errors', () => { const registrationData = Object.assign(new Registration(), registrationDataMock); component.registrationData$ = of(registrationData); diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts index ca65204e8dd..4b136cbef7a 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts @@ -10,7 +10,7 @@ describe('ExternalLoginReviewAccountInfoPageComponent', () => { const mockActivatedRoute = { snapshot: { - queryParams: { + params: { token: '1234567890' } }, @@ -43,16 +43,6 @@ describe('ExternalLoginReviewAccountInfoPageComponent', () => { expect(component.token).toEqual('1234567890'); }); - it('should set hasErrors to false if registrationData is not empty', () => { - expect(component.hasErrors).toBeFalse(); - }); - - it('should set the registrationData$', () => { - component.registrationData$.subscribe((registrationData) => { - expect(registrationData.email).toEqual(mockRegistrationDataModel.email); - }); - }); - it('should display review account info component when there are no errors', () => { component.hasErrors = false; component.registrationData$ = of(mockRegistrationDataModel); diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts index 9004e173204..3a8ee083586 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts @@ -68,7 +68,7 @@ describe('ReviewAccountGuard', () => { }); it('should navigate to 404 if the registration type is not validation and the user is not authenticated', () => { - registrationMock.registrationType = AuthRegistrationType.Password; + registrationMock.registrationType = AuthRegistrationType.Orcid; epersonRegistrationService.searchRegistrationByToken.and.returnValue(createSuccessfulRemoteDataObject$(registrationMock)); spyOn(authService, 'isAuthenticated').and.returnValue(of(false)); (guard.canActivate({ params: { token: 'invalid-token' } } as any, {} as any) as any).subscribe((result) => { diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts index c9c741672dd..2247d8de904 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -75,6 +75,7 @@ describe('ConfirmEmailComponent', () => { component.registrationData = Object.assign(new Registration(), { id: '123', email: 'test@example.com', + netId: 'test-netid', registrationMetadata: {}, registrationType: AuthMethodType.Orcid, }); diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts index e3f84663539..441a326ef53 100644 --- a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts +++ b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts @@ -2,16 +2,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { OrcidConfirmationComponent } from './orcid-confirmation.component'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { mockRegistrationDataModel } from '../../models/registration-data.mock.model'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; import { CommonModule } from '@angular/common'; import { BrowserOnlyMockPipe } from '../../../../shared/testing/browser-only-mock.pipe'; +import { Registration } from 'src/app/core/shared/registration.model'; +import { mockRegistrationDataModel } from '../../models/registration-data.mock.model'; describe('OrcidConfirmationComponent', () => { let component: OrcidConfirmationComponent; let fixture: ComponentFixture; + let model: Registration; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -32,7 +34,7 @@ describe('OrcidConfirmationComponent', () => { } }), ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); }); @@ -40,7 +42,6 @@ describe('OrcidConfirmationComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(OrcidConfirmationComponent); component = fixture.componentInstance; - component.registratioData = mockRegistrationDataModel; fixture.detectChanges(); }); @@ -59,17 +60,10 @@ describe('OrcidConfirmationComponent', () => { it('should initialize the form with null email as an empty string', () => { component.registratioData.email = null; - fixture.detectChanges(); component.ngOnInit(); + fixture.detectChanges(); const emailFormControl = component.form.get('email'); expect(emailFormControl.value).toBe(''); }); - it('should not render email input when email is null', () => { - component.registratioData.email = null; - fixture.detectChanges(); - component.ngOnInit(); - const emailInput = fixture.nativeElement.querySelector('input[type="email"]'); - expect(emailInput).toBeFalsy(); - }); }); diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts index a33434062b5..0d1f415a3b4 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts +++ b/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts @@ -4,7 +4,7 @@ import { RegistrationDataResolver } from './registration-data.resolver'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { Registration } from '../../../core/shared/registration.model'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; describe('RegistrationDataResolver', () => { let resolver: RegistrationDataResolver; @@ -14,7 +14,7 @@ describe('RegistrationDataResolver', () => { }); beforeEach(() => { - const spy = jasmine.createSpyObj('EpersonRegistrationService', ['searchByTokenAndUpdateData']); + const spy = jasmine.createSpyObj('EpersonRegistrationService', ['searchRegistrationByToken']); TestBed.configureTestingModule({ providers: [ @@ -33,14 +33,14 @@ describe('RegistrationDataResolver', () => { it('should resolve registration data based on a token', () => { const token = 'abc123'; const registrationRD$ = createSuccessfulRemoteDataObject$(registrationMock); - epersonRegistrationServiceSpy.searchByTokenAndUpdateData.and.returnValue(registrationRD$); - + epersonRegistrationServiceSpy.searchRegistrationByToken.and.returnValue(registrationRD$); const route = new ActivatedRouteSnapshot(); route.params = { token: token }; const state = {} as RouterStateSnapshot; - resolver.resolve(route, state).subscribe((result: Registration) => { - expect(result).toBeDefined(); + resolver.resolve(route, state).subscribe((data) => { + expect(data).toEqual(createSuccessfulRemoteDataObject(registrationMock)); }); + expect(epersonRegistrationServiceSpy.searchRegistrationByToken).toHaveBeenCalledWith(token); }); }); From 3efd491e68f5e1c0fab9b10a9461be371b43a157 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 6 Oct 2023 09:54:41 +0200 Subject: [PATCH 165/195] CST-11817 enable bitbucket pipeline over coar notify demo --- bitbucket-pipelines.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 bitbucket-pipelines.yml diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml new file mode 100644 index 00000000000..95040af1ead --- /dev/null +++ b/bitbucket-pipelines.yml @@ -0,0 +1,27 @@ +options: + runs-on: ubuntu-latest + +definitions: + steps: + - step: &unittest-code-checks + name: test-code-checks + image: + name: cypress/browsers:node18.12.0-chrome107 + run-as-user: 1000 + size: 2x + caches: + - node + script: + - yarn install --frozen-lockfile + - yarn run lint --quiet + - yarn run check-circ-deps + - yarn run build:prod + - yarn run test:headless + +pipelines: + branches: + 'coar-notify7-demo': + - step: *unittest-code-checks + pull-requests: + '**': + - step: *unittest-code-checks \ No newline at end of file From d9c4cbef536c68e23fbc576246dd287b05898a8c Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 6 Oct 2023 12:29:15 +0200 Subject: [PATCH 166/195] CST-11817 revert commit to the wrong branch --- bitbucket-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 95040af1ead..5cdc81bfd51 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -20,8 +20,8 @@ definitions: pipelines: branches: - 'coar-notify7-demo': + 'dspace-cris-7': - step: *unittest-code-checks pull-requests: '**': - - step: *unittest-code-checks \ No newline at end of file + - step: *unittest-code-checks From 24f242558fe1acd9fb9eebaf2200b0840813627e Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 6 Oct 2023 12:51:22 +0200 Subject: [PATCH 167/195] [CST-10703] handle authenticated user to merge registration data --- .../review-account-info.component.spec.ts | 5 +- .../review-account-info.component.ts | 62 +++++++++++++------ .../external-log-in.component.ts | 8 ++- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts index b7663674972..c7343c87bff 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts @@ -22,6 +22,8 @@ import { RouterMock } from '../../shared/mocks/router.mock'; import { EventEmitter } from '@angular/core'; import { CompareValuesPipe } from '../helpers/compare-values.pipe'; import { Registration } from '../../core/shared/registration.model'; +import { AuthService } from '../../core/auth/auth.service'; +import { AuthServiceMock } from '../../shared/mocks/auth.service.mock'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; @@ -79,6 +81,7 @@ describe('ReviewAccountInfoComponent', () => { }, { provide: TranslateService, useValue: translateServiceStub }, { provide: Router, useValue: router }, + { provide: AuthService, useValue: new AuthServiceMock() } ], imports: [ CommonModule, @@ -150,7 +153,7 @@ describe('ReviewAccountInfoComponent', () => { spyOn(ePersonDataServiceStub, 'mergeEPersonDataWithToken').and.returnValue( of({ hasSucceeded: true }) ); - component.mergeEPersonDataWithToken(); + component.mergeEPersonDataWithToken(registrationDataMock.user); tick(); expect(ePersonDataServiceStub.mergeEPersonDataWithToken).toHaveBeenCalledTimes(1); expect(router.navigate).toHaveBeenCalledWith(['/profile']); diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index bf5099c3767..53b56d79c53 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -7,7 +7,7 @@ import { } from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { Observable, Subscription, filter, from, switchMap, take } from 'rxjs'; +import { Observable, Subscription, filter, from, map, switchMap, take, tap } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { ConfirmationModalComponent } from '../../shared/confirmation-modal/confirmation-modal.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -16,6 +16,7 @@ import { TranslateService } from '@ngx-translate/core'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { Router } from '@angular/router'; import { Registration } from '../../core/shared/registration.model'; +import { AuthService } from '../../core/auth/auth.service'; export interface ReviewAccountInfoData { label: string; @@ -59,8 +60,9 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { private modalService: NgbModal, private notificationService: NotificationsService, private translateService: TranslateService, - private router: Router - ) {} + private router: Router, + private authService: AuthService + ) { } ngOnInit(): void { this.dataToCompare = this.prepareDataToCompare(); @@ -94,15 +96,37 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { modalRef.componentInstance.brandColor = 'primary'; modalRef.componentInstance.confirmIcon = 'fa fa-check'; - this.subs.push( - modalRef.componentInstance.response - .pipe(take(1)) - .subscribe((confirm: boolean) => { - if (confirm) { - this.mergeEPersonDataWithToken(); - } - }) - ); + if (!this.registrationData.user) { + this.subs.push( + this.isAuthenticated() + .pipe( + filter((isAuthenticated) => isAuthenticated), + switchMap(() => this.authService.getAuthenticatedUserFromStore()), + filter((user) => hasValue(user)), + map((user) => user.uuid), + switchMap((userId) => + modalRef.componentInstance.response.pipe( + tap((confirm: boolean) => { + if (confirm) { + this.mergeEPersonDataWithToken(userId); + } + }) + ) + ) + ) + .subscribe() + ); + } else if (this.registrationData.user) { + this.subs.push( + modalRef.componentInstance.response + .pipe(take(1)) + .subscribe((confirm: boolean) => { + if (confirm && this.registrationData.user) { + this.mergeEPersonDataWithToken(this.registrationData.user); + } + }) + ); + } } /** @@ -110,14 +134,14 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { * If any of the metadata is overridden, sent a merge request for each metadata to override. * If none of the metadata is overridden, sent a merge request with the registration token only. */ - mergeEPersonDataWithToken() { + mergeEPersonDataWithToken(userId: string) { let override$: Observable>; if (this.dataToCompare.some((d) => d.overrideValue)) { override$ = from(this.dataToCompare).pipe( filter((data: ReviewAccountInfoData) => data.overrideValue), switchMap((data: ReviewAccountInfoData) => { return this.ePersonService.mergeEPersonDataWithToken( - this.registrationData.user, + userId, this.registrationToken, data.identifier ); @@ -125,7 +149,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); } else { override$ = this.ePersonService.mergeEPersonDataWithToken( - this.registrationData.user, + userId, this.registrationToken ); } @@ -138,9 +162,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ) ); this.router.navigate(['/profile']); - } - - if (response.hasFailed) { + } else { this.notificationService.error( this.translateService.get( 'review-account-info.merge-data.notification.error' @@ -151,6 +173,10 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); } + private isAuthenticated(): Observable { + return this.authService.isAuthenticated(); + } + /** * Prepare the data to compare and display: * -> For each metadata from the registration token, get the current value from the eperson. diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts index 0a077405429..d167a0c5d08 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts @@ -55,7 +55,7 @@ export class ExternalLogInComponent implements OnInit { private translate: TranslateService, private modalService: NgbModal, private authService: AuthService, - ) {} + ) { } /** * Provide the registration data object to the objectInjector. @@ -118,11 +118,13 @@ export class ExternalLogInComponent implements OnInit { * @param content - The content to be displayed in the modal. */ openLoginModal(content: any) { + setTimeout(() => { + this.authService.setRedirectUrl(`/review-account/${this.token}`); + }, 100); this.modalRef = this.modalService.open(content); - this.authService.setRedirectUrl('/review-account'); this.modalRef.dismissed.subscribe(() => { - this.clearRedirectUrl(); + this.clearRedirectUrl(); }); } From 80fd96cba5785bd9be0cc630c7ea7df25c8b80a5 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 6 Oct 2023 14:41:34 +0200 Subject: [PATCH 168/195] [DSC-1283] Fixes user agreement acceptance for new users --- src/app/core/auth/auth.actions.ts | 48 ++++++++++++++ src/app/core/auth/auth.effects.spec.ts | 65 +++++++++++++++++++ src/app/core/auth/auth.effects.ts | 21 ++++++ src/app/core/auth/auth.reducer.ts | 20 ++++++ .../end-user-agreement.component.ts | 4 +- src/app/shared/testing/auth-service.stub.ts | 4 ++ 6 files changed, 160 insertions(+), 2 deletions(-) diff --git a/src/app/core/auth/auth.actions.ts b/src/app/core/auth/auth.actions.ts index b8a943ee106..4ffaada1951 100644 --- a/src/app/core/auth/auth.actions.ts +++ b/src/app/core/auth/auth.actions.ts @@ -37,6 +37,9 @@ export const AuthActionTypes = { RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS'), RETRIEVE_AUTHENTICATED_EPERSON_ERROR: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_ERROR'), REDIRECT_AFTER_LOGIN_SUCCESS: type('dspace/auth/REDIRECT_AFTER_LOGIN_SUCCESS'), + REFRESH_STATE_TOKEN_REDIRECT: type('dspace/auth/REFRESH_STATE_TOKEN_REDIRECT'), + REFRESH_STATE_TOKEN_REDIRECT_ERROR: type('dspace/auth/REFRESH_STATE_TOKEN_REDIRECT_ERROR'), + REFRESH_STATE_TOKEN_REDIRECT_SUCCESS: type('dspace/auth/REFRESH_STATE_TOKEN_REDIRECT_SUCCESS'), REFRESH_TOKEN_AND_REDIRECT: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT'), REFRESH_TOKEN_AND_REDIRECT_SUCCESS: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT_SUCCESS'), REFRESH_TOKEN_AND_REDIRECT_ERROR: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT_ERROR'), @@ -416,6 +419,51 @@ export class UnsetUserAsIdleAction implements Action { public type: string = AuthActionTypes.UNSET_USER_AS_IDLE; } + +/** + * Refresh user state, the token and execute a redirect. + * @class RefreshTokenAndRedirectAction + * @implements {Action} + */ +export class RefreshStateTokenRedirectAction implements Action { + public type: string = AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT; + payload: { + token: AuthTokenInfo, + redirectUrl: string + }; + + constructor(token: AuthTokenInfo, redirectUrl: string) { + this.payload = { token, redirectUrl }; + } +} + +/** + * Refresh user state, the token and execute a redirect. + * @class RefreshStateTokenRedirectSuccessAction + * @implements {Action} + */ +export class RefreshStateTokenRedirectSuccessAction implements Action { + public type: string = AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS; + payload: { + ePerson: EPerson, + token: AuthTokenInfo, + redirectUrl: string + }; + + constructor(ePerson: EPerson, token: AuthTokenInfo, redirectUrl: string) { + this.payload = { ePerson, token, redirectUrl }; + } +} + +/** + * Refresh user state, the token and execute a redirect. + * @class RefreshStateTokenRedirectErrorAction + * @implements {Action} + */ +export class RefreshStateTokenRedirectErrorAction implements Action { + public type: string = AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_ERROR; +} + /** * Refresh authentication token and redirect. * @class RefreshTokenAndRedirectAction diff --git a/src/app/core/auth/auth.effects.spec.ts b/src/app/core/auth/auth.effects.spec.ts index 7b0b8dd5d68..c32bf1193ab 100644 --- a/src/app/core/auth/auth.effects.spec.ts +++ b/src/app/core/auth/auth.effects.spec.ts @@ -20,6 +20,9 @@ import { CheckAuthenticationTokenCookieAction, LogOutErrorAction, LogOutSuccessAction, + RefreshStateTokenRedirectErrorAction, + RefreshStateTokenRedirectSuccessAction, + RefreshTokenAndRedirectAction, RefreshTokenAndRedirectErrorAction, RefreshTokenAndRedirectSuccessAction, RefreshTokenErrorAction, @@ -457,6 +460,68 @@ describe('AuthEffects', () => { }); }); + describe('refreshStateTokenRedirect$', () => { + + describe('when refresh state, token and redirect action', () => { + it('should return a REFRESH_STATE_TOKEN_AND_REDIRECT_SUCCESS action in response to a REFRESH_STATE_TOKEN_AND_REDIRECT action', (done) => { + spyOn((authEffects as any).authService, 'retrieveAuthenticatedUserById').and.returnValue(observableOf(EPersonMock)); + + actions = hot('--a-', { + a: { + type: AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT, + payload: { token, redirectUrl } + } + }); + + const expected = cold('--b-', { b: new RefreshStateTokenRedirectSuccessAction(EPersonMock, token, redirectUrl) }); + + expect(authEffects.refreshStateTokenRedirect$).toBeObservable(expected); + done(); + }); + }); + + describe('when refresh state token failed', () => { + it('should return a REFRESH_STATE_TOKEN_AND_REDIRECT_SUCCESS action in response to a REFRESH_STATE_TOKEN_AND_REDIRECT action', (done) => { + spyOn((authEffects as any).authService, 'retrieveAuthenticatedUserById').and.returnValue(observableThrow('')); + + actions = hot('--a-', { + a: { + type: AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT, + payload: { token, redirectUrl } + } + }); + + const expected = cold('--b-', { b: new RefreshStateTokenRedirectErrorAction() }); + + expect(authEffects.refreshStateTokenRedirect$).toBeObservable(expected); + done(); + }); + }); + + }); + + describe('refreshStateTokenRedirectSuccess$', () => { + + beforeEach(() => { + scheduler = getTestScheduler(); + }); + + it('should return a REFRESH_TOKEN_AND_REDIRECT action', (done) => { + + actions = hot('--a-', { + a: { + type: AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS, + payload: { ePerson: EPersonMock, token, redirectUrl } + } + }); + + const expected = cold('--b-', { b: new RefreshTokenAndRedirectAction(token, redirectUrl) }); + + expect(authEffects.refreshStateTokenRedirectSuccess$).toBeObservable(expected); + done(); + }); + }); + describe('refreshTokenAndRedirect$', () => { describe('when refresh token and redirect succeeded', () => { diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index 2ed01e1eb86..25380328eba 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -35,6 +35,9 @@ import { LogOutErrorAction, LogOutSuccessAction, RedirectAfterLoginSuccessAction, + RefreshStateTokenRedirectAction, + RefreshStateTokenRedirectErrorAction, + RefreshStateTokenRedirectSuccessAction, RefreshTokenAction, RefreshTokenAndRedirectAction, RefreshTokenAndRedirectErrorAction, @@ -270,6 +273,24 @@ export class AuthEffects { })) ); + public refreshStateTokenRedirect$: Observable = createEffect(() => this.actions$ + .pipe(ofType(AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT), + switchMap((action: RefreshStateTokenRedirectAction) => + this.authService.getAuthenticatedUserFromStore() + .pipe( + switchMap(user => this.authService.retrieveAuthenticatedUserById(user.id)), + map(user => new RefreshStateTokenRedirectSuccessAction(user, action.payload.token, action.payload.redirectUrl)), + catchError((error) => observableOf(new RefreshStateTokenRedirectErrorAction())) + ) + ) + ) + ); + + public refreshStateTokenRedirectSuccess$: Observable = createEffect(() => this.actions$ + .pipe(ofType(AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS), + map((action: RefreshStateTokenRedirectAction) => new RefreshTokenAndRedirectAction(action.payload.token, action.payload.redirectUrl))) + ); + public refreshTokenAndRedirectSuccess$: Observable = createEffect(() => this.actions$ .pipe(ofType(AuthActionTypes.REFRESH_TOKEN_AND_REDIRECT_SUCCESS), tap((action: RefreshTokenAndRedirectSuccessAction) => this.authService.replaceToken(action.payload.token)), diff --git a/src/app/core/auth/auth.reducer.ts b/src/app/core/auth/auth.reducer.ts index 0a02257fcd7..7deb207fc2e 100644 --- a/src/app/core/auth/auth.reducer.ts +++ b/src/app/core/auth/auth.reducer.ts @@ -8,6 +8,7 @@ import { LogOutErrorAction, RedirectWhenAuthenticationIsRequiredAction, RedirectWhenTokenExpiredAction, + RefreshStateTokenRedirectSuccessAction, RefreshTokenAndRedirectSuccessAction, RefreshTokenSuccessAction, RetrieveAuthenticatedEpersonSuccessAction, @@ -190,6 +191,25 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut user: undefined }); + case AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT: + return Object.assign({}, state, { + loading: true, + loaded: false, + }); + + case AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS: + return Object.assign({}, state, { + loading: false, + loaded: false, + user: (action as RefreshStateTokenRedirectSuccessAction).payload.ePerson, + }); + + case AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_ERROR: + return Object.assign({}, state, { + loading: false, + loaded: false, + }); + case AuthActionTypes.REFRESH_TOKEN: return Object.assign({}, state, { refreshing: true, diff --git a/src/app/info/end-user-agreement/end-user-agreement.component.ts b/src/app/info/end-user-agreement/end-user-agreement.component.ts index c925feb141e..f2e4c469ceb 100644 --- a/src/app/info/end-user-agreement/end-user-agreement.component.ts +++ b/src/app/info/end-user-agreement/end-user-agreement.component.ts @@ -1,4 +1,4 @@ -import { LogOutAction, RefreshTokenAndRedirectAction } from '../../core/auth/auth.actions'; +import { LogOutAction, RefreshStateTokenRedirectAction } from '../../core/auth/auth.actions'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { AuthService } from '../../core/auth/auth.service'; import { map, switchMap, take } from 'rxjs/operators'; @@ -92,7 +92,7 @@ export class EndUserAgreementComponent implements OnInit, OnDestroy { take(1) ).subscribe((redirectUrl) => { if (isNotEmpty(redirectUrl)) { - this.store.dispatch(new RefreshTokenAndRedirectAction(this.authService.getToken(), redirectUrl)); + this.store.dispatch(new RefreshStateTokenRedirectAction(this.authService.getToken(), redirectUrl)); } }); } diff --git a/src/app/shared/testing/auth-service.stub.ts b/src/app/shared/testing/auth-service.stub.ts index d859db0bdf1..a5254ddf76c 100644 --- a/src/app/shared/testing/auth-service.stub.ts +++ b/src/app/shared/testing/auth-service.stub.ts @@ -175,4 +175,8 @@ export class AuthServiceStub { getRetrieveAuthMethodsAction(authStatus: AuthStatus): RetrieveAuthMethodsAction { return; } + + public getAuthenticatedUserFromStore(): Observable { + return observableOf(EPersonMock); + } } From 31351cd24d4b6749174a55f8bf8d7639fb96defd Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 6 Oct 2023 17:04:30 +0200 Subject: [PATCH 169/195] [DSC-1283] Fixes failing agreement test --- .../end-user-agreement/end-user-agreement.component.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts b/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts index 7d6b07d73f1..bcaf045578e 100644 --- a/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts +++ b/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts @@ -9,7 +9,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { of as observableOf } from 'rxjs'; import { Store } from '@ngrx/store'; import { By } from '@angular/platform-browser'; -import { LogOutAction, RefreshTokenAndRedirectAction } from '../../core/auth/auth.actions'; +import { LogOutAction, RefreshStateTokenRedirectAction } from '../../core/auth/auth.actions'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { AuthTokenInfo } from '../../core/auth/models/auth-token-info.model'; @@ -113,7 +113,7 @@ describe('EndUserAgreementComponent', () => { }); it('should refresh the token and navigate the user to the redirect url', () => { - expect(store.dispatch).toHaveBeenCalledWith(new RefreshTokenAndRedirectAction(token, redirectUrl)); + expect(store.dispatch).toHaveBeenCalledWith(new RefreshStateTokenRedirectAction(token, redirectUrl)); }); }); From 19579e4cb8b7af95c1a7611bfa30bd78b2797324 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 6 Oct 2023 18:22:43 +0200 Subject: [PATCH 170/195] [CST-10703] redirectToExternalProvider to login --- src/app/core/auth/auth.service.ts | 25 ++++++++ .../auth/models/auth.registration-type.ts | 2 +- .../helpers/review-account.guard.ts | 3 +- .../review-account-info.component.ts | 57 ++++++++++++++++--- .../confirm-email/confirm-email.component.ts | 46 +++++++++------ .../services/external-login.service.ts | 43 ++++++++++---- .../log-in-external-provider.component.ts | 21 +------ 7 files changed, 139 insertions(+), 58 deletions(-) diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index ec98dbc06a2..4e089fc6347 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -50,6 +50,7 @@ import { PageInfo } from '../shared/page-info.model'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { MachineToken } from './models/machine-token.model'; import { NoContent } from '../shared/NoContent.model'; +import { URLCombiner } from '../url-combiner/url-combiner'; export const LOGIN_ROUTE = '/login'; export const LOGOUT_ROUTE = '/logout'; @@ -523,6 +524,30 @@ export class AuthService { }); } + /** + * Returns the external server redirect URL. + * @param redirectRoute - The redirect route. + * @param location - The location. + * @returns The external server redirect URL. + */ + getExternalServerRedirectUrl(redirectRoute: string, location: string): string { + const correctRedirectUrl = new URLCombiner(this._window.nativeWindow.origin, redirectRoute).toString(); + + let externalServerUrl = location; + const myRegexp = /\?redirectUrl=(.*)/g; + const match = myRegexp.exec(location); + const redirectUrlFromServer = (match && match[1]) ? match[1] : null; + + // Check whether the current page is different from the redirect url received from rest + if (isNotNull(redirectUrlFromServer) && redirectUrlFromServer !== correctRedirectUrl) { + // change the redirect url with the current page url + const newRedirectUrl = `?redirectUrl=${correctRedirectUrl}`; + externalServerUrl = location.replace(/\?redirectUrl=(.*)/g, newRedirectUrl); + } + + return externalServerUrl; + } + /** * Clear redirect url */ diff --git a/src/app/core/auth/models/auth.registration-type.ts b/src/app/core/auth/models/auth.registration-type.ts index aa9f9d88b67..b8aaa1fe40d 100644 --- a/src/app/core/auth/models/auth.registration-type.ts +++ b/src/app/core/auth/models/auth.registration-type.ts @@ -1,4 +1,4 @@ export enum AuthRegistrationType { Orcid = 'ORCID', - Validation = 'VALIDATION', + Validation = 'VALIDATION_', } diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.ts b/src/app/external-login-review-account-info/helpers/review-account.guard.ts index 03caf6d63ed..d908483c152 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.ts +++ b/src/app/external-login-review-account-info/helpers/review-account.guard.ts @@ -5,7 +5,6 @@ import { Router, RouterStateSnapshot, } from '@angular/router'; -import isEqual from 'lodash/isEqual'; import { Observable, catchError, mergeMap, of, tap } from 'rxjs'; import { AuthService } from '../../core/auth/auth.service'; import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; @@ -44,7 +43,7 @@ export class ReviewAccountGuard implements CanActivate { (data: RemoteData) => { if (data.hasSucceeded && hasValue(data.payload)) { // is the registration type validation (account valid) - if (hasValue(data.payload.registrationType) && isEqual(data.payload.registrationType, AuthRegistrationType.Validation)) { + if (hasValue(data.payload.registrationType) && data.payload.registrationType.includes(AuthRegistrationType.Validation)) { return of(true); } else { return this.authService.isAuthenticated(); diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index 53b56d79c53..38944f18f5f 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -7,7 +7,7 @@ import { } from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { Observable, Subscription, filter, from, map, switchMap, take, tap } from 'rxjs'; +import { Observable, Subscription, combineLatest, filter, from, map, switchMap, take, tap } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { ConfirmationModalComponent } from '../../shared/confirmation-modal/confirmation-modal.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -17,6 +17,9 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { Router } from '@angular/router'; import { Registration } from '../../core/shared/registration.model'; import { AuthService } from '../../core/auth/auth.service'; +import { ExternalLoginService } from '../../shared/external-log-in-complete/services/external-login.service'; +import { HardRedirectService } from '../../core/services/hard-redirect.service'; +import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; export interface ReviewAccountInfoData { label: string; @@ -61,7 +64,9 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { private notificationService: NotificationsService, private translateService: TranslateService, private router: Router, - private authService: AuthService + private authService: AuthService, + private externalLoginService: ExternalLoginService, + private hardRedirectService: HardRedirectService, ) { } ngOnInit(): void { @@ -108,7 +113,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { modalRef.componentInstance.response.pipe( tap((confirm: boolean) => { if (confirm) { - this.mergeEPersonDataWithToken(userId); + this.mergeEPersonDataWithToken(userId, this.registrationData.registrationType); } }) ) @@ -122,7 +127,8 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { .pipe(take(1)) .subscribe((confirm: boolean) => { if (confirm && this.registrationData.user) { - this.mergeEPersonDataWithToken(this.registrationData.user); + const registrationType = this.registrationData.registrationType.split(AuthRegistrationType.Validation)[1]; + this.mergeEPersonDataWithToken(this.registrationData.user, registrationType); } }) ); @@ -134,7 +140,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { * If any of the metadata is overridden, sent a merge request for each metadata to override. * If none of the metadata is overridden, sent a merge request with the registration token only. */ - mergeEPersonDataWithToken(userId: string) { + mergeEPersonDataWithToken(userId: string, registrationType: string) { let override$: Observable>; if (this.dataToCompare.some((d) => d.overrideValue)) { override$ = from(this.dataToCompare).pipe( @@ -153,8 +159,16 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { this.registrationToken ); } + if (this.registrationData.user && this.registrationData.registrationType.includes(AuthRegistrationType.Validation)) { + this.handleUnauthenticatedUser(override$, registrationType); + } else { + this.handleAuthenticatedUser(override$); + } + } + + handleAuthenticatedUser(override$: Observable>) { this.subs.push( - override$.subscribe((response) => { + override$.subscribe((response: RemoteData) => { if (response.hasSucceeded) { this.notificationService.success( this.translateService.get( @@ -162,7 +176,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ) ); this.router.navigate(['/profile']); - } else { + } else if (response.hasFailed) { this.notificationService.error( this.translateService.get( 'review-account-info.merge-data.notification.error' @@ -173,6 +187,35 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); } + handleUnauthenticatedUser(override$: Observable>, registrationType: string) { + this.subs.push( + combineLatest([ + override$, + this.externalLoginService.getExternalAuthLocation(registrationType), + this.authService.getRedirectUrl()]) + .subscribe(([response, location, redirectRoute]) => { + if (response.hasSucceeded) { + this.notificationService.success( + this.translateService.get( + 'review-account-info.merge-data.notification.success' + ) + ); + // set Redirect URL to User profile, so the user is redirected to the profile page after logging in + this.authService.setRedirectUrl('/profile'); + const externalServerUrl = this.authService.getExternalServerRedirectUrl(redirectRoute, location); + // redirect to external registration type authentication url + this.hardRedirectService.redirect(externalServerUrl); + } else if (response.hasFailed) { + this.notificationService.error( + this.translateService.get( + 'review-account-info.merge-data.notification.error' + ) + ); + } + }) + ); + } + private isAuthenticated(): Observable { return this.authService.isAuthenticated(); } diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index df57939021b..ad8205a2ea7 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -5,14 +5,14 @@ import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../../c import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { hasNoValue, hasValue } from '../../../../shared/empty.util'; import { EPerson } from '../../../../core/eperson/models/eperson.model'; -import { RemoteData } from '../../../../core/data/remote-data'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import isEqual from 'lodash/isEqual'; import { AuthService } from '../../../../core/auth/auth.service'; import { Router } from '@angular/router'; -import { Subscription } from 'rxjs'; +import { Subscription, combineLatest, take } from 'rxjs'; import { Registration } from '../../../../core/shared/registration.model'; +import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; @Component({ selector: 'ds-confirm-email', @@ -39,6 +39,8 @@ export class ConfirmEmailComponent implements OnDestroy { */ subs: Subscription[] = []; + externalLocation: string; + constructor( private formBuilder: FormBuilder, private externalLoginService: ExternalLoginService, @@ -46,7 +48,8 @@ export class ConfirmEmailComponent implements OnDestroy { private notificationService: NotificationsService, private translate: TranslateService, private authService: AuthService, - private router: Router + private router: Router, + private hardRedirectService: HardRedirectService, ) { this.emailForm = this.formBuilder.group({ email: ['', [Validators.required, Validators.email]] @@ -119,21 +122,28 @@ export class ConfirmEmailComponent implements OnDestroy { eperson.requireCertificate = false; eperson.selfRegistered = true; this.subs.push( - this.epersonDataService.createEPersonForToken(eperson, token).pipe( - getFirstCompletedRemoteData(), - ).subscribe((rd: RemoteData) => { - if (rd.hasFailed) { - this.notificationService.error( - this.translate.get('external-login-page.provide-email.create-account.notifications.error.header'), - this.translate.get('external-login-page.provide-email.create-account.notifications.error.content') - ); - } else if (rd.hasSucceeded) { - // redirect to login page with authMethod query param, so that the login page knows which authentication method to use - // set Redirect URL to User profile, so the user is redirected to the profile page after logging in - this.router.navigate(['/login'], { queryParams: { authMethod: registrationData.registrationType } }); - this.authService.setRedirectUrl('/profile'); - } - })); + combineLatest([ + this.epersonDataService.createEPersonForToken(eperson, token).pipe( + getFirstCompletedRemoteData(), + ), + this.externalLoginService.getExternalAuthLocation(this.registrationData.registrationType), + this.authService.getRedirectUrl().pipe(take(1)) + ]) + .subscribe(([rd, location, redirectRoute]) => { + if (rd.hasFailed) { + this.notificationService.error( + this.translate.get('external-login-page.provide-email.create-account.notifications.error.header'), + this.translate.get('external-login-page.provide-email.create-account.notifications.error.content') + ); + } else if (rd.hasSucceeded) { + // set Redirect URL to User profile, so the user is redirected to the profile page after logging in + this.authService.setRedirectUrl('/profile'); + const externalServerUrl = this.authService.getExternalServerRedirectUrl(redirectRoute, location); + // redirect to external registration type authentication url + this.hardRedirectService.redirect(externalServerUrl); + } + }) + ); } ngOnDestroy(): void { diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/shared/external-log-in-complete/services/external-login.service.ts index 3d3f97571c9..fbe64198b21 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.ts @@ -1,12 +1,16 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { Observable, map } from 'rxjs'; +import { Observable, filter, map, tap } from 'rxjs'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { RemoteData } from '../../../core/data/remote-data'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { NotificationsService } from '../../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { NoContent } from '../../../core/shared/NoContent.model'; +import { AuthMethod } from 'src/app/core/auth/models/auth.method'; +import { getAuthenticationMethods } from 'src/app/core/auth/selectors'; +import { Store, select } from '@ngrx/store'; +import { CoreState } from 'src/app/core/core-state.model'; @Injectable({ providedIn: 'root' @@ -17,19 +21,20 @@ export class ExternalLoginService { private epersonRegistrationService: EpersonRegistrationService, private router: Router, private notificationService: NotificationsService, - private translate: TranslateService + private translate: TranslateService, + private store: Store, ) { } - /** - * Update the registration data. - * Send a patch request to the server to update the registration data. - * @param values the values to update or add - * @param field the filed to be updated - * @param registrationId the registration id - * @param token the registration token - * @param operation operation to be performed - */ - patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operation: 'add' | 'replace'): Observable>{ + /** + * Update the registration data. + * Send a patch request to the server to update the registration data. + * @param values the values to update or add + * @param field the filed to be updated + * @param registrationId the registration id + * @param token the registration token + * @param operation operation to be performed + */ + patchUpdateRegistration(values: string[], field: string, registrationId: string, token: string, operation: 'add' | 'replace'): Observable> { const updatedValues = values.map((value) => value); return this.epersonRegistrationService.patchUpdateRegistration(updatedValues, field, registrationId, token, operation).pipe( getFirstCompletedRemoteData(), @@ -44,4 +49,18 @@ export class ExternalLoginService { }) ); } + + /** + * Returns an Observable that emits the external authentication location for the given registration type. + * @param registrationType The type of registration to get the external authentication location for. + * @returns An Observable that emits the external authentication location as a string. + */ + getExternalAuthLocation(registrationType: string): Observable { + return this.store.pipe( + select(getAuthenticationMethods), + filter((methods: AuthMethod[]) => methods.length > 0), + tap((methods: AuthMethod[]) => console.log(methods.find(m => m.authMethodType === registrationType.toLocaleLowerCase()), methods.find(m => m.authMethodType === registrationType.toLocaleLowerCase()).location)), + map((methods: AuthMethod[]) => methods.find(m => m.authMethodType === registrationType.toLocaleLowerCase()).location), + ); + } } diff --git a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts index f1829684575..32b551e05cd 100644 --- a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts +++ b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts @@ -8,10 +8,9 @@ import { AuthMethod } from '../../../../core/auth/models/auth.method'; import { isAuthenticated, isAuthenticationLoading } from '../../../../core/auth/selectors'; import { NativeWindowRef, NativeWindowService } from '../../../../core/services/window.service'; -import { isEmpty, isNotNull } from '../../../empty.util'; +import { isEmpty } from '../../../empty.util'; import { AuthService } from '../../../../core/auth/auth.service'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; -import { URLCombiner } from '../../../../core/url-combiner/url-combiner'; import { CoreState } from '../../../../core/core-state.model'; import { renderAuthMethodFor } from '../log-in.methods-decorator'; import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; @@ -92,24 +91,10 @@ export class LogInExternalProviderComponent implements OnInit { } else if (isEmpty(redirectRoute)) { redirectRoute = '/'; } - const correctRedirectUrl = new URLCombiner(this._window.nativeWindow.origin, redirectRoute).toString(); - - let externalServerUrl = this.location; - const myRegexp = /\?redirectUrl=(.*)/g; - const match = myRegexp.exec(this.location); - const redirectUrlFromServer = (match && match[1]) ? match[1] : null; - - // Check whether the current page is different from the redirect url received from rest - if (isNotNull(redirectUrlFromServer) && redirectUrlFromServer !== correctRedirectUrl) { - // change the redirect url with the current page url - const newRedirectUrl = `?redirectUrl=${correctRedirectUrl}`; - externalServerUrl = this.location.replace(/\?redirectUrl=(.*)/g, newRedirectUrl); - } - - // redirect to shibboleth authentication url + const externalServerUrl = this.authService.getExternalServerRedirectUrl(redirectRoute, this.location); + // redirect to shibboleth/orcid/(external) authentication url this.hardRedirectService.redirect(externalServerUrl); }); - } getButtonLabel() { From 689bd0aa451756aeffd3d0dadd385f591718caa6 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 9 Oct 2023 10:32:10 +0200 Subject: [PATCH 171/195] [CST-10703] unit test fixes --- .../review-account-info.component.spec.ts | 16 ++- .../confirm-email.component.spec.ts | 107 ++++++++++-------- .../confirm-email/confirm-email.component.ts | 2 - .../services/external-login.service.spec.ts | 2 + .../services/external-login.service.ts | 1 - ...log-in-external-provider.component.spec.ts | 3 +- src/app/shared/testing/auth-service.stub.ts | 4 + 7 files changed, 82 insertions(+), 53 deletions(-) diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts index c7343c87bff..7c52f4dddbb 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts @@ -24,6 +24,8 @@ import { CompareValuesPipe } from '../helpers/compare-values.pipe'; import { Registration } from '../../core/shared/registration.model'; import { AuthService } from '../../core/auth/auth.service'; import { AuthServiceMock } from '../../shared/mocks/auth.service.mock'; +import { ExternalLoginService } from '../../shared/external-log-in-complete/services/external-login.service'; +import { HardRedirectService } from '../../core/services/hard-redirect.service'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; @@ -31,6 +33,8 @@ describe('ReviewAccountInfoComponent', () => { let ePersonDataServiceStub: any; let router: any; let notificationsService: any; + let externalLoginServiceStub: any; + let hardRedirectService: HardRedirectService; const translateServiceStub = { get: () => of('test-message'), @@ -70,6 +74,12 @@ describe('ReviewAccountInfoComponent', () => { }; router = new RouterMock(); notificationsService = new NotificationsServiceStub(); + externalLoginServiceStub = { + getExternalAuthLocation: () => 'location', + }; + hardRedirectService = jasmine.createSpyObj('HardRedirectService', { + redirect: {} + }); await TestBed.configureTestingModule({ declarations: [ReviewAccountInfoComponent, CompareValuesPipe], providers: [ @@ -81,7 +91,9 @@ describe('ReviewAccountInfoComponent', () => { }, { provide: TranslateService, useValue: translateServiceStub }, { provide: Router, useValue: router }, - { provide: AuthService, useValue: new AuthServiceMock() } + { provide: AuthService, useValue: new AuthServiceMock() }, + { provide: ExternalLoginService, useValue: externalLoginServiceStub }, + { provide: HardRedirectService, useValue: hardRedirectService }, ], imports: [ CommonModule, @@ -153,7 +165,7 @@ describe('ReviewAccountInfoComponent', () => { spyOn(ePersonDataServiceStub, 'mergeEPersonDataWithToken').and.returnValue( of({ hasSucceeded: true }) ); - component.mergeEPersonDataWithToken(registrationDataMock.user); + component.mergeEPersonDataWithToken(registrationDataMock.user, registrationDataMock.registrationType); tick(); expect(ePersonDataServiceStub.mergeEPersonDataWithToken).toHaveBeenCalledTimes(1); expect(router.navigate).toHaveBeenCalledWith(['/profile']); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts index 2247d8de904..651608b9300 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -3,7 +3,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfirmEmailComponent } from './confirm-email.component'; import { FormBuilder } from '@angular/forms'; import { CommonModule } from '@angular/common'; -import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { + TranslateLoader, + TranslateModule, + TranslateService, +} from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { ExternalLoginService } from '../../services/external-login.service'; @@ -11,12 +15,13 @@ import { AuthService } from '../../../../core/auth/auth.service'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { + createSuccessfulRemoteDataObject$, +} from '../../../../shared/remote-data.utils'; import { EPerson } from '../../../../core/eperson/models/eperson.model'; -import { Router } from '@angular/router'; -import { RouterMock } from '../../../../shared/mocks/router.mock'; import { of } from 'rxjs'; import { Registration } from '../../../../core/shared/registration.model'; +import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; describe('ConfirmEmailComponent', () => { let component: ConfirmEmailComponent; @@ -25,17 +30,19 @@ describe('ConfirmEmailComponent', () => { let epersonDataServiceSpy: jasmine.SpyObj; let notificationServiceSpy: jasmine.SpyObj; let authServiceSpy: jasmine.SpyObj; - let router; + let hardRedirectService: HardRedirectService; + const translateServiceStub = { get: () => of(''), onLangChange: new EventEmitter(), onTranslationChange: new EventEmitter(), - onDefaultLangChange: new EventEmitter() + onDefaultLangChange: new EventEmitter(), }; beforeEach(async () => { externalLoginServiceSpy = jasmine.createSpyObj('ExternalLoginService', [ 'patchUpdateRegistration', + 'getExternalAuthLocation', ]); epersonDataServiceSpy = jasmine.createSpyObj('EPersonDataService', [ 'createEPersonForToken', @@ -43,8 +50,10 @@ describe('ConfirmEmailComponent', () => { notificationServiceSpy = jasmine.createSpyObj('NotificationsService', [ 'error', ]); - authServiceSpy = jasmine.createSpyObj('AuthService', ['setRedirectUrl']); - router = new RouterMock(); + authServiceSpy = jasmine.createSpyObj('AuthService', ['getRedirectUrl', 'setRedirectUrl', 'getExternalServerRedirectUrl']); + hardRedirectService = jasmine.createSpyObj('HardRedirectService', { + redirect: {}, + }); await TestBed.configureTestingModule({ declarations: [ConfirmEmailComponent], providers: [ @@ -53,8 +62,8 @@ describe('ConfirmEmailComponent', () => { { provide: EPersonDataService, useValue: epersonDataServiceSpy }, { provide: NotificationsService, useValue: notificationServiceSpy }, { provide: AuthService, useValue: authServiceSpy }, - { provide: Router, useValue: router }, - { provide: TranslateService, useValue: translateServiceStub } + { provide: TranslateService, useValue: translateServiceStub }, + { provide: HardRedirectService, useValue: hardRedirectService }, ], imports: [ CommonModule, @@ -92,10 +101,9 @@ describe('ConfirmEmailComponent', () => { component.emailForm.setValue({ email: 'test@example.com' }); spyOn(component as any, 'postCreateAccountFromToken'); component.submitForm(); - expect((component as any).postCreateAccountFromToken).toHaveBeenCalledWith( - 'test-token', - component.registrationData - ); + expect( + (component as any).postCreateAccountFromToken + ).toHaveBeenCalledWith('test-token', component.registrationData); }); it('should call patchUpdateRegistration if email is not confirmed', () => { @@ -112,49 +120,56 @@ describe('ConfirmEmailComponent', () => { spyOn(component as any, 'postCreateAccountFromToken'); spyOn(component as any, 'patchUpdateRegistration'); component.submitForm(); - expect((component as any).postCreateAccountFromToken).not.toHaveBeenCalled(); + expect( + (component as any).postCreateAccountFromToken + ).not.toHaveBeenCalled(); expect((component as any).patchUpdateRegistration).not.toHaveBeenCalled(); }); }); - describe('postCreateAccountFromToken', () => { - it('should call epersonDataService.createEPersonForToken with correct arguments', () => { - epersonDataServiceSpy.createEPersonForToken.and.returnValue(createSuccessfulRemoteDataObject$(new EPerson())); - (component as any).postCreateAccountFromToken( - 'test-token', - component.registrationData - ); - expect(epersonDataServiceSpy.createEPersonForToken).toHaveBeenCalledWith( - jasmine.any(Object), - 'test-token' - ); - }); + // describe('postCreateAccountFromToken', () => { + // beforeEach(() => { + // authServiceSpy.getRedirectUrl.and.returnValue(of('test-redirect')); + // authServiceSpy.getExternalServerRedirectUrl.and.returnValue('test-external-url'); + // }); + // it('should call epersonDataService.createEPersonForToken with correct arguments', () => { + // epersonDataServiceSpy.createEPersonForToken.and.returnValue( + // createSuccessfulRemoteDataObject$(new EPerson()) + // ); + // (component as any).postCreateAccountFromToken( + // 'test-token', + // component.registrationData + // ); + // expect(epersonDataServiceSpy.createEPersonForToken).toHaveBeenCalledWith( + // jasmine.any(Object), + // 'test-token' + // ); + // }); + // }); - it('should show error notification if user creation fails', () => { - epersonDataServiceSpy.createEPersonForToken.and.returnValue( - createFailedRemoteDataObject$() - ); - (component as any).postCreateAccountFromToken( - 'test-token', - component.registrationData - ); + describe('postCreateAccountFromToken', () => { + it('should call NotificationsService.error if the registration data does not have a netId', () => { + component.registrationData.netId = undefined; + (component as any).postCreateAccountFromToken('test-token', component.registrationData); expect(notificationServiceSpy.error).toHaveBeenCalled(); }); - it('should redirect to login page if user creation succeeds', () => { - epersonDataServiceSpy.createEPersonForToken.and.returnValue( - createSuccessfulRemoteDataObject$(new EPerson()) - ); - (component as any).postCreateAccountFromToken( - 'test-token', - component.registrationData - ); - expect((component as any).router.navigate).toHaveBeenCalledWith(['/login'], { - queryParams: { authMethod: 'orcid' }, - }); + it('should call EPersonDataService.createEPersonForToken and ExternalLoginService.getExternalAuthLocation if the registration data has a netId', () => { + externalLoginServiceSpy.getExternalAuthLocation.and.returnValue(of('test-location')); + authServiceSpy.getRedirectUrl.and.returnValue(of('/test-redirect')); + authServiceSpy.getExternalServerRedirectUrl.and.returnValue('test-external-url'); + epersonDataServiceSpy.createEPersonForToken.and.returnValue(createSuccessfulRemoteDataObject$(new EPerson())); + (component as any).postCreateAccountFromToken('test-token', component.registrationData); + expect(epersonDataServiceSpy.createEPersonForToken).toHaveBeenCalled(); + expect(externalLoginServiceSpy.getExternalAuthLocation).toHaveBeenCalledWith(AuthMethodType.Orcid); + expect(authServiceSpy.getRedirectUrl).toHaveBeenCalled(); + expect(authServiceSpy.setRedirectUrl).toHaveBeenCalledWith('/profile'); + expect(authServiceSpy.getExternalServerRedirectUrl).toHaveBeenCalledWith('/test-redirect', 'test-location'); + expect(hardRedirectService.redirect).toHaveBeenCalledWith('test-external-url'); }); }); + afterEach(() => { fixture.destroy(); }); diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index ad8205a2ea7..80841a8f8de 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -9,7 +9,6 @@ import { NotificationsService } from '../../../../shared/notifications/notificat import { TranslateService } from '@ngx-translate/core'; import isEqual from 'lodash/isEqual'; import { AuthService } from '../../../../core/auth/auth.service'; -import { Router } from '@angular/router'; import { Subscription, combineLatest, take } from 'rxjs'; import { Registration } from '../../../../core/shared/registration.model'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; @@ -48,7 +47,6 @@ export class ConfirmEmailComponent implements OnDestroy { private notificationService: NotificationsService, private translate: TranslateService, private authService: AuthService, - private router: Router, private hardRedirectService: HardRedirectService, ) { this.emailForm = this.formBuilder.group({ diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts b/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts index 541e71e6a5a..ce0dcb4e269 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts @@ -12,6 +12,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router } from '@angular/router'; +import { Store } from '@ngrx/store'; describe('ExternalLoginService', () => { let service: ExternalLoginService; @@ -41,6 +42,7 @@ describe('ExternalLoginService', () => { { provide: Router, useValue: router }, { provide: NotificationsService, useValue: notificationService }, { provide: TranslateService, useValue: translate }, + { provide: Store, useValue: {} }, ], schemas: [NO_ERRORS_SCHEMA] }); diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/shared/external-log-in-complete/services/external-login.service.ts index fbe64198b21..03c671b570a 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.ts @@ -59,7 +59,6 @@ export class ExternalLoginService { return this.store.pipe( select(getAuthenticationMethods), filter((methods: AuthMethod[]) => methods.length > 0), - tap((methods: AuthMethod[]) => console.log(methods.find(m => m.authMethodType === registrationType.toLocaleLowerCase()), methods.find(m => m.authMethodType === registrationType.toLocaleLowerCase()).location)), map((methods: AuthMethod[]) => methods.find(m => m.authMethodType === registrationType.toLocaleLowerCase()).location), ); } diff --git a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts index de4f62eb9eb..e75d80c5c39 100644 --- a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts +++ b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts @@ -110,8 +110,7 @@ describe('LogInExternalProviderComponent', () => { component.redirectToExternalProvider(); - expect(setHrefSpy).toHaveBeenCalledWith(currentUrl); - + expect(hardRedirectService.redirect).toHaveBeenCalled(); }); it('should not set a new redirectUrl', () => { diff --git a/src/app/shared/testing/auth-service.stub.ts b/src/app/shared/testing/auth-service.stub.ts index d859db0bdf1..29953208f86 100644 --- a/src/app/shared/testing/auth-service.stub.ts +++ b/src/app/shared/testing/auth-service.stub.ts @@ -175,4 +175,8 @@ export class AuthServiceStub { getRetrieveAuthMethodsAction(authStatus: AuthStatus): RetrieveAuthMethodsAction { return; } + + public getExternalServerRedirectUrl(redirectRoute: string, location: string) { + return; + } } From a46500b8bcca3b2e8b95c6e113c828d422da7d98 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 9 Oct 2023 10:33:10 +0200 Subject: [PATCH 172/195] [CST-10703] remove comments --- .../confirm-email.component.spec.ts | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts index 651608b9300..e250fc41e03 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -127,26 +127,6 @@ describe('ConfirmEmailComponent', () => { }); }); - // describe('postCreateAccountFromToken', () => { - // beforeEach(() => { - // authServiceSpy.getRedirectUrl.and.returnValue(of('test-redirect')); - // authServiceSpy.getExternalServerRedirectUrl.and.returnValue('test-external-url'); - // }); - // it('should call epersonDataService.createEPersonForToken with correct arguments', () => { - // epersonDataServiceSpy.createEPersonForToken.and.returnValue( - // createSuccessfulRemoteDataObject$(new EPerson()) - // ); - // (component as any).postCreateAccountFromToken( - // 'test-token', - // component.registrationData - // ); - // expect(epersonDataServiceSpy.createEPersonForToken).toHaveBeenCalledWith( - // jasmine.any(Object), - // 'test-token' - // ); - // }); - // }); - describe('postCreateAccountFromToken', () => { it('should call NotificationsService.error if the registration data does not have a netId', () => { component.registrationData.netId = undefined; @@ -169,7 +149,6 @@ describe('ConfirmEmailComponent', () => { }); }); - afterEach(() => { fixture.destroy(); }); From f7e6ab6b0f267c6423d3488a5b6f36c4c783dd76 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 9 Oct 2023 11:49:27 +0200 Subject: [PATCH 173/195] [CST-10703] added missing docs to methods --- .../review-account-info.component.spec.ts | 17 ++++++++++++--- .../review-account-info.component.ts | 21 ++++++++++++++++++- src/app/shared/mocks/auth.service.mock.ts | 8 +++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts index 7c52f4dddbb..63545370ab7 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts @@ -29,12 +29,14 @@ import { HardRedirectService } from '../../core/services/hard-redirect.service'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; + let componentAsAny: any; let fixture: ComponentFixture; let ePersonDataServiceStub: any; let router: any; let notificationsService: any; let externalLoginServiceStub: any; let hardRedirectService: HardRedirectService; + let authService: any; const translateServiceStub = { get: () => of('test-message'), @@ -75,11 +77,12 @@ describe('ReviewAccountInfoComponent', () => { router = new RouterMock(); notificationsService = new NotificationsServiceStub(); externalLoginServiceStub = { - getExternalAuthLocation: () => 'location', + getExternalAuthLocation: () => of('https://orcid.org/oauth/authorize'), }; hardRedirectService = jasmine.createSpyObj('HardRedirectService', { - redirect: {} + redirect: (url: string) => null, }); + authService = new AuthServiceMock(); await TestBed.configureTestingModule({ declarations: [ReviewAccountInfoComponent, CompareValuesPipe], providers: [ @@ -91,7 +94,7 @@ describe('ReviewAccountInfoComponent', () => { }, { provide: TranslateService, useValue: translateServiceStub }, { provide: Router, useValue: router }, - { provide: AuthService, useValue: new AuthServiceMock() }, + { provide: AuthService, useValue: authService }, { provide: ExternalLoginService, useValue: externalLoginServiceStub }, { provide: HardRedirectService, useValue: hardRedirectService }, ], @@ -110,6 +113,7 @@ describe('ReviewAccountInfoComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ReviewAccountInfoComponent); component = fixture.componentInstance; + componentAsAny = component; component.registrationData = Object.assign( new Registration(), registrationDataMock @@ -213,6 +217,13 @@ describe('ReviewAccountInfoComponent', () => { expect(subscription2.unsubscribe).toHaveBeenCalled(); }); + it('should handle authenticated user', () => { + const override$ = createSuccessfulRemoteDataObject$(new EPerson()); + component.handleAuthenticatedUser(override$); + expect(componentAsAny.notificationService.success).toHaveBeenCalled(); + expect(router.navigate).toHaveBeenCalledWith(['/profile']); + }); + afterEach(() => { fixture.destroy(); }); diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index 38944f18f5f..9759e77c339 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -86,7 +86,10 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { /** * Open a confirmation modal to confirm the override of the data - * If confirmed, merge the data from the registration token with the data from the eperson + * If confirmed, merge the data from the registration token with the data from the eperson. + * There are 2 cases: + * -> If the user is authenticated, merge the data and redirect to profile page. + * -> If the user is not authenticated, combine the override$, external auth location and redirect URL observables. */ public onSave() { const modalRef = this.modalService.open(ConfirmationModalComponent); @@ -166,6 +169,11 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { } } + /** + * Handles the authenticated user by subscribing to the override$ observable and displaying a success or error notification based on the response. + * If the response has succeeded, the user is redirected to the profile page. + * @param override$ - The observable that emits the response containing the RemoteData object. + */ handleAuthenticatedUser(override$: Observable>) { this.subs.push( override$.subscribe((response: RemoteData) => { @@ -187,6 +195,13 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); } + /** + * Handles unauthenticated user by combining the override$, external auth location and redirect URL observables. + * If the response has succeeded, sets the redirect URL to user profile and redirects to external registration type authentication URL. + * If the response has failed, shows an error notification. + * @param override$ - The override$ observable. + * @param registrationType - The registration type. + */ handleUnauthenticatedUser(override$: Observable>, registrationType: string) { this.subs.push( combineLatest([ @@ -216,6 +231,10 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); } + /** + * Checks if the user is authenticated. + * @returns An observable that emits a boolean value indicating whether the user is authenticated or not. + */ private isAuthenticated(): Observable { return this.authService.isAuthenticated(); } diff --git a/src/app/shared/mocks/auth.service.mock.ts b/src/app/shared/mocks/auth.service.mock.ts index 992a8ad034c..4345b79bcd4 100644 --- a/src/app/shared/mocks/auth.service.mock.ts +++ b/src/app/shared/mocks/auth.service.mock.ts @@ -30,4 +30,12 @@ export class AuthServiceMock { public getImpersonateID(): string { return null; } + + public getRedirectUrl(): Observable { + return; + } + + public getExternalServerRedirectUrl(): string { + return; + } } From dc5623bcd32395a9ef6f94d37ba84ef84d22067a Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 9 Oct 2023 12:18:48 +0200 Subject: [PATCH 174/195] [CST-10703] refactor --- src/app/login-page/login-page.component.html | 1 - src/app/login-page/login-page.component.ts | 4 ---- src/app/shared/log-in/log-in.component.ts | 12 +----------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/app/login-page/login-page.component.html b/src/app/login-page/login-page.component.html index 067da65739a..2a95e0ce1c5 100644 --- a/src/app/login-page/login-page.component.html +++ b/src/app/login-page/login-page.component.html @@ -4,7 +4,6 @@

{{"login.form.header" | translate}}

diff --git a/src/app/login-page/login-page.component.ts b/src/app/login-page/login-page.component.ts index 62a72ab7907..d9ecf3e8e6b 100644 --- a/src/app/login-page/login-page.component.ts +++ b/src/app/login-page/login-page.component.ts @@ -15,7 +15,6 @@ import { import { hasValue, isNotEmpty } from '../shared/empty.util'; import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model'; import { isAuthenticated } from '../core/auth/selectors'; -import { AuthMethodType } from '../core/auth/models/auth.method-type'; /** * This component represents the login page @@ -34,8 +33,6 @@ export class LoginPageComponent implements OnDestroy, OnInit { */ sub: Subscription; - externalLoginMethod: AuthMethodType; - /** * Initialize instance variables * @@ -51,7 +48,6 @@ export class LoginPageComponent implements OnDestroy, OnInit { ngOnInit() { const queryParamsObs = this.route.queryParams; const authenticated = this.store.select(isAuthenticated); - this.externalLoginMethod = this.route.snapshot.queryParams.authMethod; this.sub = observableCombineLatest(queryParamsObs, authenticated).pipe( filter(([params, auth]) => isNotEmpty(params.token) || isNotEmpty(params.expired)), take(1) diff --git a/src/app/shared/log-in/log-in.component.ts b/src/app/shared/log-in/log-in.component.ts index 30dc535d313..eff53f3b6c4 100644 --- a/src/app/shared/log-in/log-in.component.ts +++ b/src/app/shared/log-in/log-in.component.ts @@ -45,12 +45,6 @@ export class LogInComponent implements OnInit, OnDestroy { */ @Input() showRegisterLink = true; - /** - * The external login method to force - * the user to use to login while completing the external login process - */ - @Input() externalLoginMethod: AuthMethodType; - /** * The list of authentication methods available * @type {AuthMethod[]} @@ -93,11 +87,7 @@ export class LogInComponent implements OnInit, OnDestroy { this.authMethods = uniqBy(methods.filter(a => a.authMethodType !== AuthMethodType.Ip), 'authMethodType'); // exclude the given auth method in case there is one if (hasValue(this.excludedAuthMethod)) { - this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType !== this.excludedAuthMethod); - } - // if there is an external login method the user should follow, filter the auth methods to only show that one - if (hasValue(this.externalLoginMethod)) { - this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType === this.externalLoginMethod.toLocaleLowerCase()); + this.authMethods = this.authMethods.filter((authMethod: AuthMethod) => authMethod.authMethodType !== this.excludedAuthMethod.toLocaleLowerCase()); } }); From c8a7e01974725a0f352cfa82e29d9ec26b33f99b Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Mon, 9 Oct 2023 13:47:08 +0300 Subject: [PATCH 175/195] [DSC-1248] fix basic search configuration parameter to be set by searchSection.discoveryConfiguration --- .../search-section/search-section.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/explore/section-component/search-section/search-section.component.html b/src/app/shared/explore/section-component/search-section/search-section.component.html index 57cf6e66447..1f1e684b489 100644 --- a/src/app/shared/explore/section-component/search-section/search-section.component.html +++ b/src/app/shared/explore/section-component/search-section/search-section.component.html @@ -29,7 +29,7 @@

{{ 'explore.search-section.' + sectionId
>

From 5e67345118db92b6e94247e0e452e87b27a87df3 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Oct 2023 12:48:45 +0200 Subject: [PATCH 176/195] [DSC-1289] Removed unused labels --- src/assets/i18n/en.json5 | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index ecd8a7dd3d9..a8421efe6bb 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -6381,22 +6381,8 @@ "access.condition.value.embargo": "Embargo", - "access.condition.value.vlan48": "ITG Subnet", - - "access.condition.value.vlan-campus": "IAS Campus Network", - - "access.condition.value.ldap": "IAS Users", - - "access.condition.value.staff": "Staff Only", - - "access.condition.value.faculty": "Faculty Only", - - "access.condition.value.member": "Members Only", - "access.condition.value.administrator": "Administrator Only", - "access.condition.value.embargoed": "Embargoed", - "access.condition.value.lease": "Lease", "submission.sections.license.granted-label": "I confirm the license above", From 9edb8e10b39712d0b885c0440ee2b6cffa8a05b7 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 9 Oct 2023 12:56:44 +0200 Subject: [PATCH 177/195] [CST-10703] minor fixes --- .../review-account-info/review-account-info.component.ts | 5 +++++ .../confirm-email/confirm-email.component.ts | 1 + 2 files changed, 6 insertions(+) diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index 9759e77c339..cca523ce4c2 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -251,6 +251,11 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { const dataToCompare: ReviewAccountInfoData[] = []; Object.entries(this.registrationData.registrationMetadata).forEach( ([key, value]) => { + // eperson.orcid is not always present in the registration metadata, + // so display netId instead and skip it in the metadata in order not to have duplicate data. + if (value[0].value === this.registrationData.netId) { + return; + } dataToCompare.push({ label: key.split('.')?.[1] ?? key.split('.')?.[0], currentValue: value[0]?.overrides ?? '', diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 80841a8f8de..73bc5adfa2c 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -108,6 +108,7 @@ export class ConfirmEmailComponent implements OnDestroy { const metadataValues = {}; for (const [key, value] of Object.entries(registrationData.registrationMetadata)) { + // exclude the email metadata key, since the ePerson object does not have an email metadata field if (hasValue(value[0]?.value) && key !== 'email') { metadataValues[key] = value; } From 51daf53069b2abd7d5521fa5d55c3e81c4a57a82 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Oct 2023 18:23:02 +0200 Subject: [PATCH 178/195] [DSC-1283] Rename actions --- src/app/core/auth/auth.actions.ts | 24 +++++++++---------- src/app/core/auth/auth.effects.spec.ts | 14 +++++------ src/app/core/auth/auth.effects.ts | 18 +++++++------- src/app/core/auth/auth.reducer.ts | 10 ++++---- .../end-user-agreement.component.spec.ts | 4 ++-- .../end-user-agreement.component.ts | 4 ++-- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/app/core/auth/auth.actions.ts b/src/app/core/auth/auth.actions.ts index 4ffaada1951..2e971c47c8b 100644 --- a/src/app/core/auth/auth.actions.ts +++ b/src/app/core/auth/auth.actions.ts @@ -37,9 +37,9 @@ export const AuthActionTypes = { RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS'), RETRIEVE_AUTHENTICATED_EPERSON_ERROR: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_ERROR'), REDIRECT_AFTER_LOGIN_SUCCESS: type('dspace/auth/REDIRECT_AFTER_LOGIN_SUCCESS'), - REFRESH_STATE_TOKEN_REDIRECT: type('dspace/auth/REFRESH_STATE_TOKEN_REDIRECT'), - REFRESH_STATE_TOKEN_REDIRECT_ERROR: type('dspace/auth/REFRESH_STATE_TOKEN_REDIRECT_ERROR'), - REFRESH_STATE_TOKEN_REDIRECT_SUCCESS: type('dspace/auth/REFRESH_STATE_TOKEN_REDIRECT_SUCCESS'), + REFRESH_EPERSON_AND_TOKEN_REDIRECT: type('dspace/auth/REFRESH_EPERSON_AND_TOKEN_REDIRECT'), + REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR: type('dspace/auth/REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR'), + REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS: type('dspace/auth/REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS'), REFRESH_TOKEN_AND_REDIRECT: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT'), REFRESH_TOKEN_AND_REDIRECT_SUCCESS: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT_SUCCESS'), REFRESH_TOKEN_AND_REDIRECT_ERROR: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT_ERROR'), @@ -422,11 +422,11 @@ export class UnsetUserAsIdleAction implements Action { /** * Refresh user state, the token and execute a redirect. - * @class RefreshTokenAndRedirectAction + * @class RefreshEpersonAndTokenRedirectAction * @implements {Action} */ -export class RefreshStateTokenRedirectAction implements Action { - public type: string = AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT; +export class RefreshEpersonAndTokenRedirectAction implements Action { + public type: string = AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT; payload: { token: AuthTokenInfo, redirectUrl: string @@ -439,11 +439,11 @@ export class RefreshStateTokenRedirectAction implements Action { /** * Refresh user state, the token and execute a redirect. - * @class RefreshStateTokenRedirectSuccessAction + * @class RefreshEpersonAndTokenRedirectSuccessAction * @implements {Action} */ -export class RefreshStateTokenRedirectSuccessAction implements Action { - public type: string = AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS; +export class RefreshEpersonAndTokenRedirectSuccessAction implements Action { + public type: string = AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS; payload: { ePerson: EPerson, token: AuthTokenInfo, @@ -457,11 +457,11 @@ export class RefreshStateTokenRedirectSuccessAction implements Action { /** * Refresh user state, the token and execute a redirect. - * @class RefreshStateTokenRedirectErrorAction + * @class RefreshEpersonAndTokenRedirectErrorAction * @implements {Action} */ -export class RefreshStateTokenRedirectErrorAction implements Action { - public type: string = AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_ERROR; +export class RefreshEpersonAndTokenRedirectErrorAction implements Action { + public type: string = AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR; } /** diff --git a/src/app/core/auth/auth.effects.spec.ts b/src/app/core/auth/auth.effects.spec.ts index c32bf1193ab..c23d671583b 100644 --- a/src/app/core/auth/auth.effects.spec.ts +++ b/src/app/core/auth/auth.effects.spec.ts @@ -20,8 +20,8 @@ import { CheckAuthenticationTokenCookieAction, LogOutErrorAction, LogOutSuccessAction, - RefreshStateTokenRedirectErrorAction, - RefreshStateTokenRedirectSuccessAction, + RefreshEpersonAndTokenRedirectErrorAction, + RefreshEpersonAndTokenRedirectSuccessAction, RefreshTokenAndRedirectAction, RefreshTokenAndRedirectErrorAction, RefreshTokenAndRedirectSuccessAction, @@ -468,12 +468,12 @@ describe('AuthEffects', () => { actions = hot('--a-', { a: { - type: AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT, + type: AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT, payload: { token, redirectUrl } } }); - const expected = cold('--b-', { b: new RefreshStateTokenRedirectSuccessAction(EPersonMock, token, redirectUrl) }); + const expected = cold('--b-', { b: new RefreshEpersonAndTokenRedirectSuccessAction(EPersonMock, token, redirectUrl) }); expect(authEffects.refreshStateTokenRedirect$).toBeObservable(expected); done(); @@ -486,12 +486,12 @@ describe('AuthEffects', () => { actions = hot('--a-', { a: { - type: AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT, + type: AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT, payload: { token, redirectUrl } } }); - const expected = cold('--b-', { b: new RefreshStateTokenRedirectErrorAction() }); + const expected = cold('--b-', { b: new RefreshEpersonAndTokenRedirectErrorAction() }); expect(authEffects.refreshStateTokenRedirect$).toBeObservable(expected); done(); @@ -510,7 +510,7 @@ describe('AuthEffects', () => { actions = hot('--a-', { a: { - type: AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS, + type: AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS, payload: { ePerson: EPersonMock, token, redirectUrl } } }); diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index 25380328eba..c96aa34916f 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -35,9 +35,9 @@ import { LogOutErrorAction, LogOutSuccessAction, RedirectAfterLoginSuccessAction, - RefreshStateTokenRedirectAction, - RefreshStateTokenRedirectErrorAction, - RefreshStateTokenRedirectSuccessAction, + RefreshEpersonAndTokenRedirectAction, + RefreshEpersonAndTokenRedirectErrorAction, + RefreshEpersonAndTokenRedirectSuccessAction, RefreshTokenAction, RefreshTokenAndRedirectAction, RefreshTokenAndRedirectErrorAction, @@ -274,21 +274,21 @@ export class AuthEffects { ); public refreshStateTokenRedirect$: Observable = createEffect(() => this.actions$ - .pipe(ofType(AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT), - switchMap((action: RefreshStateTokenRedirectAction) => + .pipe(ofType(AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT), + switchMap((action: RefreshEpersonAndTokenRedirectAction) => this.authService.getAuthenticatedUserFromStore() .pipe( switchMap(user => this.authService.retrieveAuthenticatedUserById(user.id)), - map(user => new RefreshStateTokenRedirectSuccessAction(user, action.payload.token, action.payload.redirectUrl)), - catchError((error) => observableOf(new RefreshStateTokenRedirectErrorAction())) + map(user => new RefreshEpersonAndTokenRedirectSuccessAction(user, action.payload.token, action.payload.redirectUrl)), + catchError((error) => observableOf(new RefreshEpersonAndTokenRedirectErrorAction())) ) ) ) ); public refreshStateTokenRedirectSuccess$: Observable = createEffect(() => this.actions$ - .pipe(ofType(AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS), - map((action: RefreshStateTokenRedirectAction) => new RefreshTokenAndRedirectAction(action.payload.token, action.payload.redirectUrl))) + .pipe(ofType(AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS), + map((action: RefreshEpersonAndTokenRedirectAction) => new RefreshTokenAndRedirectAction(action.payload.token, action.payload.redirectUrl))) ); public refreshTokenAndRedirectSuccess$: Observable = createEffect(() => this.actions$ diff --git a/src/app/core/auth/auth.reducer.ts b/src/app/core/auth/auth.reducer.ts index 7deb207fc2e..101f918dbef 100644 --- a/src/app/core/auth/auth.reducer.ts +++ b/src/app/core/auth/auth.reducer.ts @@ -8,7 +8,7 @@ import { LogOutErrorAction, RedirectWhenAuthenticationIsRequiredAction, RedirectWhenTokenExpiredAction, - RefreshStateTokenRedirectSuccessAction, + RefreshEpersonAndTokenRedirectSuccessAction, RefreshTokenAndRedirectSuccessAction, RefreshTokenSuccessAction, RetrieveAuthenticatedEpersonSuccessAction, @@ -191,20 +191,20 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut user: undefined }); - case AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT: + case AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT: return Object.assign({}, state, { loading: true, loaded: false, }); - case AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_SUCCESS: + case AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS: return Object.assign({}, state, { loading: false, loaded: false, - user: (action as RefreshStateTokenRedirectSuccessAction).payload.ePerson, + user: (action as RefreshEpersonAndTokenRedirectSuccessAction).payload.ePerson, }); - case AuthActionTypes.REFRESH_STATE_TOKEN_REDIRECT_ERROR: + case AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR: return Object.assign({}, state, { loading: false, loaded: false, diff --git a/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts b/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts index bcaf045578e..3dd6b92f342 100644 --- a/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts +++ b/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts @@ -9,7 +9,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { of as observableOf } from 'rxjs'; import { Store } from '@ngrx/store'; import { By } from '@angular/platform-browser'; -import { LogOutAction, RefreshStateTokenRedirectAction } from '../../core/auth/auth.actions'; +import { LogOutAction, RefreshEpersonAndTokenRedirectAction } from '../../core/auth/auth.actions'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { AuthTokenInfo } from '../../core/auth/models/auth-token-info.model'; @@ -113,7 +113,7 @@ describe('EndUserAgreementComponent', () => { }); it('should refresh the token and navigate the user to the redirect url', () => { - expect(store.dispatch).toHaveBeenCalledWith(new RefreshStateTokenRedirectAction(token, redirectUrl)); + expect(store.dispatch).toHaveBeenCalledWith(new RefreshEpersonAndTokenRedirectAction(token, redirectUrl)); }); }); diff --git a/src/app/info/end-user-agreement/end-user-agreement.component.ts b/src/app/info/end-user-agreement/end-user-agreement.component.ts index f2e4c469ceb..d6ab02c38a1 100644 --- a/src/app/info/end-user-agreement/end-user-agreement.component.ts +++ b/src/app/info/end-user-agreement/end-user-agreement.component.ts @@ -1,4 +1,4 @@ -import { LogOutAction, RefreshStateTokenRedirectAction } from '../../core/auth/auth.actions'; +import { LogOutAction, RefreshEpersonAndTokenRedirectAction } from '../../core/auth/auth.actions'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { AuthService } from '../../core/auth/auth.service'; import { map, switchMap, take } from 'rxjs/operators'; @@ -92,7 +92,7 @@ export class EndUserAgreementComponent implements OnInit, OnDestroy { take(1) ).subscribe((redirectUrl) => { if (isNotEmpty(redirectUrl)) { - this.store.dispatch(new RefreshStateTokenRedirectAction(this.authService.getToken(), redirectUrl)); + this.store.dispatch(new RefreshEpersonAndTokenRedirectAction(this.authService.getToken(), redirectUrl)); } }); } From 0322defd164c2b1fa8b41f2cbfc40a56a6fec518 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Oct 2023 20:29:56 +0200 Subject: [PATCH 179/195] [CST-10703] Use alert box for info messages --- .../review-account-info/review-account-info.component.html | 4 ++-- .../external-log-in/external-log-in.component.html | 4 ++-- src/assets/i18n/en.json5 | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html index 039bc763b28..da5d943676f 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html @@ -1,7 +1,7 @@

{{'external-login-validation.review-account-info.header' | translate}}

-

-
+ +
diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html index 2bd3b736774..aefa9719669 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html +++ b/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html @@ -5,9 +5,9 @@

{{ 'external-login.confirmation.header' | translate}}

-
+ {{ informationText }} -
+
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 8a7ea498216..6b4a33f7960 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7204,7 +7204,7 @@ "external-login.confirm-email-sent.header": "Confirmation email sent", - "external-login.confirm-email-sent.info": " We have sent an emait to the provided address to validate your input.
Please follow the instructions in the email to complete the login process.", + "external-login.confirm-email-sent.info": " We have sent an email to the provided address to validate your input.
Please follow the instructions in the email to complete the login process.", "external-login.provide-email.header": "Provide email", @@ -7212,7 +7212,7 @@ "external-login-validation.review-account-info.header": "Review your account information", - "external-login-validation.review-account-info.info": "The information received from ORCID differs from the one recorded in your profile.
Please review them and decide if you want to update any of them.After saving you will be redirected to your profile page.", + "external-login-validation.review-account-info.info": "The information received from ORCID differs from the one recorded in your profile.
Please review them and decide if you want to update any of them. After saving you will be redirected to your profile page.", "external-login-validation.review-account-info.table.header.information": "Information", From 5362313558373be08fe9771777fc47921d3be494 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 10 Oct 2023 09:27:40 +0200 Subject: [PATCH 180/195] [CST-10703] Fix lint --- .../orcid-confirmation/orcid-confirmation.component.spec.ts | 6 +++--- .../services/external-login.service.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts index 441a326ef53..66330b2aa76 100644 --- a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts +++ b/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts @@ -3,10 +3,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { OrcidConfirmationComponent } from './orcid-confirmation.component'; import { FormBuilder, FormGroup } from '@angular/forms'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; -import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; +import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { BrowserOnlyMockPipe } from '../../../../shared/testing/browser-only-mock.pipe'; +import { BrowserOnlyMockPipe } from '../../../testing/browser-only-mock.pipe'; import { Registration } from 'src/app/core/shared/registration.model'; import { mockRegistrationDataModel } from '../../models/registration-data.mock.model'; diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/shared/external-log-in-complete/services/external-login.service.ts index 03c671b570a..38cca02a9ea 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.ts +++ b/src/app/shared/external-log-in-complete/services/external-login.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { Observable, filter, map, tap } from 'rxjs'; +import { Observable, filter, map } from 'rxjs'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { RemoteData } from '../../../core/data/remote-data'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; From 821cb99f5be3100a895325be5bc9f53e0e9f914d Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 10 Oct 2023 11:22:36 +0200 Subject: [PATCH 181/195] [CST-10703] relocation of files --- .../external-log-in.methods-decorator.ts | 2 +- .../external-login-method-entry.component.ts | 0 .../confirm-email.component.html | 0 .../confirm-email.component.scss | 0 .../confirm-email.component.spec.ts | 20 +++++------ .../confirm-email/confirm-email.component.ts | 16 ++++----- .../confirmation-sent.component.html | 0 .../confirmation-sent.component.scss | 0 .../confirmation-sent.component.spec.ts | 2 +- .../confirmation-sent.component.ts | 0 .../provide-email.component.html | 0 .../provide-email.component.scss | 0 .../provide-email.component.spec.ts | 2 +- .../provide-email/provide-email.component.ts | 2 +- .../external-log-in.component.html | 0 .../external-log-in.component.scss | 0 .../external-log-in.component.spec.ts | 32 ++++++++++------- .../external-log-in.component.ts | 12 +++---- .../external-login-complete.module.ts | 35 +++++++++++++++++++ .../guards/registration-token.guard.spec.ts | 14 ++++---- .../guards/registration-token.guard.ts | 13 +++---- .../models/registration-data.mock.model.ts | 7 ++-- .../orcid-confirmation.component.html | 8 ++--- .../orcid-confirmation.component.scss | 0 .../orcid-confirmation.component.spec.ts | 10 +++--- .../orcid-confirmation.component.ts | 9 +++-- .../registration-data.resolver.spec.ts | 6 ++-- .../resolvers/registration-data.resolver.ts | 10 +++--- .../services/external-login.service.spec.ts | 14 ++++---- .../services/external-login.service.ts | 18 +++++----- ...-email-confirmation-page.component.spec.ts | 2 +- ...al-login-email-confirmation-page.module.ts | 4 +-- .../external-login-page-routing.module.ts | 4 +-- .../external-login-page.module.ts | 4 ++- ...review-account-info-page-routing.module.ts | 2 +- ...review-account-info-page.component.spec.ts | 2 +- ...l-login-review-account-info-page.module.ts | 3 +- .../review-account-info.component.html | 2 +- .../review-account-info.component.spec.ts | 4 +-- .../review-account-info.component.ts | 2 +- ...-menu-expandable-section.component.spec.ts | 2 +- src/app/shared/shared.module.ts | 10 ------ 42 files changed, 154 insertions(+), 119 deletions(-) rename src/app/{shared/external-log-in-complete => external-log-in-complete/decorators}/external-log-in.methods-decorator.ts (90%) rename src/app/{shared/external-log-in-complete => external-log-in-complete/decorators}/external-login-method-entry.component.ts (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts (88%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts (89%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts (95%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss (100%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts (96%) rename src/app/{shared => }/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts (96%) rename src/app/{shared => }/external-log-in-complete/external-log-in/external-log-in.component.html (100%) rename src/app/{shared => }/external-log-in-complete/external-log-in/external-log-in.component.scss (100%) rename src/app/{shared => }/external-log-in-complete/external-log-in/external-log-in.component.spec.ts (78%) rename src/app/{shared => }/external-log-in-complete/external-log-in/external-log-in.component.ts (89%) create mode 100644 src/app/external-log-in-complete/external-login-complete.module.ts rename src/app/{shared => }/external-log-in-complete/guards/registration-token.guard.spec.ts (81%) rename src/app/{shared => }/external-log-in-complete/guards/registration-token.guard.ts (68%) rename src/app/{shared => }/external-log-in-complete/models/registration-data.mock.model.ts (82%) rename src/app/{shared => }/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html (83%) rename src/app/{shared => }/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.scss (100%) rename src/app/{shared => }/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts (84%) rename src/app/{shared => }/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts (83%) rename src/app/{shared => }/external-log-in-complete/resolvers/registration-data.resolver.spec.ts (89%) rename src/app/{shared => }/external-log-in-complete/resolvers/registration-data.resolver.ts (76%) rename src/app/{shared => }/external-log-in-complete/services/external-login.service.spec.ts (84%) rename src/app/{shared => }/external-log-in-complete/services/external-login.service.ts (77%) diff --git a/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts b/src/app/external-log-in-complete/decorators/external-log-in.methods-decorator.ts similarity index 90% rename from src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts rename to src/app/external-log-in-complete/decorators/external-log-in.methods-decorator.ts index ce90aea0a3f..cfd3a42d40b 100644 --- a/src/app/shared/external-log-in-complete/external-log-in.methods-decorator.ts +++ b/src/app/external-log-in-complete/decorators/external-log-in.methods-decorator.ts @@ -1,4 +1,4 @@ -import { AuthRegistrationType } from 'src/app/core/auth/models/auth.registration-type'; +import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; /** * Map to store the external login confirmation component for the given auth method type diff --git a/src/app/shared/external-log-in-complete/external-login-method-entry.component.ts b/src/app/external-log-in-complete/decorators/external-login-method-entry.component.ts similarity index 100% rename from src/app/shared/external-log-in-complete/external-login-method-entry.component.ts rename to src/app/external-log-in-complete/decorators/external-login-method-entry.component.ts diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html b/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html rename to src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss b/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss rename to src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts similarity index 88% rename from src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts rename to src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts index e250fc41e03..eb398597aac 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -8,20 +8,18 @@ import { TranslateModule, TranslateService, } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { ExternalLoginService } from '../../services/external-login.service'; -import { AuthService } from '../../../../core/auth/auth.service'; -import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; -import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { - createSuccessfulRemoteDataObject$, -} from '../../../../shared/remote-data.utils'; -import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { of } from 'rxjs'; -import { Registration } from '../../../../core/shared/registration.model'; -import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; +import { AuthService } from '../../../core/auth/auth.service'; +import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; +import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; +import { HardRedirectService } from '../../../core/services/hard-redirect.service'; +import { Registration } from '../../../core/shared/registration.model'; +import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; describe('ConfirmEmailComponent', () => { let component: ConfirmEmailComponent; diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts similarity index 89% rename from src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts rename to src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts index 73bc5adfa2c..02692afa7a3 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts @@ -1,17 +1,17 @@ import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; -import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../../core/shared/operators'; -import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; -import { hasNoValue, hasValue } from '../../../../shared/empty.util'; -import { EPerson } from '../../../../core/eperson/models/eperson.model'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import isEqual from 'lodash/isEqual'; -import { AuthService } from '../../../../core/auth/auth.service'; import { Subscription, combineLatest, take } from 'rxjs'; -import { Registration } from '../../../../core/shared/registration.model'; -import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; +import { AuthService } from '../../../core/auth/auth.service'; +import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; +import { HardRedirectService } from '../../../core/services/hard-redirect.service'; +import { getRemoteDataPayload, getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { Registration } from '../../../core/shared/registration.model'; +import { hasNoValue, hasValue } from '../../../shared/empty.util'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; @Component({ selector: 'ds-confirm-email', diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html b/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html rename to src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss b/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss rename to src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts b/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts similarity index 95% rename from src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts rename to src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts index 3de88d9ba26..791e0e58e46 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts +++ b/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts @@ -4,8 +4,8 @@ import { ConfirmationSentComponent } from './confirmation-sent.component'; import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { of } from 'rxjs'; +import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock'; describe('ConfirmationSentComponent', () => { let component: ConfirmationSentComponent; diff --git a/src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts b/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts rename to src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html b/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html rename to src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss b/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss similarity index 100% rename from src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss rename to src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts b/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts similarity index 96% rename from src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts rename to src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts index 3ccb0a189a5..a346bfa930f 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts +++ b/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts @@ -4,9 +4,9 @@ import { ProvideEmailComponent } from './provide-email.component'; import { FormBuilder } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ExternalLoginService } from '../../services/external-login.service'; +import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock'; describe('ProvideEmailComponent', () => { let component: ProvideEmailComponent; diff --git a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts similarity index 96% rename from src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts rename to src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts index a63486dea60..52687b62010 100644 --- a/src/app/shared/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts +++ b/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts @@ -2,7 +2,7 @@ import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/c import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; import { Subscription } from 'rxjs'; -import { hasValue } from '../../../../shared/empty.util'; +import { hasValue } from '../../../shared/empty.util'; @Component({ selector: 'ds-provide-email', diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/external-log-in-complete/external-log-in/external-log-in.component.html similarity index 100% rename from src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.html rename to src/app/external-log-in-complete/external-log-in/external-log-in.component.html diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.scss b/src/app/external-log-in-complete/external-log-in/external-log-in.component.scss similarity index 100% rename from src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.scss rename to src/app/external-log-in-complete/external-log-in/external-log-in.component.scss diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts b/src/app/external-log-in-complete/external-log-in/external-log-in.component.spec.ts similarity index 78% rename from src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts rename to src/app/external-log-in-complete/external-log-in/external-log-in.component.spec.ts index 9e3b74f2bfe..4f444e920df 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.spec.ts +++ b/src/app/external-log-in-complete/external-log-in/external-log-in.component.spec.ts @@ -3,17 +3,19 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLogInComponent } from './external-log-in.component'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { CommonModule } from '@angular/common'; -import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; -import { EventEmitter, Injector } from '@angular/core'; +import { EventEmitter, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { of as observableOf } from 'rxjs'; import { FormBuilder } from '@angular/forms'; -import { AuthService } from '../../../core/auth/auth.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { AuthServiceMock } from '../../mocks/auth.service.mock'; -import { MetadataValue } from '../../../core/shared/metadata.models'; -import { Registration } from '../../../core/shared/registration.model'; -import { AuthRegistrationType } from '../../../core/auth/models/auth.registration-type'; +import { AuthService } from '../../core/auth/auth.service'; +import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; +import { MetadataValue } from '../../core/shared/metadata.models'; +import { Registration } from '../../core/shared/registration.model'; +import { AuthServiceMock } from '../../shared/mocks/auth.service.mock'; +import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; +import { BrowserOnlyMockPipe } from '../../shared/testing/browser-only-mock.pipe'; +import { OrcidConfirmationComponent } from '../registration-types/orcid-confirmation/orcid-confirmation.component'; describe('ExternalLogInComponent', () => { let component: ExternalLogInComponent; @@ -40,7 +42,9 @@ describe('ExternalLogInComponent', () => { }; const translateServiceStub = { get: () => observableOf('Info Text'), - instant: (key: any) => 'Info Text', + instant(key) { + return 'Info Text'; + }, onLangChange: new EventEmitter(), onTranslationChange: new EventEmitter(), onDefaultLangChange: new EventEmitter() @@ -48,7 +52,7 @@ describe('ExternalLogInComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ExternalLogInComponent], + declarations: [ExternalLogInComponent, BrowserOnlyMockPipe, ], providers: [ { provide: TranslateService, useValue: translateServiceStub }, { provide: Injector, useValue: {} }, @@ -64,12 +68,18 @@ describe('ExternalLogInComponent', () => { useClass: TranslateLoaderMock } }), - ] + ], + schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); }); beforeEach(() => { + TestBed.overrideComponent(OrcidConfirmationComponent, { + set: { + template: '
Mocked OrcidConfirmationComponent
', + }, + }); fixture = TestBed.createComponent(ExternalLogInComponent); component = fixture.componentInstance; component.registrationData = Object.assign(new Registration, registrationDataMock); @@ -111,5 +121,3 @@ describe('ExternalLogInComponent', () => { expect(infoText.nativeElement.innerHTML).toContain('Info Text'); }); }); - - diff --git a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts b/src/app/external-log-in-complete/external-log-in/external-log-in.component.ts similarity index 89% rename from src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts rename to src/app/external-log-in-complete/external-log-in/external-log-in.component.ts index d167a0c5d08..3917567e414 100644 --- a/src/app/shared/external-log-in-complete/external-log-in/external-log-in.component.ts +++ b/src/app/external-log-in-complete/external-log-in/external-log-in.component.ts @@ -5,13 +5,13 @@ import { Input, Injector, } from '@angular/core'; -import { getExternalLoginConfirmationType } from '../external-log-in.methods-decorator'; -import { hasValue } from '../../empty.util'; +import { NgbModalRef, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { AuthService } from '../../../core/auth/auth.service'; -import { Registration } from '../../../core/shared/registration.model'; -import { AuthRegistrationType } from '../../../core/auth/models/auth.registration-type'; +import { AuthService } from '../../core/auth/auth.service'; +import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; +import { Registration } from '../../core/shared/registration.model'; +import { hasValue } from '../../shared/empty.util'; +import { getExternalLoginConfirmationType } from '../decorators/external-log-in.methods-decorator'; @Component({ selector: 'ds-external-log-in', diff --git a/src/app/external-log-in-complete/external-login-complete.module.ts b/src/app/external-log-in-complete/external-login-complete.module.ts new file mode 100644 index 00000000000..d68f13b0eca --- /dev/null +++ b/src/app/external-log-in-complete/external-login-complete.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ConfirmEmailComponent } from './email-confirmation/confirm-email/confirm-email.component'; +import { ConfirmationSentComponent } from './email-confirmation/confirmation-sent/confirmation-sent.component'; +import { ProvideEmailComponent } from './email-confirmation/provide-email/provide-email.component'; +import { ExternalLogInComponent } from './external-log-in/external-log-in.component'; +import { OrcidConfirmationComponent } from './registration-types/orcid-confirmation/orcid-confirmation.component'; +import { SharedModule } from '../shared/shared.module'; + +const COMPONENTS = [ + ExternalLogInComponent, + ProvideEmailComponent, + ConfirmEmailComponent, + ConfirmationSentComponent, +]; + +const ENTRY_COMPONENTS = [OrcidConfirmationComponent]; + +@NgModule({ + declarations: [...COMPONENTS, ...ENTRY_COMPONENTS], + imports: [CommonModule, SharedModule], + exports: [...COMPONENTS, ...ENTRY_COMPONENTS], +}) +export class ExternalLoginCompleteModule { + /** + * NOTE: this method allows to resolve issue with components that using a custom decorator + * which are not loaded during SSR otherwise + */ + static withEntryComponents() { + return { + ngModule: ExternalLoginCompleteModule, + providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })), + }; + } +} diff --git a/src/app/shared/external-log-in-complete/guards/registration-token.guard.spec.ts b/src/app/external-log-in-complete/guards/registration-token.guard.spec.ts similarity index 81% rename from src/app/shared/external-log-in-complete/guards/registration-token.guard.spec.ts rename to src/app/external-log-in-complete/guards/registration-token.guard.spec.ts index f62769add66..155e27c4c53 100644 --- a/src/app/shared/external-log-in-complete/guards/registration-token.guard.spec.ts +++ b/src/app/external-log-in-complete/guards/registration-token.guard.spec.ts @@ -2,12 +2,13 @@ import { TestBed } from '@angular/core/testing'; import { RegistrationTokenGuard } from './registration-token.guard'; import { ActivatedRoute, convertToParamMap, Params, Router } from '@angular/router'; import { of as observableOf } from 'rxjs'; -import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; -import { AuthService } from '../../../core/auth/auth.service'; -import { RouterMock } from '../../../shared/mocks/router.mock'; -import { Registration } from '../../../core/shared/registration.model'; -import { EPerson } from '../../../core/eperson/models/eperson.model'; -import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { AuthService } from '../../core/auth/auth.service'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { Registration } from '../../core/shared/registration.model'; +import { RouterMock } from '../../shared/mocks/router.mock'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { AuthRegistrationType } from 'src/app/core/auth/models/auth.registration-type'; describe('RegistrationTokenGuard', () => { let guard: RegistrationTokenGuard; @@ -16,6 +17,7 @@ describe('RegistrationTokenGuard', () => { { email: 'test@email.org', token: 'test-token', + registrationType: AuthRegistrationType.Orcid, }); const epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { searchRegistrationByToken: createSuccessfulRemoteDataObject$(registrationWithGroups) diff --git a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts b/src/app/external-log-in-complete/guards/registration-token.guard.ts similarity index 68% rename from src/app/shared/external-log-in-complete/guards/registration-token.guard.ts rename to src/app/external-log-in-complete/guards/registration-token.guard.ts index d565dfbd91a..8f0db7b63f4 100644 --- a/src/app/shared/external-log-in-complete/guards/registration-token.guard.ts +++ b/src/app/external-log-in-complete/guards/registration-token.guard.ts @@ -6,11 +6,12 @@ import { RouterStateSnapshot, } from '@angular/router'; import { Observable, map, of } from 'rxjs'; -import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; -import { RemoteData } from '../../../core/data/remote-data'; -import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; -import { Registration } from '../../../core/shared/registration.model'; -import { hasValue } from '../../empty.util'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RemoteData } from '../../core/data/remote-data'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { Registration } from '../../core/shared/registration.model'; +import { hasValue } from '../../shared/empty.util'; +import { AuthRegistrationType } from 'src/app/core/auth/models/auth.registration-type'; @Injectable({ providedIn: 'root', @@ -38,7 +39,7 @@ export class RegistrationTokenGuard implements CanActivate { getFirstCompletedRemoteData(), map( (data: RemoteData) => { - if (data.hasSucceeded && hasValue(data)) { + if (data.hasSucceeded && hasValue(data.payload) && !data.payload.registrationType.includes(AuthRegistrationType.Validation)) { return true; } else { this.router.navigate(['/404']); diff --git a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/external-log-in-complete/models/registration-data.mock.model.ts similarity index 82% rename from src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts rename to src/app/external-log-in-complete/models/registration-data.mock.model.ts index 6dc1eb28632..4c288d17483 100644 --- a/src/app/shared/external-log-in-complete/models/registration-data.mock.model.ts +++ b/src/app/external-log-in-complete/models/registration-data.mock.model.ts @@ -1,6 +1,7 @@ -import { AuthMethodType } from '../../../core/auth/models/auth.method-type'; -import { MetadataValue } from '../../../core/shared/metadata.models'; -import { Registration } from '../../../core/shared/registration.model'; +import { AuthMethodType } from '../../core/auth/models/auth.method-type'; +import { MetadataValue } from '../../core/shared/metadata.models'; +import { Registration } from '../../core/shared/registration.model'; + export const mockRegistrationDataModel: Registration = Object.assign( new Registration(), diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html b/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html similarity index 83% rename from src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html rename to src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html index 4c81c7fdb15..df5bb454dde 100644 --- a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html +++ b/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html @@ -1,7 +1,7 @@ - - {{ registratioData.registrationType }} + - - { let component: OrcidConfirmationComponent; let fixture: ComponentFixture; - let model: Registration; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ OrcidConfirmationComponent, - BrowserOnlyMockPipe, + BrowserOnlyMockPipe ], providers: [ FormBuilder, diff --git a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts b/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts similarity index 83% rename from src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts rename to src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts index 3cd3b875dc0..4cba863bef3 100644 --- a/src/app/shared/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts +++ b/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts @@ -1,10 +1,9 @@ import { Component, OnInit, ChangeDetectionStrategy, Inject } from '@angular/core'; -import { renderExternalLoginConfirmationFor } from '../../external-log-in.methods-decorator'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { ExternalLoginMethodEntryComponent } from '../../external-login-method-entry.component'; -import { Registration } from '../../../../core/shared/registration.model'; -import { AuthRegistrationType } from '../../../../core/auth/models/auth.registration-type'; - +import { AuthRegistrationType } from '../../../core/auth/models/auth.registration-type'; +import { Registration } from '../../../core/shared/registration.model'; +import { renderExternalLoginConfirmationFor } from '../../decorators/external-log-in.methods-decorator'; +import { ExternalLoginMethodEntryComponent } from '../../decorators/external-login-method-entry.component'; @Component({ selector: 'ds-orcid-confirmation', templateUrl: './orcid-confirmation.component.html', diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts b/src/app/external-log-in-complete/resolvers/registration-data.resolver.spec.ts similarity index 89% rename from src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts rename to src/app/external-log-in-complete/resolvers/registration-data.resolver.spec.ts index 0d1f415a3b4..82641a6a94d 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.spec.ts +++ b/src/app/external-log-in-complete/resolvers/registration-data.resolver.spec.ts @@ -1,10 +1,10 @@ import { TestBed } from '@angular/core/testing'; import { RegistrationDataResolver } from './registration-data.resolver'; -import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; -import { Registration } from '../../../core/shared/registration.model'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { Registration } from '../../core/shared/registration.model'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; describe('RegistrationDataResolver', () => { let resolver: RegistrationDataResolver; diff --git a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts b/src/app/external-log-in-complete/resolvers/registration-data.resolver.ts similarity index 76% rename from src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts rename to src/app/external-log-in-complete/resolvers/registration-data.resolver.ts index 6a9b6b8b31b..49667ab77a0 100644 --- a/src/app/shared/external-log-in-complete/resolvers/registration-data.resolver.ts +++ b/src/app/external-log-in-complete/resolvers/registration-data.resolver.ts @@ -5,11 +5,11 @@ import { ActivatedRouteSnapshot, } from '@angular/router'; import { Observable } from 'rxjs'; -import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; -import { hasValue } from '../../empty.util'; -import { Registration } from '../../../core/shared/registration.model'; -import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; -import { RemoteData } from '../../../core/data/remote-data'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RemoteData } from '../../core/data/remote-data'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { Registration } from '../../core/shared/registration.model'; +import { hasValue } from '../../shared/empty.util'; @Injectable({ providedIn: 'root', diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts b/src/app/external-log-in-complete/services/external-login.service.spec.ts similarity index 84% rename from src/app/shared/external-log-in-complete/services/external-login.service.spec.ts rename to src/app/external-log-in-complete/services/external-login.service.spec.ts index ce0dcb4e269..896fec12a8b 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.spec.ts +++ b/src/app/external-log-in-complete/services/external-login.service.spec.ts @@ -3,16 +3,16 @@ import { TestBed } from '@angular/core/testing'; import { ExternalLoginService } from './external-login.service'; import { TranslateService } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; -import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; -import { RemoteData } from '../../../core/data/remote-data'; -import { Registration } from '../../../core/shared/registration.model'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { RouterMock } from '../../mocks/router.mock'; -import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; -import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RemoteData } from '../../core/data/remote-data'; +import { Registration } from '../../core/shared/registration.model'; +import { RouterMock } from '../../shared/mocks/router.mock'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; describe('ExternalLoginService', () => { let service: ExternalLoginService; diff --git a/src/app/shared/external-log-in-complete/services/external-login.service.ts b/src/app/external-log-in-complete/services/external-login.service.ts similarity index 77% rename from src/app/shared/external-log-in-complete/services/external-login.service.ts rename to src/app/external-log-in-complete/services/external-login.service.ts index 03c671b570a..70ab98832ff 100644 --- a/src/app/shared/external-log-in-complete/services/external-login.service.ts +++ b/src/app/external-log-in-complete/services/external-login.service.ts @@ -1,16 +1,16 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { Observable, filter, map, tap } from 'rxjs'; -import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; -import { RemoteData } from '../../../core/data/remote-data'; -import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; -import { NotificationsService } from '../../notifications/notifications.service'; +import { Observable, filter, map } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; -import { NoContent } from '../../../core/shared/NoContent.model'; -import { AuthMethod } from 'src/app/core/auth/models/auth.method'; -import { getAuthenticationMethods } from 'src/app/core/auth/selectors'; +import { AuthMethod } from '../../core/auth/models/auth.method'; +import { getAuthenticationMethods } from '../../core/auth/selectors'; import { Store, select } from '@ngrx/store'; -import { CoreState } from 'src/app/core/core-state.model'; +import { CoreState } from '../../core/core-state.model'; +import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; +import { RemoteData } from '../../core/data/remote-data'; +import { NoContent } from '../../core/shared/NoContent.model'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; @Injectable({ providedIn: 'root' diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts index 89a96222d13..270dc60125e 100644 --- a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts @@ -1,9 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; -import { ConfirmationSentComponent } from '../shared/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; +import { ConfirmationSentComponent } from '../external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component'; describe('ExternalLoginEmailConfirmationPageComponent', () => { let component: ExternalLoginEmailConfirmationPageComponent; diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts index 36684f2f367..d62da55dbd7 100644 --- a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { ExternalLoginEmailConfirmationPageRoutingModule } from './external-login-email-confirmation-page-routing.module'; import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; -import { SharedModule } from '../shared/shared.module'; +import { ExternalLoginCompleteModule } from '../external-log-in-complete/external-login-complete.module'; @NgModule({ @@ -13,7 +13,7 @@ import { SharedModule } from '../shared/shared.module'; imports: [ CommonModule, ExternalLoginEmailConfirmationPageRoutingModule, - SharedModule + ExternalLoginCompleteModule, ] }) export class ExternalLoginEmailConfirmationPageModule { } diff --git a/src/app/external-login-page/external-login-page-routing.module.ts b/src/app/external-login-page/external-login-page-routing.module.ts index 21bbb7ee920..641809740d3 100644 --- a/src/app/external-login-page/external-login-page-routing.module.ts +++ b/src/app/external-login-page/external-login-page-routing.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; -import { RegistrationTokenGuard } from '../shared/external-log-in-complete/guards/registration-token.guard'; -import { RegistrationDataResolver } from '../shared/external-log-in-complete/resolvers/registration-data.resolver'; +import { RegistrationDataResolver } from '../external-log-in-complete/resolvers/registration-data.resolver'; +import { RegistrationTokenGuard } from '../external-log-in-complete/guards/registration-token.guard'; const routes: Routes = [ { diff --git a/src/app/external-login-page/external-login-page.module.ts b/src/app/external-login-page/external-login-page.module.ts index 697ae7e1a8e..ac68dc5c309 100644 --- a/src/app/external-login-page/external-login-page.module.ts +++ b/src/app/external-login-page/external-login-page.module.ts @@ -5,6 +5,7 @@ import { ExternalLoginPageRoutingModule } from './external-login-page-routing.mo import { ExternalLoginPageComponent } from './external-login-page.component'; import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; import { SharedModule } from '../shared/shared.module'; +import { ExternalLoginCompleteModule } from '../external-log-in-complete/external-login-complete.module'; const COMPONENTS = [ ExternalLoginPageComponent, @@ -18,7 +19,8 @@ const COMPONENTS = [ imports: [ CommonModule, ExternalLoginPageRoutingModule, - SharedModule + SharedModule, + ExternalLoginCompleteModule ] }) export class ExternalLoginPageModule { } diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts index 1119c9fc49b..66e3e29d056 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; -import { RegistrationDataResolver } from '../shared/external-log-in-complete/resolvers/registration-data.resolver'; import { ReviewAccountGuard } from './helpers/review-account.guard'; +import { RegistrationDataResolver } from '../external-log-in-complete/resolvers/registration-data.resolver'; const routes: Routes = [ { diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts index 4b136cbef7a..816cdb4e1ff 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; import { of } from 'rxjs'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; -import { mockRegistrationDataModel } from '../shared/external-log-in-complete/models/registration-data.mock.model'; +import { mockRegistrationDataModel } from '../external-log-in-complete/models/registration-data.mock.model'; describe('ExternalLoginReviewAccountInfoPageComponent', () => { let component: ExternalLoginReviewAccountInfoPageComponent; diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts b/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts index 368f988a829..13bad328d1f 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts +++ b/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts @@ -8,7 +8,7 @@ import { ThemedExternalLoginReviewAccountInfoPageComponent } from './themed-exte import { ReviewAccountInfoComponent } from './review-account-info/review-account-info.component'; import { UiSwitchModule } from 'ngx-ui-switch'; import { SharedModule } from '../shared/shared.module'; - +import { ExternalLoginCompleteModule } from '../external-log-in-complete/external-login-complete.module'; @NgModule({ declarations: [ @@ -22,6 +22,7 @@ import { SharedModule } from '../shared/shared.module'; ExternalLoginReviewAccountInfoRoutingModule, SharedModule, UiSwitchModule, + ExternalLoginCompleteModule ] }) export class ExternalLoginReviewAccountInfoModule { } diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html index da5d943676f..fb4e1298be0 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html @@ -19,7 +19,7 @@

{{'external-login-validation.review-account-info.header' | translate}}

- +
{{ registrationData.registrationType | uppercase }}{{ registrationData.registrationType }} {{ registrationData.netId }} diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts index 63545370ab7..2bfbd582e4a 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts @@ -24,8 +24,8 @@ import { CompareValuesPipe } from '../helpers/compare-values.pipe'; import { Registration } from '../../core/shared/registration.model'; import { AuthService } from '../../core/auth/auth.service'; import { AuthServiceMock } from '../../shared/mocks/auth.service.mock'; -import { ExternalLoginService } from '../../shared/external-log-in-complete/services/external-login.service'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; +import { ExternalLoginService } from '../../external-log-in-complete/services/external-login.service'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; @@ -179,7 +179,7 @@ describe('ReviewAccountInfoComponent', () => { const registrationTypeElement: HTMLElement = fixture.nativeElement.querySelector('tbody tr:first-child th'); const netIdElement: HTMLElement = fixture.nativeElement.querySelector('tbody tr:first-child td'); - expect(registrationTypeElement.textContent.trim()).toBe(registrationDataMock.registrationType.toUpperCase()); + expect(registrationTypeElement.textContent.trim()).toBe(registrationDataMock.registrationType); expect(netIdElement.textContent.trim()).toBe(registrationDataMock.netId); }); diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts index cca523ce4c2..79f9cac83c5 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts @@ -17,9 +17,9 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { Router } from '@angular/router'; import { Registration } from '../../core/shared/registration.model'; import { AuthService } from '../../core/auth/auth.service'; -import { ExternalLoginService } from '../../shared/external-log-in-complete/services/external-login.service'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; +import { ExternalLoginService } from '../../external-log-in-complete/services/external-login.service'; export interface ReviewAccountInfoData { label: string; diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.spec.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.spec.ts index 79ab35bd28c..7a72cc907cc 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.spec.ts +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.spec.ts @@ -10,7 +10,7 @@ import { of as observableOf } from 'rxjs'; import { Component } from '@angular/core'; import { DsoEditMenuExpandableSectionComponent } from './dso-edit-menu-expandable-section.component'; import { By } from '@angular/platform-browser'; -import { MenuItemType } from 'src/app/shared/menu/menu-item-type.model'; +import { MenuItemType } from '../../../../shared/menu/menu-item-type.model'; describe('DsoEditMenuExpandableSectionComponent', () => { let component: DsoEditMenuExpandableSectionComponent; diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 65bcd2dfa3c..c59d2bedaf2 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -332,11 +332,6 @@ import { EntityIconDirective } from './entity-icon/entity-icon.directive'; import { AdditionalMetadataComponent } from './object-list/search-result-list-element/additional-metadata/additional-metadata.component'; -import { ExternalLogInComponent } from './external-log-in-complete/external-log-in/external-log-in.component'; -import { OrcidConfirmationComponent } from './external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component'; -import { ProvideEmailComponent } from './external-log-in-complete/email-confirmation/provide-email/provide-email.component'; -import { ConfirmEmailComponent } from './external-log-in-complete/email-confirmation/confirm-email/confirm-email.component'; -import { ConfirmationSentComponent } from './external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component'; const MODULES = [ CommonModule, @@ -475,10 +470,6 @@ const COMPONENTS = [ ExportExcelSelectorComponent, ThemedBrowseMostElementsComponent, SearchChartBarHorizontalComponent, - ExternalLogInComponent, - ProvideEmailComponent, - ConfirmEmailComponent, - ConfirmationSentComponent, ]; const ENTRY_COMPONENTS = [ @@ -553,7 +544,6 @@ const ENTRY_COMPONENTS = [ SearchChartBarHorizontalComponent, RelationshipsListComponent, AdditionalMetadataComponent, - OrcidConfirmationComponent, ]; const PROVIDERS = [ From 2fc33b5ca2e0906efe8e2a130b4a7371df350757 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 10 Oct 2023 13:14:50 +0200 Subject: [PATCH 182/195] [CST-10703] Renamed components and modules --- src/app/app-routing.module.ts | 2 +- .../decorators/external-log-in.methods-decorator.ts | 0 .../external-login-method-entry.component.ts | 0 .../confirm-email/confirm-email.component.html | 0 .../confirm-email/confirm-email.component.scss | 0 .../confirm-email/confirm-email.component.spec.ts | 6 +----- .../confirm-email/confirm-email.component.ts | 6 +++--- .../confirmation-sent.component.html | 0 .../confirmation-sent.component.scss | 0 .../confirmation-sent.component.spec.ts | 2 +- .../confirmation-sent/confirmation-sent.component.ts | 2 +- .../provide-email/provide-email.component.html | 0 .../provide-email/provide-email.component.scss | 0 .../provide-email/provide-email.component.spec.ts | 0 .../provide-email/provide-email.component.ts | 2 +- .../external-log-in/external-log-in.component.html | 0 .../external-log-in/external-log-in.component.scss | 0 .../external-log-in.component.spec.ts | 0 .../external-log-in/external-log-in.component.ts | 10 ++-------- .../external-login.module.ts} | 4 ++-- .../guards/registration-token.guard.spec.ts | 0 .../guards/registration-token.guard.ts | 9 ++------- .../models/registration-data.mock.model.ts | 0 .../orcid-confirmation.component.html | 0 .../orcid-confirmation.component.scss | 0 .../orcid-confirmation.component.spec.ts | 0 .../orcid-confirmation.component.ts | 3 ++- .../resolvers/registration-data.resolver.spec.ts | 0 .../resolvers/registration-data.resolver.ts | 6 +----- .../services/external-login.service.spec.ts | 0 .../services/external-login.service.ts | 4 ++-- ...l-login-email-confirmation-page.component.spec.ts | 4 +++- .../external-login-email-confirmation-page.module.ts | 8 +++++--- .../external-login-page-routing.module.ts | 4 ++-- .../external-login-page.module.ts | 4 ++-- ...-login-review-account-info-page-routing.module.ts | 2 +- ...nal-login-review-account-info-page.component.html | 0 ...nal-login-review-account-info-page.component.scss | 0 ...-login-review-account-info-page.component.spec.ts | 2 +- ...ernal-login-review-account-info-page.component.ts | 2 +- ...external-login-review-account-info-page.module.ts | 8 +++++--- .../helpers/compare-values.pipe.ts | 0 .../helpers/review-account.guard.spec.ts | 0 .../helpers/review-account.guard.ts | 9 ++------- .../review-account-info.component.html | 0 .../review-account-info.component.scss | 0 .../review-account-info.component.spec.ts | 12 ++++-------- .../review-account-info.component.ts | 12 +++--------- ...ernal-login-review-account-info-page.component.ts | 0 49 files changed, 48 insertions(+), 75 deletions(-) rename src/app/{external-log-in-complete => external-log-in}/decorators/external-log-in.methods-decorator.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/decorators/external-login-method-entry.component.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirm-email/confirm-email.component.html (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirm-email/confirm-email.component.scss (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirm-email/confirm-email.component.spec.ts (98%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirm-email/confirm-email.component.ts (97%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirmation-sent/confirmation-sent.component.html (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirmation-sent/confirmation-sent.component.scss (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts (96%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/confirmation-sent/confirmation-sent.component.ts (80%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/provide-email/provide-email.component.html (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/provide-email/provide-email.component.scss (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/provide-email/provide-email.component.spec.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/email-confirmation/provide-email/provide-email.component.ts (96%) rename src/app/{external-log-in-complete => external-log-in}/external-log-in/external-log-in.component.html (100%) rename src/app/{external-log-in-complete => external-log-in}/external-log-in/external-log-in.component.scss (100%) rename src/app/{external-log-in-complete => external-log-in}/external-log-in/external-log-in.component.spec.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/external-log-in/external-log-in.component.ts (96%) rename src/app/{external-log-in-complete/external-login-complete.module.ts => external-log-in/external-login.module.ts} (93%) rename src/app/{external-log-in-complete => external-log-in}/guards/registration-token.guard.spec.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/guards/registration-token.guard.ts (91%) rename src/app/{external-log-in-complete => external-log-in}/models/registration-data.mock.model.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/registration-types/orcid-confirmation/orcid-confirmation.component.html (100%) rename src/app/{external-log-in-complete => external-log-in}/registration-types/orcid-confirmation/orcid-confirmation.component.scss (100%) rename src/app/{external-log-in-complete => external-log-in}/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/registration-types/orcid-confirmation/orcid-confirmation.component.ts (97%) rename src/app/{external-log-in-complete => external-log-in}/resolvers/registration-data.resolver.spec.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/resolvers/registration-data.resolver.ts (93%) rename src/app/{external-log-in-complete => external-log-in}/services/external-login.service.spec.ts (100%) rename src/app/{external-log-in-complete => external-log-in}/services/external-login.service.ts (96%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/external-login-review-account-info-page-routing.module.ts (85%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/external-login-review-account-info-page.component.html (100%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/external-login-review-account-info-page.component.scss (100%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/external-login-review-account-info-page.component.spec.ts (94%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/external-login-review-account-info-page.component.ts (96%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/external-login-review-account-info-page.module.ts (78%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/helpers/compare-values.pipe.ts (100%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/helpers/review-account.guard.spec.ts (100%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/helpers/review-account.guard.ts (92%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/review-account-info/review-account-info.component.html (100%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/review-account-info/review-account-info.component.scss (100%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/review-account-info/review-account-info.component.spec.ts (96%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/review-account-info/review-account-info.component.ts (97%) rename src/app/{external-login-review-account-info => external-login-review-account-info-page}/themed-external-login-review-account-info-page.component.ts (100%) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index a652eadde9e..513628fc413 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -178,7 +178,7 @@ import { RedirectService } from './redirect/redirect.service'; }, { path: 'review-account/:token', - loadChildren: () => import('./external-login-review-account-info/external-login-review-account-info-page.module') + loadChildren: () => import('./external-login-review-account-info-page/external-login-review-account-info-page.module') .then((m) => m.ExternalLoginReviewAccountInfoModule) }, { diff --git a/src/app/external-log-in-complete/decorators/external-log-in.methods-decorator.ts b/src/app/external-log-in/decorators/external-log-in.methods-decorator.ts similarity index 100% rename from src/app/external-log-in-complete/decorators/external-log-in.methods-decorator.ts rename to src/app/external-log-in/decorators/external-log-in.methods-decorator.ts diff --git a/src/app/external-log-in-complete/decorators/external-login-method-entry.component.ts b/src/app/external-log-in/decorators/external-login-method-entry.component.ts similarity index 100% rename from src/app/external-log-in-complete/decorators/external-login-method-entry.component.ts rename to src/app/external-log-in/decorators/external-login-method-entry.component.ts diff --git a/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html similarity index 100% rename from src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.html rename to src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html diff --git a/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.scss similarity index 100% rename from src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.scss rename to src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.scss diff --git a/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.spec.ts similarity index 98% rename from src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts rename to src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.spec.ts index eb398597aac..7c0e7f8d926 100644 --- a/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -3,11 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfirmEmailComponent } from './confirm-email.component'; import { FormBuilder } from '@angular/forms'; import { CommonModule } from '@angular/common'; -import { - TranslateLoader, - TranslateModule, - TranslateService, -} from '@ngx-translate/core'; +import { TranslateLoader, TranslateModule, TranslateService, } from '@ngx-translate/core'; import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { ExternalLoginService } from '../../services/external-login.service'; import { of } from 'rxjs'; diff --git a/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts similarity index 97% rename from src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts rename to src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts index 02692afa7a3..56e28333b62 100644 --- a/src/app/external-log-in-complete/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts @@ -1,14 +1,14 @@ -import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; import { TranslateService } from '@ngx-translate/core'; import isEqual from 'lodash/isEqual'; -import { Subscription, combineLatest, take } from 'rxjs'; +import { combineLatest, Subscription, take } from 'rxjs'; import { AuthService } from '../../../core/auth/auth.service'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { EPerson } from '../../../core/eperson/models/eperson.model'; import { HardRedirectService } from '../../../core/services/hard-redirect.service'; -import { getRemoteDataPayload, getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core/shared/operators'; import { Registration } from '../../../core/shared/registration.model'; import { hasNoValue, hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; diff --git a/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html b/src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.html similarity index 100% rename from src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.html rename to src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.html diff --git a/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss b/src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.scss similarity index 100% rename from src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.scss rename to src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.scss diff --git a/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts b/src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts similarity index 96% rename from src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts rename to src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts index 791e0e58e46..da4e5416d54 100644 --- a/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts +++ b/src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfirmationSentComponent } from './confirmation-sent.component'; import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; -import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { of } from 'rxjs'; import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock'; diff --git a/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts b/src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.ts similarity index 80% rename from src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts rename to src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.ts index 78a0ef81fed..2f82991c0d4 100644 --- a/src/app/external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component.ts +++ b/src/app/external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component.ts @@ -1,4 +1,4 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'ds-confirmation-sent', diff --git a/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html b/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.html similarity index 100% rename from src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.html rename to src/app/external-log-in/email-confirmation/provide-email/provide-email.component.html diff --git a/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss b/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.scss similarity index 100% rename from src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.scss rename to src/app/external-log-in/email-confirmation/provide-email/provide-email.component.scss diff --git a/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts b/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.spec.ts similarity index 100% rename from src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.spec.ts rename to src/app/external-log-in/email-confirmation/provide-email/provide-email.component.spec.ts diff --git a/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts b/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.ts similarity index 96% rename from src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts rename to src/app/external-log-in/email-confirmation/provide-email/provide-email.component.ts index 52687b62010..4e3e220eceb 100644 --- a/src/app/external-log-in-complete/email-confirmation/provide-email/provide-email.component.ts +++ b/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.ts @@ -1,4 +1,4 @@ -import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; import { Subscription } from 'rxjs'; diff --git a/src/app/external-log-in-complete/external-log-in/external-log-in.component.html b/src/app/external-log-in/external-log-in/external-log-in.component.html similarity index 100% rename from src/app/external-log-in-complete/external-log-in/external-log-in.component.html rename to src/app/external-log-in/external-log-in/external-log-in.component.html diff --git a/src/app/external-log-in-complete/external-log-in/external-log-in.component.scss b/src/app/external-log-in/external-log-in/external-log-in.component.scss similarity index 100% rename from src/app/external-log-in-complete/external-log-in/external-log-in.component.scss rename to src/app/external-log-in/external-log-in/external-log-in.component.scss diff --git a/src/app/external-log-in-complete/external-log-in/external-log-in.component.spec.ts b/src/app/external-log-in/external-log-in/external-log-in.component.spec.ts similarity index 100% rename from src/app/external-log-in-complete/external-log-in/external-log-in.component.spec.ts rename to src/app/external-log-in/external-log-in/external-log-in.component.spec.ts diff --git a/src/app/external-log-in-complete/external-log-in/external-log-in.component.ts b/src/app/external-log-in/external-log-in/external-log-in.component.ts similarity index 96% rename from src/app/external-log-in-complete/external-log-in/external-log-in.component.ts rename to src/app/external-log-in/external-log-in/external-log-in.component.ts index 3917567e414..60d91d2aee3 100644 --- a/src/app/external-log-in-complete/external-log-in/external-log-in.component.ts +++ b/src/app/external-log-in/external-log-in/external-log-in.component.ts @@ -1,11 +1,5 @@ -import { - Component, - OnInit, - ChangeDetectionStrategy, - Input, - Injector, -} from '@angular/core'; -import { NgbModalRef, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ChangeDetectionStrategy, Component, Injector, Input, OnInit, } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { AuthService } from '../../core/auth/auth.service'; import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; diff --git a/src/app/external-log-in-complete/external-login-complete.module.ts b/src/app/external-log-in/external-login.module.ts similarity index 93% rename from src/app/external-log-in-complete/external-login-complete.module.ts rename to src/app/external-log-in/external-login.module.ts index d68f13b0eca..b0d75aa7303 100644 --- a/src/app/external-log-in-complete/external-login-complete.module.ts +++ b/src/app/external-log-in/external-login.module.ts @@ -21,14 +21,14 @@ const ENTRY_COMPONENTS = [OrcidConfirmationComponent]; imports: [CommonModule, SharedModule], exports: [...COMPONENTS, ...ENTRY_COMPONENTS], }) -export class ExternalLoginCompleteModule { +export class ExternalLoginModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator * which are not loaded during SSR otherwise */ static withEntryComponents() { return { - ngModule: ExternalLoginCompleteModule, + ngModule: ExternalLoginModule, providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })), }; } diff --git a/src/app/external-log-in-complete/guards/registration-token.guard.spec.ts b/src/app/external-log-in/guards/registration-token.guard.spec.ts similarity index 100% rename from src/app/external-log-in-complete/guards/registration-token.guard.spec.ts rename to src/app/external-log-in/guards/registration-token.guard.spec.ts diff --git a/src/app/external-log-in-complete/guards/registration-token.guard.ts b/src/app/external-log-in/guards/registration-token.guard.ts similarity index 91% rename from src/app/external-log-in-complete/guards/registration-token.guard.ts rename to src/app/external-log-in/guards/registration-token.guard.ts index 8f0db7b63f4..31e38143a3b 100644 --- a/src/app/external-log-in-complete/guards/registration-token.guard.ts +++ b/src/app/external-log-in/guards/registration-token.guard.ts @@ -1,11 +1,6 @@ import { Injectable } from '@angular/core'; -import { - ActivatedRouteSnapshot, - CanActivate, - Router, - RouterStateSnapshot, -} from '@angular/router'; -import { Observable, map, of } from 'rxjs'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, } from '@angular/router'; +import { map, Observable, of } from 'rxjs'; import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; import { RemoteData } from '../../core/data/remote-data'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; diff --git a/src/app/external-log-in-complete/models/registration-data.mock.model.ts b/src/app/external-log-in/models/registration-data.mock.model.ts similarity index 100% rename from src/app/external-log-in-complete/models/registration-data.mock.model.ts rename to src/app/external-log-in/models/registration-data.mock.model.ts diff --git a/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html b/src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.html similarity index 100% rename from src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.html rename to src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.html diff --git a/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.scss b/src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.scss similarity index 100% rename from src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.scss rename to src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.scss diff --git a/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts b/src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts similarity index 100% rename from src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts rename to src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.spec.ts diff --git a/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts b/src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.ts similarity index 97% rename from src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts rename to src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.ts index 4cba863bef3..52973e8267b 100644 --- a/src/app/external-log-in-complete/registration-types/orcid-confirmation/orcid-confirmation.component.ts +++ b/src/app/external-log-in/registration-types/orcid-confirmation/orcid-confirmation.component.ts @@ -1,9 +1,10 @@ -import { Component, OnInit, ChangeDetectionStrategy, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { AuthRegistrationType } from '../../../core/auth/models/auth.registration-type'; import { Registration } from '../../../core/shared/registration.model'; import { renderExternalLoginConfirmationFor } from '../../decorators/external-log-in.methods-decorator'; import { ExternalLoginMethodEntryComponent } from '../../decorators/external-login-method-entry.component'; + @Component({ selector: 'ds-orcid-confirmation', templateUrl: './orcid-confirmation.component.html', diff --git a/src/app/external-log-in-complete/resolvers/registration-data.resolver.spec.ts b/src/app/external-log-in/resolvers/registration-data.resolver.spec.ts similarity index 100% rename from src/app/external-log-in-complete/resolvers/registration-data.resolver.spec.ts rename to src/app/external-log-in/resolvers/registration-data.resolver.spec.ts diff --git a/src/app/external-log-in-complete/resolvers/registration-data.resolver.ts b/src/app/external-log-in/resolvers/registration-data.resolver.ts similarity index 93% rename from src/app/external-log-in-complete/resolvers/registration-data.resolver.ts rename to src/app/external-log-in/resolvers/registration-data.resolver.ts index 49667ab77a0..306d0d8f235 100644 --- a/src/app/external-log-in-complete/resolvers/registration-data.resolver.ts +++ b/src/app/external-log-in/resolvers/registration-data.resolver.ts @@ -1,9 +1,5 @@ import { Injectable } from '@angular/core'; -import { - Resolve, - RouterStateSnapshot, - ActivatedRouteSnapshot, -} from '@angular/router'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot, } from '@angular/router'; import { Observable } from 'rxjs'; import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; import { RemoteData } from '../../core/data/remote-data'; diff --git a/src/app/external-log-in-complete/services/external-login.service.spec.ts b/src/app/external-log-in/services/external-login.service.spec.ts similarity index 100% rename from src/app/external-log-in-complete/services/external-login.service.spec.ts rename to src/app/external-log-in/services/external-login.service.spec.ts diff --git a/src/app/external-log-in-complete/services/external-login.service.ts b/src/app/external-log-in/services/external-login.service.ts similarity index 96% rename from src/app/external-log-in-complete/services/external-login.service.ts rename to src/app/external-log-in/services/external-login.service.ts index 70ab98832ff..8f3fce41514 100644 --- a/src/app/external-log-in-complete/services/external-login.service.ts +++ b/src/app/external-log-in/services/external-login.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { Observable, filter, map } from 'rxjs'; +import { filter, map, Observable } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { AuthMethod } from '../../core/auth/models/auth.method'; import { getAuthenticationMethods } from '../../core/auth/selectors'; -import { Store, select } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import { CoreState } from '../../core/core-state.model'; import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; import { RemoteData } from '../../core/data/remote-data'; diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts index 270dc60125e..60e03b3c51c 100644 --- a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.component.spec.ts @@ -3,7 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; -import { ConfirmationSentComponent } from '../external-log-in-complete/email-confirmation/confirmation-sent/confirmation-sent.component'; +import { + ConfirmationSentComponent +} from '../external-log-in/email-confirmation/confirmation-sent/confirmation-sent.component'; describe('ExternalLoginEmailConfirmationPageComponent', () => { let component: ExternalLoginEmailConfirmationPageComponent; diff --git a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts index d62da55dbd7..fe91160627e 100644 --- a/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts +++ b/src/app/external-login-email-confirmation-page/external-login-email-confirmation-page.module.ts @@ -1,9 +1,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { ExternalLoginEmailConfirmationPageRoutingModule } from './external-login-email-confirmation-page-routing.module'; +import { + ExternalLoginEmailConfirmationPageRoutingModule +} from './external-login-email-confirmation-page-routing.module'; import { ExternalLoginEmailConfirmationPageComponent } from './external-login-email-confirmation-page.component'; -import { ExternalLoginCompleteModule } from '../external-log-in-complete/external-login-complete.module'; +import { ExternalLoginModule } from '../external-log-in/external-login.module'; @NgModule({ @@ -13,7 +15,7 @@ import { ExternalLoginCompleteModule } from '../external-log-in-complete/externa imports: [ CommonModule, ExternalLoginEmailConfirmationPageRoutingModule, - ExternalLoginCompleteModule, + ExternalLoginModule, ] }) export class ExternalLoginEmailConfirmationPageModule { } diff --git a/src/app/external-login-page/external-login-page-routing.module.ts b/src/app/external-login-page/external-login-page-routing.module.ts index 641809740d3..b248115ddbb 100644 --- a/src/app/external-login-page/external-login-page-routing.module.ts +++ b/src/app/external-login-page/external-login-page-routing.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; -import { RegistrationDataResolver } from '../external-log-in-complete/resolvers/registration-data.resolver'; -import { RegistrationTokenGuard } from '../external-log-in-complete/guards/registration-token.guard'; +import { RegistrationDataResolver } from '../external-log-in/resolvers/registration-data.resolver'; +import { RegistrationTokenGuard } from '../external-log-in/guards/registration-token.guard'; const routes: Routes = [ { diff --git a/src/app/external-login-page/external-login-page.module.ts b/src/app/external-login-page/external-login-page.module.ts index ac68dc5c309..a7808057629 100644 --- a/src/app/external-login-page/external-login-page.module.ts +++ b/src/app/external-login-page/external-login-page.module.ts @@ -5,7 +5,7 @@ import { ExternalLoginPageRoutingModule } from './external-login-page-routing.mo import { ExternalLoginPageComponent } from './external-login-page.component'; import { ThemedExternalLoginPageComponent } from './themed-external-login-page.component'; import { SharedModule } from '../shared/shared.module'; -import { ExternalLoginCompleteModule } from '../external-log-in-complete/external-login-complete.module'; +import { ExternalLoginModule } from '../external-log-in/external-login.module'; const COMPONENTS = [ ExternalLoginPageComponent, @@ -20,7 +20,7 @@ const COMPONENTS = [ CommonModule, ExternalLoginPageRoutingModule, SharedModule, - ExternalLoginCompleteModule + ExternalLoginModule ] }) export class ExternalLoginPageModule { } diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts b/src/app/external-login-review-account-info-page/external-login-review-account-info-page-routing.module.ts similarity index 85% rename from src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts rename to src/app/external-login-review-account-info-page/external-login-review-account-info-page-routing.module.ts index 66e3e29d056..afe0249f076 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page-routing.module.ts +++ b/src/app/external-login-review-account-info-page/external-login-review-account-info-page-routing.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; import { ReviewAccountGuard } from './helpers/review-account.guard'; -import { RegistrationDataResolver } from '../external-log-in-complete/resolvers/registration-data.resolver'; +import { RegistrationDataResolver } from '../external-log-in/resolvers/registration-data.resolver'; const routes: Routes = [ { diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.html b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.html similarity index 100% rename from src/app/external-login-review-account-info/external-login-review-account-info-page.component.html rename to src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.html diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.scss b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.scss similarity index 100% rename from src/app/external-login-review-account-info/external-login-review-account-info-page.component.scss rename to src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.scss diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.spec.ts similarity index 94% rename from src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts rename to src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.spec.ts index 816cdb4e1ff..7ef0e1cac3d 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.spec.ts +++ b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; import { of } from 'rxjs'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; -import { mockRegistrationDataModel } from '../external-log-in-complete/models/registration-data.mock.model'; +import { mockRegistrationDataModel } from '../external-log-in/models/registration-data.mock.model'; describe('ExternalLoginReviewAccountInfoPageComponent', () => { let component: ExternalLoginReviewAccountInfoPageComponent; diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.ts similarity index 96% rename from src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts rename to src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.ts index a42c9e05cfd..6217d4c79c2 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.component.ts +++ b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { AlertType } from '../shared/alert/aletr-type'; -import { Observable, first, map, tap } from 'rxjs'; +import { first, map, Observable, tap } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; import { hasNoValue } from '../shared/empty.util'; import { Registration } from '../core/shared/registration.model'; diff --git a/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.module.ts similarity index 78% rename from src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts rename to src/app/external-login-review-account-info-page/external-login-review-account-info-page.module.ts index 13bad328d1f..bcad6db4265 100644 --- a/src/app/external-login-review-account-info/external-login-review-account-info-page.module.ts +++ b/src/app/external-login-review-account-info-page/external-login-review-account-info-page.module.ts @@ -4,11 +4,13 @@ import { CommonModule } from '@angular/common'; import { ExternalLoginReviewAccountInfoRoutingModule } from './external-login-review-account-info-page-routing.module'; import { ExternalLoginReviewAccountInfoPageComponent } from './external-login-review-account-info-page.component'; import { CompareValuesPipe } from './helpers/compare-values.pipe'; -import { ThemedExternalLoginReviewAccountInfoPageComponent } from './themed-external-login-review-account-info-page.component'; +import { + ThemedExternalLoginReviewAccountInfoPageComponent +} from './themed-external-login-review-account-info-page.component'; import { ReviewAccountInfoComponent } from './review-account-info/review-account-info.component'; import { UiSwitchModule } from 'ngx-ui-switch'; import { SharedModule } from '../shared/shared.module'; -import { ExternalLoginCompleteModule } from '../external-log-in-complete/external-login-complete.module'; +import { ExternalLoginModule } from '../external-log-in/external-login.module'; @NgModule({ declarations: [ @@ -22,7 +24,7 @@ import { ExternalLoginCompleteModule } from '../external-log-in-complete/externa ExternalLoginReviewAccountInfoRoutingModule, SharedModule, UiSwitchModule, - ExternalLoginCompleteModule + ExternalLoginModule ] }) export class ExternalLoginReviewAccountInfoModule { } diff --git a/src/app/external-login-review-account-info/helpers/compare-values.pipe.ts b/src/app/external-login-review-account-info-page/helpers/compare-values.pipe.ts similarity index 100% rename from src/app/external-login-review-account-info/helpers/compare-values.pipe.ts rename to src/app/external-login-review-account-info-page/helpers/compare-values.pipe.ts diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts b/src/app/external-login-review-account-info-page/helpers/review-account.guard.spec.ts similarity index 100% rename from src/app/external-login-review-account-info/helpers/review-account.guard.spec.ts rename to src/app/external-login-review-account-info-page/helpers/review-account.guard.spec.ts diff --git a/src/app/external-login-review-account-info/helpers/review-account.guard.ts b/src/app/external-login-review-account-info-page/helpers/review-account.guard.ts similarity index 92% rename from src/app/external-login-review-account-info/helpers/review-account.guard.ts rename to src/app/external-login-review-account-info-page/helpers/review-account.guard.ts index d908483c152..7caf465550c 100644 --- a/src/app/external-login-review-account-info/helpers/review-account.guard.ts +++ b/src/app/external-login-review-account-info-page/helpers/review-account.guard.ts @@ -1,11 +1,6 @@ import { Injectable } from '@angular/core'; -import { - ActivatedRouteSnapshot, - CanActivate, - Router, - RouterStateSnapshot, -} from '@angular/router'; -import { Observable, catchError, mergeMap, of, tap } from 'rxjs'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, } from '@angular/router'; +import { catchError, mergeMap, Observable, of, tap } from 'rxjs'; import { AuthService } from '../../core/auth/auth.service'; import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; import { EpersonRegistrationService } from '../../core/data/eperson-registration.service'; diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.html b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.html similarity index 100% rename from src/app/external-login-review-account-info/review-account-info/review-account-info.component.html rename to src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.html diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.scss b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.scss similarity index 100% rename from src/app/external-login-review-account-info/review-account-info/review-account-info.component.scss rename to src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.scss diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.spec.ts similarity index 96% rename from src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts rename to src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.spec.ts index 2bfbd582e4a..9b4bd2b8242 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.spec.ts @@ -1,15 +1,11 @@ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { ReviewAccountInfoComponent } from './review-account-info.component'; -import { - TranslateLoader, - TranslateModule, - TranslateService, -} from '@ngx-translate/core'; +import { TranslateLoader, TranslateModule, TranslateService, } from '@ngx-translate/core'; import { CommonModule } from '@angular/common'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { Observable, Subscription, of } from 'rxjs'; +import { Observable, of, Subscription } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; @@ -25,7 +21,7 @@ import { Registration } from '../../core/shared/registration.model'; import { AuthService } from '../../core/auth/auth.service'; import { AuthServiceMock } from '../../shared/mocks/auth.service.mock'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; -import { ExternalLoginService } from '../../external-log-in-complete/services/external-login.service'; +import { ExternalLoginService } from '../../external-log-in/services/external-login.service'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; diff --git a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.ts similarity index 97% rename from src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts rename to src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.ts index 79f9cac83c5..aee858c339c 100644 --- a/src/app/external-login-review-account-info/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.ts @@ -1,13 +1,7 @@ -import { - Component, - ChangeDetectionStrategy, - OnInit, - Input, - OnDestroy, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, } from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { Observable, Subscription, combineLatest, filter, from, map, switchMap, take, tap } from 'rxjs'; +import { combineLatest, filter, from, map, Observable, Subscription, switchMap, take, tap } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { ConfirmationModalComponent } from '../../shared/confirmation-modal/confirmation-modal.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -19,7 +13,7 @@ import { Registration } from '../../core/shared/registration.model'; import { AuthService } from '../../core/auth/auth.service'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; -import { ExternalLoginService } from '../../external-log-in-complete/services/external-login.service'; +import { ExternalLoginService } from '../../external-log-in/services/external-login.service'; export interface ReviewAccountInfoData { label: string; diff --git a/src/app/external-login-review-account-info/themed-external-login-review-account-info-page.component.ts b/src/app/external-login-review-account-info-page/themed-external-login-review-account-info-page.component.ts similarity index 100% rename from src/app/external-login-review-account-info/themed-external-login-review-account-info-page.component.ts rename to src/app/external-login-review-account-info-page/themed-external-login-review-account-info-page.component.ts From bf7f6eaebcde8e7aac8e35abfd113e745db4c44c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 10 Oct 2023 13:44:13 +0200 Subject: [PATCH 183/195] [CST-10703] change getExternalServerRedirectUrl in order to have origin as param --- src/app/core/auth/auth.service.ts | 5 +++-- .../confirm-email/confirm-email.component.spec.ts | 5 ++++- .../confirm-email/confirm-email.component.ts | 10 ++++++++-- .../review-account-info.component.spec.ts | 3 +++ .../review-account-info.component.ts | 10 ++++++++-- .../log-in-external-provider.component.ts | 6 +++++- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index 4e089fc6347..128b03bd4d0 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -526,12 +526,13 @@ export class AuthService { /** * Returns the external server redirect URL. + * @param origin - The origin route. * @param redirectRoute - The redirect route. * @param location - The location. * @returns The external server redirect URL. */ - getExternalServerRedirectUrl(redirectRoute: string, location: string): string { - const correctRedirectUrl = new URLCombiner(this._window.nativeWindow.origin, redirectRoute).toString(); + getExternalServerRedirectUrl(origin: string, redirectRoute: string, location: string): string { + const correctRedirectUrl = new URLCombiner(origin, redirectRoute).toString(); let externalServerUrl = location; const myRegexp = /\?redirectUrl=(.*)/g; diff --git a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.spec.ts b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.spec.ts index 7c0e7f8d926..1c9a876b027 100644 --- a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.spec.ts +++ b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.spec.ts @@ -16,6 +16,8 @@ import { Registration } from '../../../core/shared/registration.model'; import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { NativeWindowService } from '../../../core/services/window.service'; +import { MockWindow, NativeWindowMockFactory } from '../../../shared/mocks/mock-native-window-ref'; describe('ConfirmEmailComponent', () => { let component: ConfirmEmailComponent; @@ -52,6 +54,7 @@ describe('ConfirmEmailComponent', () => { declarations: [ConfirmEmailComponent], providers: [ FormBuilder, + { provide: NativeWindowService, useFactory: NativeWindowMockFactory }, { provide: ExternalLoginService, useValue: externalLoginServiceSpy }, { provide: EPersonDataService, useValue: epersonDataServiceSpy }, { provide: NotificationsService, useValue: notificationServiceSpy }, @@ -138,7 +141,7 @@ describe('ConfirmEmailComponent', () => { expect(externalLoginServiceSpy.getExternalAuthLocation).toHaveBeenCalledWith(AuthMethodType.Orcid); expect(authServiceSpy.getRedirectUrl).toHaveBeenCalled(); expect(authServiceSpy.setRedirectUrl).toHaveBeenCalledWith('/profile'); - expect(authServiceSpy.getExternalServerRedirectUrl).toHaveBeenCalledWith('/test-redirect', 'test-location'); + expect(authServiceSpy.getExternalServerRedirectUrl).toHaveBeenCalledWith(MockWindow.origin,'/test-redirect', 'test-location'); expect(hardRedirectService.redirect).toHaveBeenCalledWith('test-external-url'); }); }); diff --git a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts index 56e28333b62..eaecf71cb2d 100644 --- a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; import { TranslateService } from '@ngx-translate/core'; @@ -12,6 +12,7 @@ import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core import { Registration } from '../../../core/shared/registration.model'; import { hasNoValue, hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service'; @Component({ selector: 'ds-confirm-email', @@ -41,6 +42,7 @@ export class ConfirmEmailComponent implements OnDestroy { externalLocation: string; constructor( + @Inject(NativeWindowService) protected _window: NativeWindowRef, private formBuilder: FormBuilder, private externalLoginService: ExternalLoginService, private epersonDataService: EPersonDataService, @@ -137,7 +139,11 @@ export class ConfirmEmailComponent implements OnDestroy { } else if (rd.hasSucceeded) { // set Redirect URL to User profile, so the user is redirected to the profile page after logging in this.authService.setRedirectUrl('/profile'); - const externalServerUrl = this.authService.getExternalServerRedirectUrl(redirectRoute, location); + const externalServerUrl = this.authService.getExternalServerRedirectUrl( + this._window.nativeWindow.origin, + redirectRoute, + location + ); // redirect to external registration type authentication url this.hardRedirectService.redirect(externalServerUrl); } diff --git a/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.spec.ts b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.spec.ts index 9b4bd2b8242..63559ef904c 100644 --- a/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.spec.ts +++ b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.spec.ts @@ -22,6 +22,8 @@ import { AuthService } from '../../core/auth/auth.service'; import { AuthServiceMock } from '../../shared/mocks/auth.service.mock'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; import { ExternalLoginService } from '../../external-log-in/services/external-login.service'; +import { NativeWindowService } from '../../core/services/window.service'; +import { NativeWindowMockFactory } from '../../shared/mocks/mock-native-window-ref'; describe('ReviewAccountInfoComponent', () => { let component: ReviewAccountInfoComponent; @@ -82,6 +84,7 @@ describe('ReviewAccountInfoComponent', () => { await TestBed.configureTestingModule({ declarations: [ReviewAccountInfoComponent, CompareValuesPipe], providers: [ + { provide: NativeWindowService, useFactory: NativeWindowMockFactory }, { provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: NgbModal, useValue: modalStub }, { diff --git a/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.ts b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.ts index aee858c339c..f388cffb91d 100644 --- a/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.ts +++ b/src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy, OnInit, } from '@angular/core'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { combineLatest, filter, from, map, Observable, Subscription, switchMap, take, tap } from 'rxjs'; @@ -14,6 +14,7 @@ import { AuthService } from '../../core/auth/auth.service'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; import { AuthRegistrationType } from '../../core/auth/models/auth.registration-type'; import { ExternalLoginService } from '../../external-log-in/services/external-login.service'; +import { NativeWindowRef, NativeWindowService } from '../../core/services/window.service'; export interface ReviewAccountInfoData { label: string; @@ -53,6 +54,7 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { subs: Subscription[] = []; constructor( + @Inject(NativeWindowService) protected _window: NativeWindowRef, private ePersonService: EPersonDataService, private modalService: NgbModal, private notificationService: NotificationsService, @@ -211,7 +213,11 @@ export class ReviewAccountInfoComponent implements OnInit, OnDestroy { ); // set Redirect URL to User profile, so the user is redirected to the profile page after logging in this.authService.setRedirectUrl('/profile'); - const externalServerUrl = this.authService.getExternalServerRedirectUrl(redirectRoute, location); + const externalServerUrl = this.authService.getExternalServerRedirectUrl( + this._window.nativeWindow.origin, + redirectRoute, + location + ); // redirect to external registration type authentication url this.hardRedirectService.redirect(externalServerUrl); } else if (response.hasFailed) { diff --git a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts index 32b551e05cd..e52cfcc40c6 100644 --- a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts +++ b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts @@ -91,7 +91,11 @@ export class LogInExternalProviderComponent implements OnInit { } else if (isEmpty(redirectRoute)) { redirectRoute = '/'; } - const externalServerUrl = this.authService.getExternalServerRedirectUrl(redirectRoute, this.location); + const externalServerUrl = this.authService.getExternalServerRedirectUrl( + this._window.nativeWindow.origin, + redirectRoute, + this.location + ); // redirect to shibboleth/orcid/(external) authentication url this.hardRedirectService.redirect(externalServerUrl); }); From 327d8b5a66790dcc8154f0e278454f999e321322 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 11 Oct 2023 16:28:18 +0200 Subject: [PATCH 184/195] [DSC-337] Fix for merge and refactoring --- src/app/core/layout/tab-data.service.spec.ts | 507 +++++++++++++++++- src/app/core/layout/tab-data.service.ts | 61 ++- .../item-page/cris-item-page-tab.resolver.ts | 1 + 3 files changed, 537 insertions(+), 32 deletions(-) diff --git a/src/app/core/layout/tab-data.service.spec.ts b/src/app/core/layout/tab-data.service.spec.ts index 256bff1618f..b4620f18102 100644 --- a/src/app/core/layout/tab-data.service.spec.ts +++ b/src/app/core/layout/tab-data.service.spec.ts @@ -16,7 +16,8 @@ import { of } from 'rxjs'; import { FindListOptions } from '../data/find-list-options.model'; import { RequestParam } from '../cache/models/request-param.model'; import { createPaginatedList } from '../../shared/testing/utils.test'; -import { bothTabs } from '../../shared/testing/layout-tab.mocks'; +import objectContaining = jasmine.objectContaining; +import arrayContaining = jasmine.arrayContaining; describe('TabDataService', () => { let scheduler: TestScheduler; @@ -75,6 +76,484 @@ describe('TabDataService', () => { } }; + const tabWithOnlyMinors: CrisLayoutTab = { + type: TAB, + id: 4, + shortname: 'person-bibliometrics', + header: 'person-bibliometrics-header', + entityType: 'Person', + priority: 0, + security: 0, + rows: [ + { + style: '', + cells: [ + { + style: '', + boxes: [ + { + id: 3418, + shortname: 'heading', + header: null, + entityType: 'Person', + collapsed: false, + minor: true, + style: null, + security: 0, + boxType: 'METADATA', + maxColumn: null, + clear: false, + configuration: { + id: '1', + type: 'boxmetadataconfiguration', + rows: [ + { + style: '', + cells: [ + { + style: '', + fields: [ + { + metadata: 'dc.title', + label: null, + rendering: 'heading', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + } + ] + } + ] + } + ] + }, + metadataSecurityFields: [], + container: false + } + ] + } + ] + }, + { + style: '', + cells: [ + { + style: '', + boxes: [ + { + id: 3419, + shortname: 'namecard', + header: 'Name Card', + entityType: 'Person', + collapsed: false, + minor: true, + style: null, + security: 0, + boxType: 'METADATA', + maxColumn: null, + clear: false, + configuration: { + id: '0', + type: 'boxmetadataconfiguration', + rows: [ + { + style: '', + cells: [ + { + style: 'col-3', + fields: [ + { + bitstream: { + bundle: 'ORIGINAL', + metadataField: 'dc.type', + metadataValue: 'personal pictur' + }, + label: null, + rendering: 'thumbnail', + fieldType: 'BITSTREAM', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + } + ] + }, + { + style: 'px-2', + fields: [ + { + metadata: 'dc.title', + label: 'Preferred name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.name', + label: 'Official Name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.name.translated', + label: 'Translated Name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.name.variant', + label: 'Alternative Name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.affiliation.name', + label: 'Main Affiliation', + rendering: 'crisref', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.workgroup', + label: null, + rendering: 'crisref', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'oairecerif.identifier.url', + label: 'Web Site', + rendering: 'link', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.email', + label: 'Email', + rendering: 'crisref.email', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.identifier.orcid', + label: 'ORCID', + rendering: 'orcid', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.identifier.scopus-author-id', + label: 'Scopus Author ID', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.identifier.rid', + label: 'Researcher ID', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + } + ] + } + ] + } + ] + }, + metadataSecurityFields: [], + container: false + } + ] + } + ] + } + ], + uuid: 'person-bibliometrics-4', + _links: { + self: { + href: 'https://rest.api/rest/api/tabs/3' + } + } + }; + + const tabWithSomeMinors: CrisLayoutTab = { + type: TAB, + id: 5, + shortname: 'person-bibliometrics', + header: 'person-bibliometrics-header', + entityType: 'Person', + priority: 0, + security: 0, + rows: [ + { + style: '', + cells: [ + { + style: '', + boxes: [ + { + id: 3418, + shortname: 'heading', + header: null, + entityType: 'Person', + collapsed: false, + minor: false, + style: null, + security: 0, + boxType: 'METADATA', + maxColumn: null, + clear: false, + configuration: { + id: '3', + type: 'boxmetadataconfiguration', + rows: [ + { + style: '', + cells: [ + { + style: '', + fields: [ + { + metadata: 'dc.title', + label: null, + rendering: 'heading', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + } + ] + } + ] + } + ] + }, + metadataSecurityFields: [], + container: false + } + ] + } + ] + }, + { + style: '', + cells: [ + { + style: '', + boxes: [ + { + id: 3419, + shortname: 'namecard', + header: 'Name Card', + entityType: 'Person', + collapsed: false, + minor: true, + style: null, + security: 0, + boxType: 'METADATA', + maxColumn: null, + clear: false, + configuration: { + id: '0', + type: 'boxmetadataconfiguration', + rows: [ + { + style: '', + cells: [ + { + style: 'col-3', + fields: [ + { + bitstream: { + bundle: 'ORIGINAL', + metadataField: 'dc.type', + metadataValue: 'personal pictur' + }, + label: null, + rendering: 'thumbnail', + fieldType: 'BITSTREAM', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + } + ] + }, + { + style: 'px-2', + fields: [ + { + metadata: 'dc.title', + label: 'Preferred name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.name', + label: 'Official Name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.name.translated', + label: 'Translated Name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.name.variant', + label: 'Alternative Name', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.affiliation.name', + label: 'Main Affiliation', + rendering: 'crisref', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'crisrp.workgroup', + label: null, + rendering: 'crisref', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'oairecerif.identifier.url', + label: 'Web Site', + rendering: 'link', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.email', + label: 'Email', + rendering: 'crisref.email', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.identifier.orcid', + label: 'ORCID', + rendering: 'orcid', + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.identifier.scopus-author-id', + label: 'Scopus Author ID', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + }, + { + metadata: 'person.identifier.rid', + label: 'Researcher ID', + rendering: null, + fieldType: 'METADATA', + styleLabel: 'font-weight-bold col-3', + styleValue: null, + labelAsHeading: false, + valuesInline: false + } + ] + } + ] + } + ] + }, + metadataSecurityFields: [], + container: false + } + ] + } + ] + } + ], + uuid: 'person-bibliometrics-5', + _links: { + self: { + href: 'https://rest.api/rest/api/tabs/3' + } + } + }; + const endpointURL = `https://rest.api/rest/api/tabs`; const requestURL = `https://rest.api/rest/api/tabs/${tabPersonProfile.id}`; const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; @@ -82,10 +561,13 @@ describe('TabDataService', () => { const entityType = 'Person'; const tabId = '1'; - const array = [tabPersonProfile, tabPersonBiography, tabPersonBibliometrics]; + const array = [tabPersonProfile, tabPersonBiography, tabPersonBibliometrics, tabWithOnlyMinors, tabWithSomeMinors]; const paginatedList = createPaginatedList(array); const tabRD = createSuccessfulRemoteDataObject(tabPersonProfile); const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList); + const noMinorsList = + createPaginatedList([tabPersonProfile, tabPersonBiography, tabPersonBibliometrics, tabWithSomeMinors]); + const paginatedListWithoutMinorsRD = createSuccessfulRemoteDataObject(noMinorsList); beforeEach(() => { scheduler = getTestScheduler(); @@ -168,6 +650,19 @@ describe('TabDataService', () => { expect(result).toBeObservable(expected); }); + it('should remove tab with minor cells', () => { + const result = service.findByItem(itemUUID, true, true); + result.subscribe(tabs => { + expect(tabs.payload.page).toHaveSize(4); + expect(tabs.payload.page).not.toEqual( + arrayContaining([objectContaining({ id: tabWithOnlyMinors.id })]) + ); + expect(tabs.payload.page).toEqual( + arrayContaining([objectContaining({ id: tabWithSomeMinors.id })]) + ); + }); + }); + }); describe('searchByEntityType', () => { @@ -191,12 +686,4 @@ describe('TabDataService', () => { }); }); - - - fdescribe('filterTab', () => { - it('should return non minor element', () => { - const tabs: CrisLayoutTab[] = service.filterTab(bothTabs); - expect(tabs.length).toBe(2); - }); - }); }); diff --git a/src/app/core/layout/tab-data.service.ts b/src/app/core/layout/tab-data.service.ts index 2a9c1663539..2e9437742fe 100644 --- a/src/app/core/layout/tab-data.service.ts +++ b/src/app/core/layout/tab-data.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { CrisLayoutTab } from './models/tab.model'; +import { CrisLayoutCell, CrisLayoutRow, CrisLayoutTab } from './models/tab.model'; import { RequestService } from '../data/request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; @@ -12,11 +12,13 @@ import { TAB } from './models/tab.resource-type'; import { dataService } from '../data/base/data-service.decorator'; import { RemoteData } from '../data/remote-data'; import { PaginatedList } from '../data/paginated-list.model'; -import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FindListOptions } from '../data/find-list-options.model'; import { RequestParam } from '../cache/models/request-param.model'; import { IdentifiableDataService } from '../data/base/identifiable-data.service'; import { SearchDataImpl } from '../data/base/search-data'; +import { map } from 'rxjs/operators'; +import { hasNoValue, hasValue } from '../../shared/empty.util'; +import { CrisLayoutBox } from './models/box.model'; /** * A service responsible for fetching data from the REST API on the tabs endpoint @@ -54,43 +56,58 @@ export class TabDataService extends IdentifiableDataService { * available data. Empty tabs are filter out. * @param itemUuid UUID of the Item * @param useCachedVersionIfAvailable - * @param linkToFollow */ - findByItem(itemUuid: string, useCachedVersionIfAvailable, excludeMinors?: boolean ,linkToFollow?: FollowLinkConfig): Observable>> { + findByItem( + itemUuid: string, useCachedVersionIfAvailable: boolean, excludeMinors?: boolean + ): Observable>> { const options = new FindListOptions(); options.searchParams = [new RequestParam('uuid', itemUuid)]; - return this.searchData.searchBy(this.searchFindByItem, options, useCachedVersionIfAvailable).pipe(map((data) => { - if (!!data.payload && !!data.payload.page && excludeMinors) { - data.payload.page = this.filterTab(data.payload.page); - } - return data; - })); + return this.searchData.searchBy(this.searchFindByItem, options, useCachedVersionIfAvailable) + .pipe( + map((data) => { + if (hasValue(data?.payload?.page) && excludeMinors) { + data.payload.page = this.filterTabWithOnlyMinor(data.payload.page); + } + return data; + })); } /** * @param tabs * @returns Tabs which contains non minor element */ - filterTab(tabs: CrisLayoutTab[]): CrisLayoutTab[] { - return tabs.filter(tab => this.checkForMinor(tab)); + filterTabWithOnlyMinor(tabs: CrisLayoutTab[]): CrisLayoutTab[] { + return tabs.filter(tab => !this.hasTabOnlyMinor(tab)); } /** * @param tab Contains a tab data which has rows, cells and boxes * @returns Boolean based on cells has minor or not */ - checkForMinor(tab: CrisLayoutTab): boolean { - for (const row of tab.rows) { - for (const cell of row.cells) { - for (const box of cell.boxes) { - if (box.minor) { - return false; - } - } - } + hasTabOnlyMinor(tab: CrisLayoutTab): boolean { + if (hasNoValue(tab?.rows)) { + return false; } - return true; + return tab.rows.every(row => this.hasRowOnlyMinor(row)); + } + + hasRowOnlyMinor(row: CrisLayoutRow): boolean { + if (hasNoValue(row?.cells)) { + return false; + } + return row.cells.every(cell => this.hasCellOnlyMinor(cell)); + } + + hasCellOnlyMinor(cell: CrisLayoutCell): boolean { + if (hasNoValue(cell?.boxes)) { + return false; + } + return cell.boxes.every(box => this.isMinor(box)); + } + + isMinor(box: CrisLayoutBox): boolean { + return box.minor === true; } /** diff --git a/src/app/item-page/cris-item-page-tab.resolver.ts b/src/app/item-page/cris-item-page-tab.resolver.ts index 5efc7d311f3..e601c75fbdc 100644 --- a/src/app/item-page/cris-item-page-tab.resolver.ts +++ b/src/app/item-page/cris-item-page-tab.resolver.ts @@ -37,6 +37,7 @@ export class CrisItemPageTabResolver implements Resolve Date: Wed, 11 Oct 2023 17:37:38 +0200 Subject: [PATCH 185/195] [DSC-1251] Refactoring --- .../file-download-link/file-download-link.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/shared/file-download-link/file-download-link.component.ts b/src/app/shared/file-download-link/file-download-link.component.ts index d2f1d215c0d..025cb2ce537 100644 --- a/src/app/shared/file-download-link/file-download-link.component.ts +++ b/src/app/shared/file-download-link/file-download-link.component.ts @@ -4,8 +4,8 @@ import { getBitstreamDownloadRoute, getBitstreamRequestACopyRoute } from '../../ import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { hasValue, isNotEmpty } from '../empty.util'; -import { map } from 'rxjs/operators'; -import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { combineLatest as observableCombineLatest, Observable, of as observableOf, shareReplay } from 'rxjs'; import { Item } from '../../core/shared/item.model'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { getFirstCompletedRemoteData, getRemoteDataPayload } from 'src/app/core/shared/operators'; @@ -79,6 +79,8 @@ export class FileDownloadLinkComponent implements OnInit { // in case requestItemType empty/commented out(undefined) - request-copy not allowed hasValue(requestItemType) && requestItemType.values.length > 0 ), + catchError(() => observableOf(false)), + shareReplay(1) ); } else { this.bitstreamPath$ = observableOf(this.getBitstreamDownloadPath()); From f3072b89916df988c6eeffa99826bbbf06ec29bd Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 11 Oct 2023 19:11:50 +0200 Subject: [PATCH 186/195] [CST-12212] Prefill email with the one received from registration data --- .../confirm-email/confirm-email.component.html | 1 + .../confirm-email/confirm-email.component.spec.ts | 11 ++++++++++- .../confirm-email/confirm-email.component.ts | 10 ++++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html index 455aaf75e73..69b65341549 100644 --- a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html +++ b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html @@ -10,6 +10,7 @@

formControlName="email" placeholder="profile.email@example.com" class="form-control form-control-lg position-relative" + data-test="emailInput" />
{ it('should call postCreateAccountFromToken if email is confirmed', () => { component.emailForm.setValue({ email: 'test@example.com' }); diff --git a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts index eaecf71cb2d..e996babddce 100644 --- a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts +++ b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ExternalLoginService } from '../../services/external-login.service'; import { TranslateService } from '@ngx-translate/core'; @@ -20,7 +20,7 @@ import { NativeWindowRef, NativeWindowService } from '../../../core/services/win styleUrls: ['./confirm-email.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class ConfirmEmailComponent implements OnDestroy { +export class ConfirmEmailComponent implements OnInit, OnDestroy { /** * The form containing the email input */ @@ -51,12 +51,14 @@ export class ConfirmEmailComponent implements OnDestroy { private authService: AuthService, private hardRedirectService: HardRedirectService, ) { + } + + ngOnInit() { this.emailForm = this.formBuilder.group({ - email: ['', [Validators.required, Validators.email]] + email: [this.registrationData.email, [Validators.required, Validators.email]] }); } - /** * Submits the email form and performs appropriate actions based on the form's validity and user input. * If the form is valid and the confirmed email matches the registration email, calls the postCreateAccountFromToken method with the token and registration data. From c012d4a5195638d5c822387ce7cc779c2b698c1f Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 11 Oct 2023 19:17:32 +0200 Subject: [PATCH 187/195] [CST-12212] Add input placeholder label --- .../confirm-email/confirm-email.component.html | 2 +- .../provide-email/provide-email.component.html | 2 +- .../external-log-in/external-log-in.component.html | 2 +- src/assets/i18n/en.json5 | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html index 69b65341549..43848a2d3e5 100644 --- a/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html +++ b/src/app/external-log-in/email-confirmation/confirm-email/confirm-email.component.html @@ -8,7 +8,7 @@

type="email" id="email" formControlName="email" - placeholder="profile.email@example.com" + placeholder="{{'external-login.form.email' | translate}}" class="form-control form-control-lg position-relative" data-test="emailInput" /> diff --git a/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.html b/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.html index 46e804e1c2e..ab95d3986eb 100644 --- a/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.html +++ b/src/app/external-log-in/email-confirmation/provide-email/provide-email.component.html @@ -8,7 +8,7 @@

type="email" id="email" formControlName="email" - placeholder="Input box" + placeholder="{{'external-login.form.email' | translate}}" class="form-control form-control-lg position-relative" /> diff --git a/src/app/external-log-in/external-log-in/external-log-in.component.html b/src/app/external-log-in/external-log-in/external-log-in.component.html index aefa9719669..5fdb56f626c 100644 --- a/src/app/external-log-in/external-log-in/external-log-in.component.html +++ b/src/app/external-log-in/external-log-in/external-log-in.component.html @@ -18,7 +18,7 @@

{{ 'external-login.confirmation.header' | translate}}

-

or

+

{{'external-login.form.or-divider' | translate}}

diff --git a/src/app/breadcrumbs/breadcrumbs.component.spec.ts b/src/app/breadcrumbs/breadcrumbs.component.spec.ts index 7c80c87c3af..ae07babdfc0 100644 --- a/src/app/breadcrumbs/breadcrumbs.component.spec.ts +++ b/src/app/breadcrumbs/breadcrumbs.component.spec.ts @@ -10,8 +10,9 @@ import { TranslateLoaderMock } from '../shared/testing/translate-loader.mock'; import { RouterTestingModule } from '@angular/router/testing'; import { of as observableOf } from 'rxjs'; import { DebugElement } from '@angular/core'; -import { IsTextTruncatedPipe } from './breadcrumb/is-text-truncated.pipe'; +import { BreadcrumbTooltipPipe } from './breadcrumb/breadcrumb-tooltip.pipe'; import { TruncateBreadcrumbItemCharactersPipe } from './breadcrumb/truncate-breadcrumb-item-characters.pipe'; +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; describe('BreadcrumbsComponent', () => { let component: BreadcrumbsComponent; @@ -42,6 +43,7 @@ describe('BreadcrumbsComponent', () => { // NOTE: a root breadcrumb is automatically rendered new Breadcrumb('bc 1', 'example.com'), new Breadcrumb('bc 2', 'another.com'), + new Breadcrumb('breadcrumb to be truncated', 'truncated.com'), ]), showBreadcrumbs$: observableOf(true), } as BreadcrumbsService; @@ -50,10 +52,11 @@ describe('BreadcrumbsComponent', () => { declarations: [ BreadcrumbsComponent, VarDirective, - IsTextTruncatedPipe, + BreadcrumbTooltipPipe, TruncateBreadcrumbItemCharactersPipe, ], imports: [ + NgbTooltipModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot({ loader: { @@ -78,12 +81,35 @@ describe('BreadcrumbsComponent', () => { expect(component).toBeTruthy(); }); - it('should render the breadcrumbs', () => { + it('should render the breadcrumbs accordingly', () => { const breadcrumbs = fixture.debugElement.queryAll(By.css('.breadcrumb-item')); - expect(breadcrumbs.length).toBe(3); + expect(breadcrumbs.length).toBe(4); expectBreadcrumb(breadcrumbs[0], 'home.breadcrumbs', '/'); expectBreadcrumb(breadcrumbs[1], 'bc 1', '/example.com'); expectBreadcrumb(breadcrumbs[2].query(By.css('.text-truncate')), 'bc 2', null); + expectBreadcrumb(breadcrumbs[3].query(By.css('.text-truncate')), 'breadcrumb...', null); + }); + + it('should show tooltip only for truncated text', () => { + const breadcrumbs = fixture.debugElement.queryAll(By.css('.breadcrumb-item .text-truncate')); + expect(breadcrumbs.length).toBe(4); + + const truncatable = breadcrumbs[3]; + truncatable.triggerEventHandler('mouseenter', null); + fixture.detectChanges(); + let tooltip = truncatable.parent.query(By.css('div.tooltip-inner')); + expect(tooltip).not.toBeNull(); + expect(tooltip.nativeElement.innerText).toBe('breadcrumb to be truncated'); + truncatable.triggerEventHandler('mouseleave', null); + fixture.detectChanges(); + + const notTruncatable = breadcrumbs[2]; + notTruncatable.triggerEventHandler('mouseenter', null); + fixture.detectChanges(); + const tooltip2 = notTruncatable.parent.query(By.css('div.tooltip-inner')); + expect(tooltip2).toBeNull(); + notTruncatable.triggerEventHandler('mouseleave', null); + fixture.detectChanges(); }); }); diff --git a/src/app/root.module.ts b/src/app/root.module.ts index 84841ae7068..a4edec2a914 100644 --- a/src/app/root.module.ts +++ b/src/app/root.module.ts @@ -42,8 +42,10 @@ import { import { FooterModule } from './footer/footer.module'; import { SocialModule } from './social/social.module'; import { ExploreModule } from './shared/explore/explore.module'; -import { IsTextTruncatedPipe } from './breadcrumbs/breadcrumb/is-text-truncated.pipe'; -import { TruncateBreadcrumbItemCharactersPipe } from './breadcrumbs/breadcrumb/truncate-breadcrumb-item-characters.pipe'; +import { BreadcrumbTooltipPipe } from './breadcrumbs/breadcrumb/breadcrumb-tooltip.pipe'; +import { + TruncateBreadcrumbItemCharactersPipe +} from './breadcrumbs/breadcrumb/truncate-breadcrumb-item-characters.pipe'; const IMPORTS = [ CommonModule, @@ -86,7 +88,7 @@ const DECLARATIONS = [ PageErrorComponent, ContextHelpToggleComponent, TruncateBreadcrumbItemCharactersPipe, - IsTextTruncatedPipe + BreadcrumbTooltipPipe ]; const EXPORTS = [ From 62e150db555ade4073a6bcc519345e9cff579303 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 12 Oct 2023 16:08:35 +0200 Subject: [PATCH 191/195] [DSC-106] fixes import lint error --- .../models/date-picker/date-picker.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts index e34989f9765..29e62fe266a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts @@ -8,7 +8,7 @@ import { DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { DOCUMENT } from '@angular/common'; -import { isEqual } from 'lodash'; +import isEqual from 'lodash/isEqual'; export const DS_DATE_PICKER_SEPARATOR = '-'; From 6ceb4e196924817b8d95304984731ef897b958b0 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 12 Oct 2023 16:26:41 +0200 Subject: [PATCH 192/195] [DSC-1058] Add margin to labels --- src/app/shared/search/search.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index d85a8d40dbd..39a2aa1e42e 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -116,7 +116,7 @@ -
+
From 852c5bf85dffdbc4cedd052c836202617001fde4 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 12 Oct 2023 16:39:08 +0200 Subject: [PATCH 193/195] [DSC-106] Refactoring to handle shift + tab --- .../date-picker/date-picker.component.ts | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts index 29e62fe266a..2fafdc0ae3d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts @@ -11,6 +11,8 @@ import { DOCUMENT } from '@angular/common'; import isEqual from 'lodash/isEqual'; +export type DatePickerFieldType = '_year' | '_month' | '_day'; + export const DS_DATE_PICKER_SEPARATOR = '-'; @Component({ @@ -52,6 +54,9 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement disabledMonth = true; disabledDay = true; + + private readonly fields: DatePickerFieldType[] = ['_year', '_month', '_day']; + constructor(protected layoutService: DynamicFormLayoutService, protected validationService: DynamicFormValidationService, private renderer: Renderer2, @@ -172,31 +177,52 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement * Listen to keydown Tab event. * Get the active element and blur it, in order to focus the next input field. */ - @HostListener('keydown', ['$event']) - onKeyDown(event: KeyboardEvent) { - if (event.key === 'Tab') { - event.preventDefault(); - const activeElement: Element = this._document.activeElement; - (activeElement as any).blur(); - if (isEqual(activeElement.id, this.model.id.concat('_year')) ) { - this.focusInput('_month'); - } else if (isEqual(activeElement.id, this.model.id.concat('_month'))) { - this.focusInput('_day'); - } + @HostListener('keydown.tab', ['$event']) + onTabKeydown(event: KeyboardEvent) { + event.preventDefault(); + const activeElement: Element = this._document.activeElement; + (activeElement as any).blur(); + const index = this.selectedFieldIndex(activeElement); + if (index < 0) { + return; + } + let fieldToFocusOn = index + 1; + if (fieldToFocusOn < this.fields.length) { + this.focusInput(this.fields[fieldToFocusOn]); } } + @HostListener('keydown.shift.tab', ['$event']) + onShiftTabKeyDown(event: KeyboardEvent) { + event.preventDefault(); + const activeElement: Element = this._document.activeElement; + (activeElement as any).blur(); + const index = this.selectedFieldIndex(activeElement); + let fieldToFocusOn = index - 1; + if (fieldToFocusOn >= 0) { + this.focusInput(this.fields[fieldToFocusOn]); + } + } + + private selectedFieldIndex(activeElement: Element): number { + return this.fields.findIndex(field => isEqual(activeElement.id, this.model.id.concat(field))); + } + /** * Focus the input field for the given type * based on the model id. * Used to focus the next input field * in case of a disabled field. - * @param type '_month' | '_day' + * @param type DatePickerFieldType */ - focusInput(type: '_month' | '_day') { + focusInput(type: DatePickerFieldType) { const field = this._document.getElementById(this.model.id.concat(type)); if (field) { + if (hasValue(this.year) && isEqual(type, '_year')) { + this.disabledMonth = true; + this.disabledDay = true; + } if (hasValue(this.year) && isEqual(type, '_month')) { this.disabledMonth = false; } else if (hasValue(this.month) && isEqual(type, '_day')) { From aa4e4bb5d6fb37c02f2bda94bd2cc350d89e110d Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 12 Oct 2023 16:55:50 +0200 Subject: [PATCH 194/195] [DSC-106] Fixed tests --- .../models/date-picker/date-picker.component.spec.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts index 4989dab93a7..094fa582753 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts @@ -1,5 +1,5 @@ // Load the implementations that should be tested -import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, Renderer2 } from '@angular/core'; import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/testing'; import { FormControl, FormGroup } from '@angular/forms'; @@ -39,6 +39,11 @@ describe('DsDatePickerComponent test suite', () => { let dateFixture: ComponentFixture; let html; + const renderer2: Renderer2 = { + selectRootElement: jasmine.createSpy('selectRootElement'), + querySelector: jasmine.createSpy('querySelector'), + } as unknown as Renderer2; + // waitForAsync beforeEach beforeEach(waitForAsync(() => { @@ -54,7 +59,8 @@ describe('DsDatePickerComponent test suite', () => { ChangeDetectorRef, DsDatePickerComponent, { provide: DynamicFormLayoutService, useValue: mockDynamicFormLayoutService }, - { provide: DynamicFormValidationService, useValue: mockDynamicFormValidationService } + { provide: DynamicFormValidationService, useValue: mockDynamicFormValidationService }, + { provide: Renderer2, useValue: renderer2 }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }); From c5b67d7299086d997d5dafe8062658bcaeee75a5 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 12 Oct 2023 18:10:03 +0200 Subject: [PATCH 195/195] [DSC-106] Improved Tests --- .../date-picker/date-picker.component.spec.ts | 99 ++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts index 094fa582753..f3ca741475c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts @@ -1,6 +1,6 @@ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, Renderer2 } from '@angular/core'; -import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing'; import { FormControl, FormGroup } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @@ -13,6 +13,7 @@ import { mockDynamicFormLayoutService, mockDynamicFormValidationService } from '../../../../../testing/dynamic-form-mock-services'; +import { By } from '@angular/platform-browser'; export const DATE_TEST_GROUP = new FormGroup({ @@ -239,6 +240,102 @@ describe('DsDatePickerComponent test suite', () => { expect(dateComp.disabledMonth).toBeFalsy(); expect(dateComp.disabledDay).toBeFalsy(); }); + + it('should move focus on month field when on year field and tab pressed', fakeAsync(() => { + const event = { + field: 'day', + value: null + }; + const event1 = { + field: 'month', + value: null + }; + dateComp.onChange(event); + dateComp.onChange(event1); + + const yearElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_year`)); + const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`)); + + yearElement.nativeElement.focus(); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(yearElement.nativeElement); + + dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'tab' })); + dateFixture.detectChanges(); + + tick(200); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(monthElement.nativeElement); + })); + + it('should move focus on day field when on month field and tab pressed', fakeAsync(() => { + const event = { + field: 'day', + value: null + }; + dateComp.onChange(event); + + const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`)); + const dayElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_day`)); + + monthElement.nativeElement.focus(); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(monthElement.nativeElement); + + dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'tab' })); + dateFixture.detectChanges(); + + tick(200); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(dayElement.nativeElement); + })); + + it('should move focus on month field when on day field and shift tab pressed', fakeAsync(() => { + const event = { + field: 'day', + value: null + }; + dateComp.onChange(event); + + const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`)); + const dayElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_day`)); + + dayElement.nativeElement.focus(); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(dayElement.nativeElement); + + dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'shift.tab' })); + dateFixture.detectChanges(); + + tick(200); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(monthElement.nativeElement); + })); + + it('should move focus on year field when on month field and shift tab pressed', fakeAsync(() => { + const yearElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_year`)); + const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`)); + + monthElement.nativeElement.focus(); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(monthElement.nativeElement); + + dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'shift.tab' })); + dateFixture.detectChanges(); + + tick(200); + dateFixture.detectChanges(); + + expect(document.activeElement).toBe(yearElement.nativeElement); + })); + }); });