From 48ad0fc8ae67f0d0aae5d586efadb52aa3316f04 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Fri, 31 Dec 2021 11:03:47 +0530 Subject: [PATCH 001/758] [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/758] [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/758] [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/758] [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/758] [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/758] [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/758] [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/758] [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/758] [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/758] 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 f70fc7b87ac04cd4c591e9f6816d84582194385e Mon Sep 17 00:00:00 2001 From: lotte Date: Mon, 12 Sep 2022 17:42:37 +0200 Subject: [PATCH 011/758] 94233: use actual CSS variables in theme --- .../admin-sidebar-section.component.spec.ts | 2 +- .../admin-sidebar.component.spec.ts | 2 +- .../admin-sidebar/admin-sidebar.component.ts | 4 +- ...le-admin-sidebar-section.component.spec.ts | 2 +- ...andable-admin-sidebar-section.component.ts | 4 +- src/app/app.component.spec.ts | 2 +- src/app/app.component.ts | 21 ++--- src/app/app.reducer.ts | 2 +- src/app/core/core.module.ts | 2 +- src/app/root/root.component.spec.ts | 2 +- src/app/root/root.component.ts | 6 +- src/app/shared/host-window.service.ts | 10 +-- src/app/shared/key-value-pair.model.ts | 4 + ...per.actions.ts => css-variable.actions.ts} | 0 ...per.reducer.ts => css-variable.reducer.ts} | 2 +- .../sass-helper/css-variable.service.spec.ts | 78 +++++++++++++++++++ .../sass-helper/css-variable.service.ts | 71 +++++++++++++++++ .../shared/sass-helper/css-variable.utils.ts | 46 +++++++++++ .../shared/sass-helper/sass-helper.service.ts | 30 ------- 19 files changed, 226 insertions(+), 64 deletions(-) create mode 100644 src/app/shared/key-value-pair.model.ts rename src/app/shared/sass-helper/{sass-helper.actions.ts => css-variable.actions.ts} (100%) rename src/app/shared/sass-helper/{sass-helper.reducer.ts => css-variable.reducer.ts} (85%) create mode 100644 src/app/shared/sass-helper/css-variable.service.spec.ts create mode 100644 src/app/shared/sass-helper/css-variable.service.ts create mode 100644 src/app/shared/sass-helper/css-variable.utils.ts delete mode 100644 src/app/shared/sass-helper/sass-helper.service.ts diff --git a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts index 14d5d38199a..1e28b62626c 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MenuService } from '../../../shared/menu/menu.service'; import { MenuServiceStub } from '../../../shared/testing/menu-service.stub'; -import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service'; import { CSSVariableServiceStub } from '../../../shared/testing/css-variable-service.stub'; import { Component } from '@angular/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts index 65026c1504c..bcc40aae601 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts @@ -6,7 +6,7 @@ import { ScriptDataService } from '../../core/data/processes/script-data.service import { AdminSidebarComponent } from './admin-sidebar.component'; import { MenuService } from '../../shared/menu/menu.service'; import { MenuServiceStub } from '../../shared/testing/menu-service.stub'; -import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../shared/sass-helper/css-variable.service'; import { CSSVariableServiceStub } from '../../shared/testing/css-variable-service.stub'; import { AuthServiceStub } from '../../shared/testing/auth-service.stub'; import { AuthService } from '../../core/auth/auth.service'; diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index c81b2e6e93b..777fc4a250f 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -18,7 +18,7 @@ import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick import { TextMenuItemModel } from '../../shared/menu/menu-item/models/text.model'; import { MenuComponent } from '../../shared/menu/menu.component'; import { MenuService } from '../../shared/menu/menu.service'; -import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../shared/sass-helper/css-variable.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { Router, ActivatedRoute } from '@angular/router'; @@ -82,7 +82,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { ngOnInit(): void { this.createMenu(); super.ngOnInit(); - this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth'); + this.sidebarWidth = this.variableService.getVariable('--ds-sidebar-items-width'); this.authService.isAuthenticated() .subscribe((loggedIn: boolean) => { if (loggedIn) { diff --git a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts index b1f3a63c06e..0f0181c3d52 100644 --- a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts +++ b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ExpandableAdminSidebarSectionComponent } from './expandable-admin-sidebar-section.component'; import { MenuService } from '../../../shared/menu/menu.service'; import { MenuServiceStub } from '../../../shared/testing/menu-service.stub'; -import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service'; import { CSSVariableServiceStub } from '../../../shared/testing/css-variable-service.stub'; import { of as observableOf } from 'rxjs'; import { Component } from '@angular/core'; diff --git a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts index aaa6a85c518..59f07979bb9 100644 --- a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts +++ b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts @@ -2,7 +2,7 @@ import { Component, Inject, Injector, OnInit } from '@angular/core'; import { rotate } from '../../../shared/animations/rotate'; import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sidebar-section.component'; import { slide } from '../../../shared/animations/slide'; -import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service'; import { bgColor } from '../../../shared/animations/bgColor'; import { MenuID } from '../../../shared/menu/initial-menus-state'; import { MenuService } from '../../../shared/menu/menu.service'; @@ -65,7 +65,7 @@ export class ExpandableAdminSidebarSectionComponent extends AdminSidebarSectionC */ ngOnInit(): void { super.ngOnInit(); - this.sidebarActiveBg = this.variableService.getVariable('adminSidebarActiveBg'); + this.sidebarActiveBg = this.variableService.getVariable('--ds-admin-sidebar-active-bg'); this.sidebarCollapsed = this.menuService.isMenuCollapsed(this.menuID); this.sidebarPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID); this.expanded = combineLatestObservable(this.active, this.sidebarCollapsed, this.sidebarPreviewCollapsed) diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index a892e34a5ad..dcbf11525ef 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -19,7 +19,7 @@ import { AngularticsProviderMock } from './shared/mocks/angulartics-provider.ser import { AuthServiceMock } from './shared/mocks/auth.service.mock'; import { AuthService } from './core/auth/auth.service'; import { MenuService } from './shared/menu/menu.service'; -import { CSSVariableService } from './shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from './shared/sass-helper/css-variable.service'; import { CSSVariableServiceStub } from './shared/testing/css-variable-service.stub'; import { MenuServiceStub } from './shared/testing/menu-service.stub'; import { HostWindowService } from './shared/host-window.service'; diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 669411d9aaf..0c38d2bd645 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -31,7 +31,7 @@ import { HostWindowState } from './shared/search/host-window.reducer'; import { NativeWindowRef, NativeWindowService } from './core/services/window.service'; import { isAuthenticationBlocking } from './core/auth/selectors'; import { AuthService } from './core/auth/auth.service'; -import { CSSVariableService } from './shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from './shared/sass-helper/css-variable.service'; import { MenuService } from './shared/menu/menu.service'; import { HostWindowService } from './shared/host-window.service'; import { HeadTagConfig, ThemeConfig } from '../config/theme.model'; @@ -48,6 +48,7 @@ import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { getDefaultThemeConfig } from '../config/config.util'; import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; +import { getCSSCustomPropIndex } from './shared/sass-helper/css-variable.utils'; @Component({ selector: 'ds-app', @@ -161,7 +162,6 @@ export class AppComponent implements OnInit, AfterViewInit { if (environment.debug) { console.info(environment); } - this.storeCSSVariables(); } ngOnInit() { @@ -181,18 +181,9 @@ export class AppComponent implements OnInit, AfterViewInit { } private storeCSSVariables() { - this.cssService.addCSSVariable('xlMin', '1200px'); - this.cssService.addCSSVariable('mdMin', '768px'); - this.cssService.addCSSVariable('lgMin', '576px'); - this.cssService.addCSSVariable('smMin', '0'); - this.cssService.addCSSVariable('adminSidebarActiveBg', '#0f1b28'); - this.cssService.addCSSVariable('sidebarItemsWidth', '250px'); - this.cssService.addCSSVariable('collapsedSidebarWidth', '53.234px'); - this.cssService.addCSSVariable('totalSidebarWidth', '303.234px'); - // const vars = variables.locals || {}; - // Object.keys(vars).forEach((name: string) => { - // this.cssService.addCSSVariable(name, vars[name]); - // }) + getCSSCustomPropIndex(this.document).forEach(([prop, val]) => { + this.cssService.addCSSVariable(prop, val); + }); } ngAfterViewInit() { @@ -282,6 +273,8 @@ export class AppComponent implements OnInit, AfterViewInit { } // the fact that this callback is used, proves we're on the browser. this.isThemeCSSLoading$.next(false); + + this.storeCSSVariables(); }; head.appendChild(link); } diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index 5bd4f745d96..23d38bae305 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -35,7 +35,7 @@ import { ObjectSelectionListState, objectSelectionReducer } from './shared/object-select/object-select.reducer'; -import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer'; +import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/css-variable.reducer'; import { hostWindowReducer, HostWindowState } from './shared/search/host-window.reducer'; import { diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 8d8a614a899..2a4c75545ef 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -23,7 +23,7 @@ import { NotificationsService } from '../shared/notifications/notifications.serv import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service'; import { ObjectSelectService } from '../shared/object-select/object-select.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { CSSVariableService } from '../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../shared/sass-helper/css-variable.service'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { UploaderService } from '../shared/uploader/uploader.service'; import { SectionFormOperationsService } from '../submission/sections/form/section-form-operations.service'; diff --git a/src/app/root/root.component.spec.ts b/src/app/root/root.component.spec.ts index 81b22592d6a..2d41b8b610b 100644 --- a/src/app/root/root.component.spec.ts +++ b/src/app/root/root.component.spec.ts @@ -18,7 +18,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { RouterMock } from '../shared/mocks/router.mock'; import { MockActivatedRoute } from '../shared/mocks/active-router.mock'; import { MenuService } from '../shared/menu/menu.service'; -import { CSSVariableService } from '../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../shared/sass-helper/css-variable.service'; import { CSSVariableServiceStub } from '../shared/testing/css-variable-service.stub'; import { HostWindowService } from '../shared/host-window.service'; import { HostWindowServiceStub } from '../shared/testing/host-window-service.stub'; diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index dc44095573f..8584af357e6 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -11,7 +11,7 @@ import { MetadataService } from '../core/metadata/metadata.service'; import { HostWindowState } from '../shared/search/host-window.reducer'; import { NativeWindowRef, NativeWindowService } from '../core/services/window.service'; import { AuthService } from '../core/auth/auth.service'; -import { CSSVariableService } from '../shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../shared/sass-helper/css-variable.service'; import { MenuService } from '../shared/menu/menu.service'; import { MenuID } from '../shared/menu/initial-menus-state'; import { HostWindowService } from '../shared/host-window.service'; @@ -65,8 +65,8 @@ export class RootComponent implements OnInit { ngOnInit() { this.sidebarVisible = this.menuService.isMenuVisible(MenuID.ADMIN); - this.collapsedSidebarWidth = this.cssService.getVariable('collapsedSidebarWidth'); - this.totalSidebarWidth = this.cssService.getVariable('totalSidebarWidth'); + this.collapsedSidebarWidth = this.cssService.getVariable('--ds-collapsed-sidebar-width'); + this.totalSidebarWidth = this.cssService.getVariable('--ds-total-sidebar-width'); const sidebarCollapsed = this.menuService.isMenuCollapsed(MenuID.ADMIN); this.slideSidebarOver = combineLatestObservable([sidebarCollapsed, this.windowService.isXsOrSm()]) diff --git a/src/app/shared/host-window.service.ts b/src/app/shared/host-window.service.ts index 443dd40b14a..6d13d921e07 100644 --- a/src/app/shared/host-window.service.ts +++ b/src/app/shared/host-window.service.ts @@ -7,7 +7,7 @@ import { createSelector, select, Store } from '@ngrx/store'; import { hasValue } from './empty.util'; import { AppState } from '../app.reducer'; -import { CSSVariableService } from './sass-helper/sass-helper.service'; +import { CSSVariableService } from './sass-helper/css-variable.service'; export enum WidthCategory { XS, @@ -31,10 +31,10 @@ export class HostWindowService { /* See _exposed_variables.scss */ variableService.getAllVariables() .subscribe((variables) => { - this.breakPoints.XL_MIN = parseInt(variables.xlMin, 10); - this.breakPoints.LG_MIN = parseInt(variables.lgMin, 10); - this.breakPoints.MD_MIN = parseInt(variables.mdMin, 10); - this.breakPoints.SM_MIN = parseInt(variables.smMin, 10); + this.breakPoints.XL_MIN = parseInt(variables['--bs-xl-min'], 10); + this.breakPoints.LG_MIN = parseInt(variables['--bs-lg-min'], 10); + this.breakPoints.MD_MIN = parseInt(variables['--bs-md-min'], 10); + this.breakPoints.SM_MIN = parseInt(variables['--bs-sm-min'], 10); }); } diff --git a/src/app/shared/key-value-pair.model.ts b/src/app/shared/key-value-pair.model.ts new file mode 100644 index 00000000000..6fb63070ad3 --- /dev/null +++ b/src/app/shared/key-value-pair.model.ts @@ -0,0 +1,4 @@ +export interface KeyValuePair { + key: K; + value: V; +} diff --git a/src/app/shared/sass-helper/sass-helper.actions.ts b/src/app/shared/sass-helper/css-variable.actions.ts similarity index 100% rename from src/app/shared/sass-helper/sass-helper.actions.ts rename to src/app/shared/sass-helper/css-variable.actions.ts diff --git a/src/app/shared/sass-helper/sass-helper.reducer.ts b/src/app/shared/sass-helper/css-variable.reducer.ts similarity index 85% rename from src/app/shared/sass-helper/sass-helper.reducer.ts rename to src/app/shared/sass-helper/css-variable.reducer.ts index 6f080619fa9..a196304c92b 100644 --- a/src/app/shared/sass-helper/sass-helper.reducer.ts +++ b/src/app/shared/sass-helper/css-variable.reducer.ts @@ -1,4 +1,4 @@ -import { CSSVariableAction, CSSVariableActionTypes } from './sass-helper.actions'; +import { CSSVariableAction, CSSVariableActionTypes } from './css-variable.actions'; export interface CSSVariablesState { [name: string]: string; diff --git a/src/app/shared/sass-helper/css-variable.service.spec.ts b/src/app/shared/sass-helper/css-variable.service.spec.ts new file mode 100644 index 00000000000..4fd33591f51 --- /dev/null +++ b/src/app/shared/sass-helper/css-variable.service.spec.ts @@ -0,0 +1,78 @@ +import { TestBed } from '@angular/core/testing'; +import { CSSVariableService } from './css-variable.service'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { getTestScheduler } from 'jasmine-marbles'; +import { buildPaginatedList } from '../../core/data/paginated-list.model'; +import { PageInfo } from '../../core/shared/page-info.model'; +import { KeyValuePair } from '../key-value-pair.model'; + +describe('CSSVariableService', () => { + let store: MockStore; + + let service: CSSVariableService; + let initialState; + const varKey1 = '--test-1'; + const varValue1 = 'test-value-1'; + const varKey2 = '--test-2'; + const varValue2 = 'test-value-2'; + const varKey3 = '--test-3'; + const varValue3 = 'test-value-3'; + const queryInAll = 'test'; + const queryFor3 = '3'; + + function init() { + initialState = { + ['cssVariables']: { + [varKey1]: varValue1, + [varKey2]: varValue2, + [varKey3]: varValue3, + } + }; + } + + beforeEach(() => { + init(); + TestBed.configureTestingModule({ + providers: [ + CSSVariableService, + provideMockStore({ initialState }), + ], + }); + service = TestBed.inject(CSSVariableService as any); + store = TestBed.inject(MockStore as any); + }); + + it('should create', () => { + expect(service).toBeTruthy(); + }); + + fdescribe('searchVariable', () => { + it('should return the right keys and variables in a paginated list for query that returns all 3 results', () => { + const currentPage = 1; + const pageSize = 5; + const pageInfo = new PageInfo({ currentPage, elementsPerPage: pageSize, totalPages: 1, totalElements: 3 }); + const page: KeyValuePair[] = [{ key: varKey1, value: varValue1 }, { key: varKey2, value: varValue2 }, { key: varKey3, value: varValue3 }]; + const result = buildPaginatedList(pageInfo, page); + getTestScheduler().expectObservable(service.searchVariable(queryInAll, { currentPage, pageSize } as any)).toBe('a', { a: result }); + }); + + it('should return the right keys and variables in a paginated list for query that returns only the 3rd results', () => { + const currentPage = 1; + const pageSize = 5; + const pageInfo = new PageInfo({ currentPage, elementsPerPage: pageSize, totalPages: 1, totalElements: 1 }); + const page: KeyValuePair[] = [{ key: varKey3, value: varValue3 }]; + const result = buildPaginatedList(pageInfo, page); + getTestScheduler().expectObservable(service.searchVariable(queryFor3, { currentPage, pageSize } as any)).toBe('a', { a: result }); + }); + + it('should return the right keys and variables in a paginated list that\'s not longer than the page size', () => { + const currentPage = 1; + const pageSize = 2; + const pageInfo = new PageInfo({ currentPage, elementsPerPage: pageSize, totalPages: 2, totalElements: 3 }); + const page: KeyValuePair[] = [{ key: varKey1, value: varValue1 }, { key: varKey2, value: varValue2 }]; + const result = buildPaginatedList(pageInfo, page); + getTestScheduler().expectObservable(service.searchVariable(queryInAll, { currentPage, pageSize } as any)).toBe('a', { a: result }); + }); + }); + +}); diff --git a/src/app/shared/sass-helper/css-variable.service.ts b/src/app/shared/sass-helper/css-variable.service.ts new file mode 100644 index 00000000000..3e0ed7f2146 --- /dev/null +++ b/src/app/shared/sass-helper/css-variable.service.ts @@ -0,0 +1,71 @@ +import { Injectable } from '@angular/core'; +import { AppState, keySelector } from '../../app.reducer'; +import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; +import { AddCSSVariableAction } from './css-variable.actions'; +import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; +import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; +import { Observable } from 'rxjs'; +import { hasValue } from '../empty.util'; +import { KeyValuePair } from '../key-value-pair.model'; +import { PageInfo } from '../../core/shared/page-info.model'; + +@Injectable() +export class CSSVariableService { + constructor( + protected store: Store) { + } + + addCSSVariable(name: string, value: string) { + this.store.dispatch(new AddCSSVariableAction(name, value)); + } + + getVariable(name: string) { + return this.store.pipe(select(themeVariableByNameSelector(name))); + } + + getAllVariables() { + return this.store.pipe(select(themeVariablesSelector)); + } + + searchVariable(query: string, paginationOptions: PaginationComponentOptions): Observable>> { + return this.store.pipe(select(themePaginatedVariablesByQuery(query, paginationOptions))); + } +} + +const themeVariablesSelector = (state: AppState) => state.cssVariables; + +const themeVariableByNameSelector = (name: string): MemoizedSelector => { + return keySelector(name, themeVariablesSelector); +}; + +// Split this up into two memoized selectors so the query search gets cached separately from the pagination, +// since the entire list has to be retrieved every time anyway +const themePaginatedVariablesByQuery = (query: string, pagination: PaginationComponentOptions): MemoizedSelector>> => { + return createSelector(themeVariablesByQuery(query), (pairs) => { + if (hasValue(pairs)) { + const { currentPage, pageSize } = pagination; + const startIndex = (currentPage - 1) * pageSize; + const endIndex = startIndex + pageSize; + const pairsPage = pairs.slice(startIndex, endIndex); + const totalPages = Math.ceil(pairs.length / pageSize); + const pageInfo = new PageInfo({ currentPage, elementsPerPage: pageSize, totalElements: pairs.length, totalPages }); + return buildPaginatedList(pageInfo, pairsPage); + } else { + return undefined; + } + }); +}; + +const themeVariablesByQuery = (query: string): MemoizedSelector[]> => { + return createSelector(themeVariablesSelector, (state) => { + if (hasValue(state)) { + return Object.keys(state) + .filter((key: string) => key.includes(query)) + .map((key: string) => { + return { key, value: state[key] }; + }); + } else { + return undefined; + } + }); +}; diff --git a/src/app/shared/sass-helper/css-variable.utils.ts b/src/app/shared/sass-helper/css-variable.utils.ts new file mode 100644 index 00000000000..63a93b8bc4b --- /dev/null +++ b/src/app/shared/sass-helper/css-variable.utils.ts @@ -0,0 +1,46 @@ +// Uses code from https://css-tricks.com/how-to-get-all-custom-properties-on-a-page-in-javascript/ + +const isSameDomain = (styleSheet) => { + // Internal style blocks won't have an href value + if (!styleSheet.href) { + return true; + } + + return styleSheet.href.indexOf(window.location.origin) === 0; +}; + +/* + Determine if the given rule is a CSSStyleRule + See: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants +*/ +const isStyleRule = (rule) => rule.type === 1; + +/** + * Get all custom properties on a page + * @return array + * ex; [["--color-accent", "#b9f500"], ["--color-text", "#252525"], ...] + */ +export const getCSSCustomPropIndex = (document: Document) => + // styleSheets is array-like, so we convert it to an array. + // Filter out any stylesheets not on this domain + [...document.styleSheets] + .filter(isSameDomain) + .reduce( + (finalArr, sheet) => + finalArr.concat( + // cssRules is array-like, so we convert it to an array + [...sheet.cssRules].filter(isStyleRule).reduce((propValArr, rule: any) => { + const props = [...rule.style] + .map((propName) => [ + propName.trim(), + rule.style.getPropertyValue(propName).trim() + ]) + // Discard any props that don't start with "--". Custom props are required to. + .filter(([propName]) => propName.indexOf('--') === 0); + + return [...propValArr, ...props]; + }, []) + ), + [] + ); + diff --git a/src/app/shared/sass-helper/sass-helper.service.ts b/src/app/shared/sass-helper/sass-helper.service.ts deleted file mode 100644 index 7cc83dab2d8..00000000000 --- a/src/app/shared/sass-helper/sass-helper.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Injectable } from '@angular/core'; -import { AppState, keySelector } from '../../app.reducer'; -import { MemoizedSelector, select, Store } from '@ngrx/store'; -import { AddCSSVariableAction } from './sass-helper.actions'; - -@Injectable() -export class CSSVariableService { - constructor( - protected store: Store) { - } - - addCSSVariable(name: string, value: string) { - this.store.dispatch(new AddCSSVariableAction(name, value)); - } - - getVariable(name: string) { - return this.store.pipe(select(themeVariableByNameSelector(name))); - } - - getAllVariables() { - return this.store.pipe(select(themeVariablesSelector)); - } - -} - -const themeVariablesSelector = (state: AppState) => state.cssVariables; - -const themeVariableByNameSelector = (name: string): MemoizedSelector => { - return keySelector(name, themeVariablesSelector); -}; From e96a9794894e5859980b71516b6399eb908e898b Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 12 Sep 2022 20:29:49 +0200 Subject: [PATCH 012/758] [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 ac094aa623bfb1d83e540f82ff4a6487287cffd4 Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 13 Sep 2022 12:22:55 +0200 Subject: [PATCH 013/758] 94233: added typedoc --- .../sass-helper/css-variable.reducer.ts | 8 ++++++-- .../sass-helper/css-variable.service.ts | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/app/shared/sass-helper/css-variable.reducer.ts b/src/app/shared/sass-helper/css-variable.reducer.ts index a196304c92b..a2d150dabfd 100644 --- a/src/app/shared/sass-helper/css-variable.reducer.ts +++ b/src/app/shared/sass-helper/css-variable.reducer.ts @@ -6,12 +6,16 @@ export interface CSSVariablesState { const initialState: CSSVariablesState = Object.create({}); +/** + * Reducer that handles the state of CSS variables in the store + * @param state The current state of the store + * @param action The action to apply onto the current state of the store + */ export function cssVariablesReducer(state = initialState, action: CSSVariableAction): CSSVariablesState { switch (action.type) { case CSSVariableActionTypes.ADD: { const variable = action.payload; - const t = Object.assign({}, state, { [variable.name]: variable.value }); - return t; + return Object.assign({}, state, { [variable.name]: variable.value }); } default: { return state; diff --git a/src/app/shared/sass-helper/css-variable.service.ts b/src/app/shared/sass-helper/css-variable.service.ts index 3e0ed7f2146..bdcba8503ef 100644 --- a/src/app/shared/sass-helper/css-variable.service.ts +++ b/src/app/shared/sass-helper/css-variable.service.ts @@ -9,24 +9,44 @@ import { hasValue } from '../empty.util'; import { KeyValuePair } from '../key-value-pair.model'; import { PageInfo } from '../../core/shared/page-info.model'; +/** + * This service deals with adding and retrieving CSS variables to and from the store + */ @Injectable() export class CSSVariableService { constructor( protected store: Store) { } + /** + * Adds a CSS variable to the store + * @param name The name/key of the CSS variable + * @param value The value of the CSS variable + */ addCSSVariable(name: string, value: string) { this.store.dispatch(new AddCSSVariableAction(name, value)); } + /** + * Returns the value of a specific CSS key + * @param name The name/key of the CSS value + */ getVariable(name: string) { return this.store.pipe(select(themeVariableByNameSelector(name))); } + /** + * Returns the CSSVariablesState of the store containing all variables + */ getAllVariables() { return this.store.pipe(select(themeVariablesSelector)); } + /** + * Method to find CSS variables by their partially supplying their key. Case sensitive. Returns a paginated list of KeyValuePairs with CSS variables that match the query. + * @param query The query to look for in the keys + * @param paginationOptions The pagination options for the requested page + */ searchVariable(query: string, paginationOptions: PaginationComponentOptions): Observable>> { return this.store.pipe(select(themePaginatedVariablesByQuery(query, paginationOptions))); } From 8d2636455fb59e1c62842383e6c42f9e0da3df17 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Wed, 14 Sep 2022 18:22:04 +0530 Subject: [PATCH 014/758] [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 015/758] [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 89ba24fc1c1df13cfecdf12ebe59883901980d28 Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 16 Sep 2022 12:40:04 +0200 Subject: [PATCH 016/758] 94233: applied feedback for css variables --- src/app/app.component.ts | 6 +- .../sass-helper/css-variable.actions.ts | 20 +++++- .../sass-helper/css-variable.reducer.ts | 6 ++ .../sass-helper/css-variable.service.ts | 71 ++++++++++++++++++- .../shared/sass-helper/css-variable.utils.ts | 44 +----------- 5 files changed, 96 insertions(+), 51 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 0c38d2bd645..32753ce74c3 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -48,7 +48,6 @@ import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { getDefaultThemeConfig } from '../config/config.util'; import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; -import { getCSSCustomPropIndex } from './shared/sass-helper/css-variable.utils'; @Component({ selector: 'ds-app', @@ -181,9 +180,8 @@ export class AppComponent implements OnInit, AfterViewInit { } private storeCSSVariables() { - getCSSCustomPropIndex(this.document).forEach(([prop, val]) => { - this.cssService.addCSSVariable(prop, val); - }); + this.cssService.clearCSSVariables(); + this.cssService.addCSSVariables(this.cssService.getCSSVariablesFromStylesheets(this.document)); } ngAfterViewInit() { diff --git a/src/app/shared/sass-helper/css-variable.actions.ts b/src/app/shared/sass-helper/css-variable.actions.ts index 144904646ea..93225f94262 100644 --- a/src/app/shared/sass-helper/css-variable.actions.ts +++ b/src/app/shared/sass-helper/css-variable.actions.ts @@ -1,5 +1,6 @@ import { Action } from '@ngrx/store'; import { type } from '../ngrx/type'; +import { KeyValuePair } from '../key-value-pair.model'; /** * For each action type in an action group, make a simple @@ -11,6 +12,8 @@ import { type } from '../ngrx/type'; */ export const CSSVariableActionTypes = { ADD: type('dspace/css-variables/ADD'), + ADD_ALL: type('dspace/css-variables/ADD_ALL'), + CLEAR: type('dspace/css-variables/CLEAR'), }; export class AddCSSVariableAction implements Action { @@ -24,5 +27,20 @@ export class AddCSSVariableAction implements Action { this.payload = {name, value}; } } +export class AddAllCSSVariablesAction implements Action { + type = CSSVariableActionTypes.ADD_ALL; + payload: KeyValuePair[]; -export type CSSVariableAction = AddCSSVariableAction; + constructor(variables: KeyValuePair[]) { + this.payload = variables; + } +} + +export class ClearCSSVariablesAction implements Action { + type = CSSVariableActionTypes.CLEAR; + + constructor() { + } +} + +export type CSSVariableAction = AddCSSVariableAction | AddAllCSSVariablesAction | ClearCSSVariablesAction; diff --git a/src/app/shared/sass-helper/css-variable.reducer.ts b/src/app/shared/sass-helper/css-variable.reducer.ts index a2d150dabfd..405cbf5df41 100644 --- a/src/app/shared/sass-helper/css-variable.reducer.ts +++ b/src/app/shared/sass-helper/css-variable.reducer.ts @@ -1,4 +1,5 @@ import { CSSVariableAction, CSSVariableActionTypes } from './css-variable.actions'; +import { KeyValuePair } from '../key-value-pair.model'; export interface CSSVariablesState { [name: string]: string; @@ -16,6 +17,11 @@ export function cssVariablesReducer(state = initialState, action: CSSVariableAct case CSSVariableActionTypes.ADD: { const variable = action.payload; return Object.assign({}, state, { [variable.name]: variable.value }); + } case CSSVariableActionTypes.ADD_ALL: { + const variables = action.payload; + return Object.assign({}, state, ...variables.map(({ key, value }: KeyValuePair) => {return {[key]: value}})); + } case CSSVariableActionTypes.CLEAR: { + return initialState; } default: { return state; diff --git a/src/app/shared/sass-helper/css-variable.service.ts b/src/app/shared/sass-helper/css-variable.service.ts index bdcba8503ef..9ba9dfca3c9 100644 --- a/src/app/shared/sass-helper/css-variable.service.ts +++ b/src/app/shared/sass-helper/css-variable.service.ts @@ -1,19 +1,35 @@ import { Injectable } from '@angular/core'; import { AppState, keySelector } from '../../app.reducer'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; -import { AddCSSVariableAction } from './css-variable.actions'; +import { AddAllCSSVariablesAction, AddCSSVariableAction, ClearCSSVariablesAction } from './css-variable.actions'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { Observable } from 'rxjs'; import { hasValue } from '../empty.util'; import { KeyValuePair } from '../key-value-pair.model'; import { PageInfo } from '../../core/shared/page-info.model'; +import { CSSVariablesState } from './css-variable.reducer'; /** * This service deals with adding and retrieving CSS variables to and from the store */ @Injectable() export class CSSVariableService { + isSameDomain = (styleSheet) => { + // Internal style blocks won't have an href value + if (!styleSheet.href) { + return true; + } + + return styleSheet.href.indexOf(window.location.origin) === 0; + }; + + /* + Determine if the given rule is a CSSStyleRule + See: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants + */ + isStyleRule = (rule) => rule.type === 1; + constructor( protected store: Store) { } @@ -27,18 +43,33 @@ export class CSSVariableService { this.store.dispatch(new AddCSSVariableAction(name, value)); } + /** + * Adds multiples CSS variables to the store + * @param variables The key-value pairs with the CSS variables to be added + */ + addCSSVariables(variables: KeyValuePair[]) { + this.store.dispatch(new AddAllCSSVariablesAction(variables)); + } + + /** + * Clears all CSS variables ƒrom the store + */ + clearCSSVariables() { + this.store.dispatch(new ClearCSSVariablesAction()); + } + /** * Returns the value of a specific CSS key * @param name The name/key of the CSS value */ - getVariable(name: string) { + getVariable(name: string): Observable { return this.store.pipe(select(themeVariableByNameSelector(name))); } /** * Returns the CSSVariablesState of the store containing all variables */ - getAllVariables() { + getAllVariables(): Observable { return this.store.pipe(select(themeVariablesSelector)); } @@ -50,6 +81,40 @@ export class CSSVariableService { searchVariable(query: string, paginationOptions: PaginationComponentOptions): Observable>> { return this.store.pipe(select(themePaginatedVariablesByQuery(query, paginationOptions))); } + + /** + * Get all custom properties on a page + * @return array> + * ex; [{key: "--color-accent", value: "#b9f500"}, {key: "--color-text", value: "#252525"}, ...] + */ + getCSSVariablesFromStylesheets(document: Document): KeyValuePair[] + { + // styleSheets is array-like, so we convert it to an array. + // Filter out any stylesheets not on this domain + return [...document.styleSheets] + .filter(this.isSameDomain) + .reduce( + (finalArr, sheet) => + finalArr.concat( + // cssRules is array-like, so we convert it to an array + [...sheet.cssRules].filter(this.isStyleRule).reduce((propValArr, rule: any) => { + const props = [...rule.style] + .map((propName) => { + return { + key: propName.trim(), + value: rule.style.getPropertyValue(propName).trim() + } as KeyValuePair; + } + ) + // Discard any props that don't start with "--". Custom props are required to. + .filter(({ key }: KeyValuePair) => key.indexOf('--') === 0); + + return [...propValArr, ...props]; + }, []) + ), + [] + ); + } } const themeVariablesSelector = (state: AppState) => state.cssVariables; diff --git a/src/app/shared/sass-helper/css-variable.utils.ts b/src/app/shared/sass-helper/css-variable.utils.ts index 63a93b8bc4b..05e3074f98a 100644 --- a/src/app/shared/sass-helper/css-variable.utils.ts +++ b/src/app/shared/sass-helper/css-variable.utils.ts @@ -1,46 +1,4 @@ // Uses code from https://css-tricks.com/how-to-get-all-custom-properties-on-a-page-in-javascript/ -const isSameDomain = (styleSheet) => { - // Internal style blocks won't have an href value - if (!styleSheet.href) { - return true; - } - - return styleSheet.href.indexOf(window.location.origin) === 0; -}; - -/* - Determine if the given rule is a CSSStyleRule - See: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants -*/ -const isStyleRule = (rule) => rule.type === 1; - -/** - * Get all custom properties on a page - * @return array - * ex; [["--color-accent", "#b9f500"], ["--color-text", "#252525"], ...] - */ -export const getCSSCustomPropIndex = (document: Document) => - // styleSheets is array-like, so we convert it to an array. - // Filter out any stylesheets not on this domain - [...document.styleSheets] - .filter(isSameDomain) - .reduce( - (finalArr, sheet) => - finalArr.concat( - // cssRules is array-like, so we convert it to an array - [...sheet.cssRules].filter(isStyleRule).reduce((propValArr, rule: any) => { - const props = [...rule.style] - .map((propName) => [ - propName.trim(), - rule.style.getPropertyValue(propName).trim() - ]) - // Discard any props that don't start with "--". Custom props are required to. - .filter(([propName]) => propName.indexOf('--') === 0); - - return [...propValArr, ...props]; - }, []) - ), - [] - ); +import { KeyValuePair } from '../key-value-pair.model'; From 48a5e6bb7de2e1435d6daa5239a8e50b05b6dc0e Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 16 Sep 2022 15:44:21 +0200 Subject: [PATCH 017/758] [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 018/758] [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 ce67003f26478f2ef225c82e0466642454a0cd6d Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 16 Sep 2022 16:23:11 +0200 Subject: [PATCH 019/758] 94233: fixed tests after CSSVariableService changes --- src/app/shared/sass-helper/css-variable.service.spec.ts | 2 +- src/app/shared/testing/css-variable-service.stub.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/shared/sass-helper/css-variable.service.spec.ts b/src/app/shared/sass-helper/css-variable.service.spec.ts index 4fd33591f51..559384a5d7f 100644 --- a/src/app/shared/sass-helper/css-variable.service.spec.ts +++ b/src/app/shared/sass-helper/css-variable.service.spec.ts @@ -46,7 +46,7 @@ describe('CSSVariableService', () => { expect(service).toBeTruthy(); }); - fdescribe('searchVariable', () => { + describe('searchVariable', () => { it('should return the right keys and variables in a paginated list for query that returns all 3 results', () => { const currentPage = 1; const pageSize = 5; diff --git a/src/app/shared/testing/css-variable-service.stub.ts b/src/app/shared/testing/css-variable-service.stub.ts index 6159d896557..f72e3384553 100644 --- a/src/app/shared/testing/css-variable-service.stub.ts +++ b/src/app/shared/testing/css-variable-service.stub.ts @@ -1,10 +1,10 @@ import { Observable, of as observableOf } from 'rxjs'; const variables = { - smMin: '576px,', - mdMin: '768px,', - lgMin: '992px', - xlMin: '1200px', + '--bs-sm-min': '576px,', + '--bs-md-min': '768px,', + '--bs-lg-min': '992px', + '--bs-xl-min': '1200px', } as any; export class CSSVariableServiceStub { From 70ad345701069b0f1deeed752e8de454b3e1ae8d Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 16 Sep 2022 16:34:23 +0200 Subject: [PATCH 020/758] [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 021/758] [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 878db5be752595277e68bd71ceb221a416801e63 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 20 Sep 2022 15:45:04 +0200 Subject: [PATCH 022/758] 94390: Replace DSO page edit buttons with a menu --- .../admin-sidebar-section.component.html | 4 +- .../admin-sidebar-section.component.spec.ts | 104 +++++++--- .../admin-sidebar-section.component.ts | 13 +- ...dable-admin-sidebar-section.component.html | 1 + .../collection-page-routing.module.ts | 7 +- .../collection-page.component.html | 4 +- .../community-page-routing.module.ts | 7 +- .../community-page.component.html | 4 +- .../journal-issue.component.html | 5 +- .../journal-volume.component.html | 4 +- .../item-pages/journal/journal.component.html | 5 +- .../org-unit/org-unit.component.html | 5 +- .../item-pages/person/person.component.html | 5 +- .../item-pages/project/project.component.html | 5 +- .../full/full-item-page.component.html | 5 +- src/app/item-page/item-page-routing.module.ts | 5 +- src/app/item-page/item-page.module.ts | 2 - .../publication/publication.component.html | 4 +- .../untyped-item/untyped-item.component.html | 8 +- .../untyped-item/untyped-item.component.ts | 4 +- .../versioned-item.component.scss | 0 .../versioned-item.component.spec.ts | 95 --------- .../versioned-item.component.ts | 80 -------- .../expandable-navbar-section.component.html | 1 + .../dso-page/dso-edit-menu.resolver.spec.ts | 190 ++++++++++++++++++ .../shared/dso-page/dso-edit-menu.resolver.ts | 164 +++++++++++++++ ...dit-menu-expandable-section.component.html | 18 ++ ...dit-menu-expandable-section.component.scss | 26 +++ ...-menu-expandable-section.component.spec.ts | 75 +++++++ ...-edit-menu-expandable-section.component.ts | 39 ++++ .../dso-edit-menu-section.component.html | 25 +++ .../dso-edit-menu-section.component.scss | 3 + .../dso-edit-menu-section.component.spec.ts | 173 ++++++++++++++++ .../dso-edit-menu-section.component.ts | 51 +++++ .../dso-edit-menu.component.html | 6 + .../dso-edit-menu.component.scss} | 0 .../dso-edit-menu.component.spec.ts | 78 +++++++ .../dso-edit-menu/dso-edit-menu.component.ts | 36 ++++ .../dso-page-edit-button.component.html | 7 - .../dso-page-edit-button.component.scss | 3 - .../dso-page-edit-button.component.spec.ts | 76 ------- .../dso-page-edit-button.component.ts | 43 ---- .../dso-page-version-button.component.html | 8 - .../dso-page-version-button.component.scss | 3 - .../dso-page-version-button.component.spec.ts | 96 --------- .../dso-page-version-button.component.ts | 77 ------- .../dso-versioning-modal.service.spec.ts | 92 +++++++++ .../dso-versioning-modal.service.ts | 98 +++++++++ src/app/shared/menu/initial-menus-state.ts | 14 +- .../menu-item/link-menu-item.component.html | 4 +- .../menu-item/link-menu-item.component.ts | 2 +- .../menu/menu-item/models/altmetric.model.ts | 1 + .../menu/menu-item/models/link.model.ts | 1 + .../menu/menu-item/models/menu-item.model.ts | 1 + .../menu/menu-item/models/onclick.model.ts | 1 + .../menu/menu-item/models/search.model.ts | 1 + .../menu/menu-item/models/text.model.ts | 1 + .../onclick-menu-item.component.html | 4 +- .../menu-item/onclick-menu-item.component.ts | 9 +- .../menu-item/text-menu-item.component.html | 2 +- .../menu-section/menu-section.component.ts | 8 +- src/app/shared/shared.module.ts | 17 +- 62 files changed, 1246 insertions(+), 584 deletions(-) delete mode 100644 src/app/item-page/simple/item-types/versioned-item/versioned-item.component.scss delete mode 100644 src/app/item-page/simple/item-types/versioned-item/versioned-item.component.spec.ts delete mode 100644 src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu.resolver.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.spec.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.html rename src/app/{item-page/simple/item-types/versioned-item/versioned-item.component.html => shared/dso-page/dso-edit-menu/dso-edit-menu.component.scss} (100%) create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.spec.ts create mode 100644 src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts delete mode 100644 src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.html delete mode 100644 src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.scss delete mode 100644 src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.spec.ts delete mode 100644 src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.ts delete mode 100644 src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.html delete mode 100644 src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.scss delete mode 100644 src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.spec.ts delete mode 100644 src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.ts create mode 100644 src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts create mode 100644 src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.ts diff --git a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html index 8706b40ee09..7f1e8716ba5 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html +++ b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html @@ -1,7 +1,7 @@
diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index ad1b1fd2f20..ac90f3618c5 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -14,6 +14,7 @@ import { CommunityPageAdministratorGuard } from './community-page-administrator. import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCommunityPageComponent } from './themed-community-page.component'; +import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; @NgModule({ imports: [ @@ -27,7 +28,8 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' path: ':id', resolve: { dso: CommunityPageResolver, - breadcrumb: CommunityBreadcrumbResolver + breadcrumb: CommunityBreadcrumbResolver, + menu: DSOEditMenuResolver }, runGuardsAndResolvers: 'always', children: [ @@ -72,7 +74,8 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' DSOBreadcrumbsService, LinkService, CreateCommunityPageGuard, - CommunityPageAdministratorGuard + CommunityPageAdministratorGuard, + DSOEditMenuResolver ] }) export class CommunityPageRoutingModule { diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index cf7282eb4bd..cee13eadd67 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -20,9 +20,7 @@ [title]="'community.page.news'"> -
- -
+
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index 5847be7dd2f..94dc81fc8f4 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -2,9 +2,8 @@

{{'journalissue.page.titleprefix' | translate}}

-
- -
+ +
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index a0a25766efc..b472f4e6486 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -2,9 +2,7 @@

{{'journalvolume.page.titleprefix' | translate}}

-
- -
+
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index af3ac85959b..9b11409a31c 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -2,9 +2,8 @@

{{'journal.page.titleprefix' | translate}}

-
- -
+ +
diff --git a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html index c9ea8fb5499..21b36de7226 100644 --- a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html @@ -2,9 +2,8 @@

{{'orgunit.page.titleprefix' | translate}}

-
- -
+ +
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index 5c2fd227fdf..c23b5430110 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -2,9 +2,8 @@

{{'person.page.titleprefix' | translate}}

-
- -
+ +
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index 8f2ff6adcda..b9a1a30225c 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -2,9 +2,8 @@

{{'project.page.titleprefix' | translate}}

-
- -
+ +
diff --git a/src/app/item-page/full/full-item-page.component.html b/src/app/item-page/full/full-item-page.component.html index d2655c4ad03..8bb7860aaaf 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -6,9 +6,8 @@
-
- -
+ +
diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index 04794717f1f..c9c88fa0850 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -11,12 +11,8 @@

-
- - -
+ +
diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.ts b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.ts index 3ce33dc90ab..ead62008dee 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.ts +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; -import { VersionedItemComponent } from '../versioned-item/versioned-item.component'; +import { ItemComponent } from '../shared/item.component'; /** * Component that represents a publication Item page @@ -15,6 +15,6 @@ import { VersionedItemComponent } from '../versioned-item/versioned-item.compone templateUrl: './untyped-item.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class UntypedItemComponent extends VersionedItemComponent { +export class UntypedItemComponent extends ItemComponent { } diff --git a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.scss b/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.spec.ts b/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.spec.ts deleted file mode 100644 index eff51b10199..00000000000 --- a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { VersionedItemComponent } from './versioned-item.component'; -import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service'; -import { TranslateService } from '@ngx-translate/core'; -import { VersionDataService } from '../../../../core/data/version-data.service'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; -import { ItemVersionsSharedService } from '../../../../shared/item/item-versions/item-versions-shared.service'; -import { Item } from '../../../../core/shared/item.model'; -import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -import { buildPaginatedList } from '../../../../core/data/paginated-list.model'; -import { PageInfo } from '../../../../core/shared/page-info.model'; -import { MetadataMap } from '../../../../core/shared/metadata.models'; -import { createRelationshipsObservable, mockRouteService } from '../shared/item.component.spec'; -import { RouterTestingModule } from '@angular/router/testing'; -import { Component } from '@angular/core'; -import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service'; -import { SearchService } from '../../../../core/shared/search/search.service'; -import { ItemDataService } from '../../../../core/data/item-data.service'; -import { Version } from '../../../../core/shared/version.model'; -import { RouteService } from '../../../../core/services/route.service'; - -const mockItem: Item = Object.assign(new Item(), { - bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), - metadata: new MetadataMap(), - relationships: createRelationshipsObservable(), - _links: { - self: { - href: 'item-href' - }, - version: { - href: 'version-href' - } - } -}); - - -@Component({template: ''}) -class DummyComponent { -} - -describe('VersionedItemComponent', () => { - let component: VersionedItemComponent; - let fixture: ComponentFixture; - - let versionService: VersionDataService; - let versionHistoryService: VersionHistoryDataService; - - const versionServiceSpy = jasmine.createSpyObj('versionService', { - findByHref: createSuccessfulRemoteDataObject$(new Version()), - }); - - const versionHistoryServiceSpy = jasmine.createSpyObj('versionHistoryService', { - createVersion: createSuccessfulRemoteDataObject$(new Version()), - }); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [VersionedItemComponent, DummyComponent], - imports: [RouterTestingModule], - providers: [ - { provide: VersionHistoryDataService, useValue: versionHistoryServiceSpy }, - { provide: TranslateService, useValue: {} }, - { provide: VersionDataService, useValue: versionServiceSpy }, - { provide: NotificationsService, useValue: {} }, - { provide: ItemVersionsSharedService, useValue: {} }, - { provide: WorkspaceitemDataService, useValue: {} }, - { provide: SearchService, useValue: {} }, - { provide: ItemDataService, useValue: {} }, - { provide: RouteService, useValue: mockRouteService } - ] - }).compileComponents(); - versionService = TestBed.inject(VersionDataService); - versionHistoryService = TestBed.inject(VersionHistoryDataService); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(VersionedItemComponent); - component = fixture.componentInstance; - component.object = mockItem; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('when onCreateNewVersion() is called', () => { - it('should call versionService.findByHref', () => { - component.onCreateNewVersion(); - expect(versionService.findByHref).toHaveBeenCalledWith('version-href'); - }); - }); - -}); diff --git a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts b/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts deleted file mode 100644 index cd2eb3a19b4..00000000000 --- a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Component } from '@angular/core'; -import { ItemComponent } from '../shared/item.component'; -import { ItemVersionsSummaryModalComponent } from '../../../../shared/item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component'; -import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { Version } from '../../../../core/shared/version.model'; -import { switchMap, tap } from 'rxjs/operators'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service'; -import { TranslateService } from '@ngx-translate/core'; -import { VersionDataService } from '../../../../core/data/version-data.service'; -import { ItemVersionsSharedService } from '../../../../shared/item/item-versions/item-versions-shared.service'; -import { Router } from '@angular/router'; -import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service'; -import { SearchService } from '../../../../core/shared/search/search.service'; -import { Item } from '../../../../core/shared/item.model'; -import { ItemDataService } from '../../../../core/data/item-data.service'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; -import { RouteService } from '../../../../core/services/route.service'; - -@Component({ - selector: 'ds-versioned-item', - templateUrl: './versioned-item.component.html', - styleUrls: ['./versioned-item.component.scss'] -}) -export class VersionedItemComponent extends ItemComponent { - - constructor( - private modalService: NgbModal, - private versionHistoryService: VersionHistoryDataService, - private translateService: TranslateService, - private versionService: VersionDataService, - private itemVersionShared: ItemVersionsSharedService, - private router: Router, - private workspaceItemDataService: WorkspaceitemDataService, - private searchService: SearchService, - private itemService: ItemDataService, - protected routeService: RouteService - ) { - super(routeService); - } - - /** - * Open a modal that allows to create a new version starting from the specified item, with optional summary - */ - onCreateNewVersion(): void { - - const item = this.object; - const versionHref = item._links.version.href; - - // Open modal - const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent); - - // Show current version in modal - this.versionService.findByHref(versionHref).pipe(getFirstCompletedRemoteData()).subscribe((res: RemoteData) => { - // if res.hasNoContent then the item is unversioned - activeModal.componentInstance.firstVersion = res.hasNoContent; - activeModal.componentInstance.versionNumber = (res.hasNoContent ? undefined : res.payload.version); - }); - - // On createVersionEvent emitted create new version and notify - activeModal.componentInstance.createVersionEvent.pipe( - switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)), - getFirstCompletedRemoteData(), - // show success/failure notification - tap((res: RemoteData) => { this.itemVersionShared.notifyCreateNewVersion(res); }), - // get workspace item - getFirstSucceededRemoteDataPayload(), - switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)), - getFirstSucceededRemoteDataPayload(), - switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)), - getFirstSucceededRemoteDataPayload(), - ).subscribe((wsItem) => { - const wsiId = wsItem.id; - const route = 'workspaceitems/' + wsiId + '/edit'; - this.router.navigateByUrl(route); - }); - - } -} diff --git a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html index d8a517dc554..a77b6bc2530 100644 --- a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html +++ b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html @@ -6,6 +6,7 @@ (mouseenter)="activateSection($event)" (mouseleave)="deactivateSection($event)"> { + + const MENU_STATE = { + id: 'some menu' + }; + + let resolver: DSOEditMenuResolver; + + let dSpaceObjectDataService; + let menuService; + let authorizationService; + let dsoVersioningModalService; + + const route = { + data: { + menu: { + 'statistics': [{ + id: 'statistics-dummy-1', + active: false, + visible: true, + model: null + }] + } + }, + params: {id: 'test-uuid'}, + }; + + const state = { + url: 'test-url' + }; + + const testObject = Object.assign(new Item(), {uuid: 'test-uuid', type: 'item', _links: {self: {href: 'self-link'}}}); + + const dummySections1 = [{ + id: 'dummy-1', + active: false, + visible: true, + model: null + }, + { + id: 'dummy-2', + active: false, + visible: true, + model: null + }]; + + const dummySections2 = [{ + id: 'dummy-3', + active: false, + visible: true, + model: null + }, + { + id: 'dummy-4', + active: false, + visible: true, + model: null + }, + { + id: 'dummy-5', + active: false, + visible: true, + model: null + }]; + + beforeEach(waitForAsync(() => { + menuService = new MenuServiceStub(); + spyOn(menuService, 'getMenu').and.returnValue(observableOf(MENU_STATE)); + + dSpaceObjectDataService = jasmine.createSpyObj('dSpaceObjectDataService', { + findById: createSuccessfulRemoteDataObject$(testObject) + }); + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + dsoVersioningModalService = jasmine.createSpyObj('dsoVersioningModalService', { + isNewVersionButtonDisabled: observableOf(false), + getVersioningTooltipMessage: observableOf('message'), + openCreateVersionModal: {} + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule], + declarations: [AdminSidebarComponent], + providers: [ + {provide: DSpaceObjectDataService, useValue: dSpaceObjectDataService}, + {provide: MenuService, useValue: menuService}, + {provide: AuthorizationDataService, useValue: authorizationService}, + {provide: DsoVersioningModalService, useValue: dsoVersioningModalService}, + { + provide: NgbModal, useValue: { + open: () => {/*comment*/ + } + } + } + ], + schemas: [NO_ERRORS_SCHEMA] + }); + resolver = TestBed.inject(DSOEditMenuResolver); + + spyOn(menuService, 'addSection'); + })); + + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); + + describe('resolve', () => { + it('should create all menus when a dso is found', (done) => { + spyOn(resolver, 'getDsoMenus').and.returnValue( + [observableOf(dummySections1), observableOf(dummySections2)] + ); + resolver.resolve(route as any, null).subscribe(resolved => { + expect(resolved).toEqual( + { + ...route.data.menu, + [MenuID.DSO_EDIT]: [ + ...dummySections1.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'})), + ...dummySections2.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'})) + ] + } + ); + expect(resolver.getDsoMenus).toHaveBeenCalled(); + done(); + }); + }); + it('should return the statistics menu when no dso is found', (done) => { + (dSpaceObjectDataService.findById as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$()); + + resolver.resolve(route as any, null).subscribe(resolved => { + expect(resolved).toEqual( + { + ...route.data.menu + } + ); + done(); + }); + }); + }); + describe('getDsoMenus', () => { + it('should return as first part the item version list ', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + result[0].subscribe((menuList) => { + expect(menuList.length).toEqual(1); + expect(menuList[0].id).toEqual('version-dso'); + expect(menuList[0].active).toEqual(false); + expect(menuList[0].visible).toEqual(true); + expect(menuList[0].model.type).toEqual(MenuItemType.ONCLICK); + expect(menuList[0].model.text).toEqual('message'); + expect(menuList[0].model.disabled).toEqual(false); + expect(menuList[0].icon).toEqual('code-branch'); + done(); + }); + + }); + it('should return as second part the common list ', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + result[1].subscribe((menuList) => { + expect(menuList.length).toEqual(1); + expect(menuList[0].id).toEqual('edit-dso'); + expect(menuList[0].active).toEqual(false); + expect(menuList[0].visible).toEqual(true); + expect(menuList[0].model.type).toEqual(MenuItemType.LINK); + expect(menuList[0].model.text).toEqual('item.page.edit'); + expect(menuList[0].model.link).toEqual('test-url/edit/metadata'); + expect(menuList[0].icon).toEqual('pencil-alt'); + done(); + }); + + }); + }); +}); diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.ts new file mode 100644 index 00000000000..003e61df954 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -0,0 +1,164 @@ +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 { MenuID, MenuItemType } from '../menu/initial-menus-state'; +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 { hasValue } from '../empty.util'; +import { MenuSection } from '../menu/menu.reducer'; + +/** + * Creates the menus for the dspace object pages + */ +@Injectable({ + providedIn: 'root' +}) +export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection[] }> { + + constructor( + protected dSpaceObjectDataService: DSpaceObjectDataService, + protected menuService: MenuService, + protected authorizationService: AuthorizationDataService, + protected modalService: NgbModal, + protected dsoVersioningModalService: DsoVersioningModalService, + ) { + } + + /** + * Initialise all dspace object related menus + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<{ [key: string]: MenuSection[] }> { + const uuid = route.params.id; + return this.dSpaceObjectDataService.findById(uuid, true, false).pipe( + getFirstCompletedRemoteData(), + switchMap((dsoRD) => { + if (dsoRD.hasSucceeded) { + const dso = dsoRD.payload; + return combineLatest(this.getDsoMenus(dso, route, state)).pipe( + map((combinedMenus) => [].concat.apply([], combinedMenus)), + map((menus) => this.addDsoUuidToMenuIDs(menus, dso)), + map((menus) => { + return { + ...route.data?.menu, + [MenuID.DSO_EDIT]: menus + }; + }) + ); + } else { + return observableOf({...route.data?.menu}); + } + }) + ); + } + + /** + * Return all the menus for a dso based on the route and state + */ + getDsoMenus(dso, route, state) { + return [ + this.getItemMenu(dso), + this.getCommonMenu(dso, state) + ]; + } + + /** + * Get the common menus between all dspace objects + */ + protected getCommonMenu(dso, state): Observable { + return combineLatest([ + this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, dso.self), + ]).pipe( + map(([canEditItem]) => { + return [ + { + id: 'edit-dso', + active: false, + visible: canEditItem, + model: { + type: MenuItemType.LINK, + text: this.getDsoType(dso) + '.page.edit', + link: new URLCombiner(state.url, 'edit', 'metadata').toString() + } as LinkMenuItemModel, + icon: 'pencil-alt', + index: 1 + }, + ]; + + }) + ); + } + + /** + * Get item sepcific menus + */ + protected getItemMenu(dso): Observable { + if (dso instanceof Item) { + return combineLatest([ + this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, dso.self), + this.dsoVersioningModalService.isNewVersionButtonDisabled(dso), + this.dsoVersioningModalService.getVersioningTooltipMessage(dso, 'item.page.version.hasDraft', 'item.page.version.create') + ]).pipe( + map(([canCreateVersion, disableVersioning, versionTooltip]) => { + return [ + { + id: 'version-dso', + active: false, + visible: canCreateVersion, + model: { + type: MenuItemType.ONCLICK, + text: versionTooltip, + disabled: disableVersioning, + function: () => { + this.dsoVersioningModalService.openCreateVersionModal(dso); + } + } as OnClickMenuItemModel, + icon: 'code-branch', + index: 0 + }, + ]; + }), + ); + } else { + return observableOf([]); + } + } + + /** + * Retrieve the dso or entity type for an object to be used in generic messages + */ + protected getDsoType(dso) { + const renderType = dso.getRenderTypes()[0]; + if (typeof renderType === 'string' || renderType instanceof String) { + return renderType.toLowerCase(); + } else { + return dso.type.toString().toLowerCase(); + } + } + + /** + * Add the dso uuid to all provided menu ids and parent ids + */ + protected addDsoUuidToMenuIDs(menus, dso) { + return menus.map((menu) => { + Object.assign(menu, { + id: menu.id + '-' + dso.uuid + }); + if (hasValue(menu.parentID)) { + Object.assign(menu, { + parentID: menu.parentID + '-' + dso.uuid + }); + } + return menu; + }); + } +} diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html new file mode 100644 index 00000000000..b330b1e87d5 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html @@ -0,0 +1,18 @@ +
+
+ +
    + + + +
+
+
+ + + + diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss new file mode 100644 index 00000000000..61ec6347f95 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss @@ -0,0 +1,26 @@ +.btn-dark { + background-color: var(--ds-admin-sidebar-bg); +} + +.dso-button-menu { + .dropdown-toggle::after { + display: none; + } +} + +ul.dropdown-menu { + background-color: var(--ds-admin-sidebar-bg); + color: white; + + ::ng-deep a { + color: white; + + &.disabled { + color: $btn-link-disabled-color; + } + } + + .disabled { + color: $btn-link-disabled-color; + } +} 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 new file mode 100644 index 00000000000..211fd0bdf07 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.spec.ts @@ -0,0 +1,75 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { MenuServiceStub } from '../../../testing/menu-service.stub'; +import { TranslateModule } from '@ngx-translate/core'; +import { MenuService } from '../../../menu/menu.service'; +import { CSSVariableService } from '../../../sass-helper/sass-helper.service'; +import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub'; +import { Router } from '@angular/router'; +import { RouterStub } from '../../../testing/router.stub'; +import { of as observableOf } from 'rxjs'; +import { Component } from '@angular/core'; +import { DsoEditMenuExpandableSectionComponent } from './dso-edit-menu-expandable-section.component'; +import { MenuItemType } from '../../../menu/initial-menus-state'; +import { By } from '@angular/platform-browser'; + +describe('DsoEditMenuExpandableSectionComponent', () => { + let component: DsoEditMenuExpandableSectionComponent; + let fixture: ComponentFixture; + const menuService = new MenuServiceStub(); + const iconString = 'test'; + + const dummySection = { + id: 'dummy', + active: false, + visible: true, + model: { + type: MenuItemType.TEXT, + disabled: false, + text: 'text' + }, + icon: iconString + }; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [DsoEditMenuExpandableSectionComponent, TestComponent], + providers: [ + {provide: 'sectionDataProvider', useValue: dummySection}, + {provide: MenuService, useValue: menuService}, + {provide: CSSVariableService, useClass: CSSVariableServiceStub}, + {provide: Router, useValue: new RouterStub()}, + ] + }).overrideComponent(DsoEditMenuExpandableSectionComponent, { + set: { + entryComponents: [TestComponent] + } + }) + .compileComponents(); + })); + + beforeEach(() => { + spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([])); + fixture = TestBed.createComponent(DsoEditMenuExpandableSectionComponent); + component = fixture.componentInstance; + spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should show a button with the icon', () => { + const button = fixture.debugElement.query(By.css('.btn-dark')); + expect(button.nativeElement.innerHTML).toContain('fa-' + iconString); + }); +}); + +// declare a test component +@Component({ + selector: 'ds-test-cmp', + template: `` +}) +class TestComponent { +} diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.ts new file mode 100644 index 00000000000..e0ad28617b8 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.ts @@ -0,0 +1,39 @@ +import { Component, Inject, Injector } from '@angular/core'; +import { MenuID } from 'src/app/shared/menu/initial-menus-state'; +import { rendersSectionForMenu } from 'src/app/shared/menu/menu-section.decorator'; +import { MenuSectionComponent } from 'src/app/shared/menu/menu-section/menu-section.component'; +import { MenuService } from '../../../menu/menu.service'; +import { MenuSection } from '../../../menu/menu.reducer'; +import { Router } from '@angular/router'; + +/** + * Represents an expandable section in the dso edit menus + */ +@Component({ + /* tslint:disable:component-selector */ + selector: 'ds-dso-edit-menu-expandable-section', + templateUrl: './dso-edit-menu-expandable-section.component.html', + styleUrls: ['./dso-edit-menu-expandable-section.component.scss'], +}) +@rendersSectionForMenu(MenuID.DSO_EDIT, true) +export class DsoEditMenuExpandableSectionComponent extends MenuSectionComponent { + + menuID: MenuID = MenuID.DSO_EDIT; + itemModel; + + constructor( + @Inject('sectionDataProvider') menuSection: MenuSection, + protected menuService: MenuService, + protected injector: Injector, + protected router: Router, + ) { + super(menuSection, menuService, injector); + this.itemModel = menuSection.model; + } + + ngOnInit(): void { + this.menuService.activateSection(this.menuID, this.section.id); + super.ngOnInit(); + } + +} diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html new file mode 100644 index 00000000000..0fc6d19667d --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html @@ -0,0 +1,25 @@ +
+ + + +
+ +
+
+ +
+ +
diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss new file mode 100644 index 00000000000..cf0e81c5538 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss @@ -0,0 +1,3 @@ +.btn-dark { + background-color: var(--ds-admin-sidebar-bg); +} diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts new file mode 100644 index 00000000000..bcfeab8180b --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts @@ -0,0 +1,173 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { MenuServiceStub } from '../../../testing/menu-service.stub'; +import { TranslateModule } from '@ngx-translate/core'; +import { MenuService } from '../../../menu/menu.service'; +import { CSSVariableService } from '../../../sass-helper/sass-helper.service'; +import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub'; +import { Router } from '@angular/router'; +import { RouterStub } from '../../../testing/router.stub'; +import { of as observableOf } from 'rxjs'; +import { Component } from '@angular/core'; +import { MenuItemType } from '../../../menu/initial-menus-state'; +import { By } from '@angular/platform-browser'; +import { DsoEditMenuSectionComponent } from './dso-edit-menu-section.component'; +import { OnClickMenuItemModel } from '../../../menu/menu-item/models/onclick.model'; + +function initAsync(dummySectionText: { visible: boolean; icon: string; active: boolean; model: { disabled: boolean; text: string; type: MenuItemType }; id: string }, menuService: MenuServiceStub) { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [DsoEditMenuSectionComponent, TestComponent], + providers: [ + {provide: 'sectionDataProvider', useValue: dummySectionText}, + {provide: MenuService, useValue: menuService}, + {provide: CSSVariableService, useClass: CSSVariableServiceStub}, + {provide: Router, useValue: new RouterStub()}, + ] + }).overrideComponent(DsoEditMenuSectionComponent, { + set: { + entryComponents: [TestComponent] + } + }) + .compileComponents(); + })); +} + +describe('DsoEditMenuSectionComponent', () => { + let component: DsoEditMenuSectionComponent; + let fixture: ComponentFixture; + const menuService = new MenuServiceStub(); + const iconString = 'test'; + + const dummySectionText = { + id: 'dummy', + active: false, + visible: true, + model: { + type: MenuItemType.TEXT, + disabled: false, + text: 'text' + }, + icon: iconString + }; + const dummySectionLink = { + id: 'dummy', + active: false, + visible: true, + model: { + type: MenuItemType.LINK, + disabled: false, + text: 'text', + link: 'link' + }, + icon: iconString + }; + + const dummySectionClick = { + id: 'dummy', + active: false, + visible: true, + model: { + type: MenuItemType.ONCLICK, + disabled: false, + text: 'text', + function: () => 'test' + }, + icon: iconString + }; + + describe('text model', () => { + initAsync(dummySectionText, menuService); + beforeEach(() => { + spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([])); + fixture = TestBed.createComponent(DsoEditMenuSectionComponent); + component = fixture.componentInstance; + spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should show a button with the icon', () => { + const button = fixture.debugElement.query(By.css('.btn-dark')); + expect(button.nativeElement.innerHTML).toContain('fa-' + iconString); + }); + describe('when the section model in a disabled link or text', () => { + it('should show just the button', () => { + const textButton = fixture.debugElement.query(By.css('div div button')); + expect(textButton.nativeElement.innerHTML).toContain('fa-' + iconString); + }); + }); + }); + describe('on click model', () => { + initAsync(dummySectionClick, menuService); + beforeEach(() => { + spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([])); + fixture = TestBed.createComponent(DsoEditMenuSectionComponent); + component = fixture.componentInstance; + spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent); + fixture.detectChanges(); + }); + + describe('when the section model in an on click menu', () => { + it('should call the activate method when clicking the button', () => { + spyOn(component, 'activate'); + + const button = fixture.debugElement.query(By.css('.btn-dark')); + button.triggerEventHandler('click', null); + + expect(component.activate).toHaveBeenCalled(); + }); + }); + describe('activate', () => { + const mockEvent = jasmine.createSpyObj('event', { + preventDefault: jasmine.createSpy('preventDefault'), + stopPropagation: jasmine.createSpy('stopPropagation'), + }); + it('should call the item model function when not disabled', () => { + spyOn(component.section.model as OnClickMenuItemModel, 'function'); + component.activate(mockEvent); + + expect((component.section.model as OnClickMenuItemModel).function).toHaveBeenCalled(); + }); + it('should call not the item model function when disabled', () => { + spyOn(component.section.model as OnClickMenuItemModel, 'function'); + component.itemModel.disabled = true; + component.activate(mockEvent); + + expect((component.section.model as OnClickMenuItemModel).function).not.toHaveBeenCalled(); + component.itemModel.disabled = false; + }); + }); + + }); + + describe('link model', () => { + initAsync(dummySectionLink, menuService); + beforeEach(() => { + spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([])); + fixture = TestBed.createComponent(DsoEditMenuSectionComponent); + component = fixture.componentInstance; + spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent); + fixture.detectChanges(); + }); + + describe('when the section model in a non disabled link', () => { + it('should show a link element with the button in it', () => { + const link = fixture.debugElement.query(By.css('a')); + expect(link.nativeElement.innerHTML).toContain('button'); + }); + }); + + }); +}); + +// declare a test component +@Component({ + selector: 'ds-test-cmp', + template: `` +}) +class TestComponent { +} diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts new file mode 100644 index 00000000000..63354e97494 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts @@ -0,0 +1,51 @@ +import { Component, Inject, Injector, OnInit } from '@angular/core'; +import { MenuID } from 'src/app/shared/menu/initial-menus-state'; +import { rendersSectionForMenu } from 'src/app/shared/menu/menu-section.decorator'; +import { MenuSectionComponent } from 'src/app/shared/menu/menu-section/menu-section.component'; +import { MenuService } from '../../../menu/menu.service'; +import { MenuSection } from '../../../menu/menu.reducer'; +import { isNotEmpty } from '../../../empty.util'; + +/** + * Represents a non-expandable section in the dso edit menus + */ +@Component({ + /* tslint:disable:component-selector */ + selector: 'ds-dso-edit-menu-section', + templateUrl: './dso-edit-menu-section.component.html', + styleUrls: ['./dso-edit-menu-section.component.scss'] +}) +@rendersSectionForMenu(MenuID.DSO_EDIT, false) +export class DsoEditMenuSectionComponent extends MenuSectionComponent implements OnInit { + + menuID: MenuID = MenuID.DSO_EDIT; + itemModel; + hasLink: boolean; + canActivate: boolean; + + constructor( + @Inject('sectionDataProvider') menuSection: MenuSection, + protected menuService: MenuService, + protected injector: Injector, + ) { + super(menuSection, menuService, injector); + this.itemModel = menuSection.model; + } + + ngOnInit(): void { + this.hasLink = isNotEmpty(this.itemModel?.link); + this.canActivate = isNotEmpty(this.itemModel?.function); + super.ngOnInit(); + } + + /** + * Activate the section's model funtion + */ + public activate(event: any) { + event.preventDefault(); + if (!this.itemModel.disabled) { + this.itemModel.function(); + } + event.stopPropagation(); + } +} diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.html new file mode 100644 index 00000000000..9ef6fff1dc2 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.html @@ -0,0 +1,6 @@ +
+
+ +
+
diff --git a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.scss similarity index 100% rename from src/app/item-page/simple/item-types/versioned-item/versioned-item.component.html rename to src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.scss diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.spec.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.spec.ts new file mode 100644 index 00000000000..f2fb656eccf --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.spec.ts @@ -0,0 +1,78 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { of as observableOf } from 'rxjs'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ActivatedRoute } from '@angular/router'; +import { DsoEditMenuComponent } from './dso-edit-menu.component'; +import { MenuServiceStub } from '../../testing/menu-service.stub'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { AuthService } from '../../../core/auth/auth.service'; +import { AuthServiceStub } from '../../testing/auth-service.stub'; +import { MenuService } from '../../menu/menu.service'; +import { + ExpandableNavbarSectionComponent +} from '../../../navbar/expandable-navbar-section/expandable-navbar-section.component'; +import { MenuItemModel } from '../../menu/menu-item/models/menu-item.model'; + +describe('DsoEditMenuComponent', () => { + let comp: DsoEditMenuComponent; + let fixture: ComponentFixture; + const menuService = new MenuServiceStub(); + let authorizationService: AuthorizationDataService; + + const routeStub = { + children: [] + }; + + const section = { + id: 'edit-dso', + active: false, + visible: true, + model: { + type: null, + disabled: false, + } as MenuItemModel, + icon: 'pencil-alt', + index: 1 + }; + + + beforeEach(waitForAsync(() => { + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + spyOn(menuService, 'getMenuTopSections').and.returnValue(observableOf([section])); + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule], + declarations: [DsoEditMenuComponent], + providers: [ + Injector, + {provide: MenuService, useValue: menuService}, + {provide: AuthService, useClass: AuthServiceStub}, + {provide: ActivatedRoute, useValue: routeStub}, + {provide: AuthorizationDataService, useValue: authorizationService}, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ExpandableNavbarSectionComponent, { + set: { + changeDetection: ChangeDetectionStrategy.Default, + } + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DsoEditMenuComponent); + comp = fixture.componentInstance; + comp.sections = observableOf([]); + fixture.detectChanges(); + }); + + describe('onInit', () => { + it('should create', () => { + expect(comp).toBeTruthy(); + }); + }); +}); + diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts new file mode 100644 index 00000000000..d754e07b328 --- /dev/null +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts @@ -0,0 +1,36 @@ +import { Component, Injector } from '@angular/core'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { MenuID } from '../../menu/initial-menus-state'; +import { MenuComponent } from '../../menu/menu.component'; +import { MenuService } from '../../menu/menu.service'; +import { ActivatedRoute } from '@angular/router'; +import { AuthService } from '../../../core/auth/auth.service'; + +/** + * Component representing the edit menu and other menus on the dspace object pages + */ +@Component({ + selector: 'ds-dso-edit-menu', + styleUrls: ['./dso-edit-menu.component.scss'], + templateUrl: './dso-edit-menu.component.html', +}) +export class DsoEditMenuComponent extends MenuComponent { + /** + * The menu ID of this component is DSO_EDIT + * @type {MenuID.DSO_EDIT} + */ + menuID = MenuID.DSO_EDIT; + + constructor(protected menuService: MenuService, + protected injector: Injector, + public authorizationService: AuthorizationDataService, + public route: ActivatedRoute, + private authService: AuthService, + ) { + super(menuService, injector, authorizationService, route); + } + + ngOnInit(): void { + super.ngOnInit(); + } +} diff --git a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.html b/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.html deleted file mode 100644 index d680c140d8e..00000000000 --- a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.html +++ /dev/null @@ -1,7 +0,0 @@ -
- - diff --git a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.scss b/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.scss deleted file mode 100644 index e8b7d689a35..00000000000 --- a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.btn-dark { - background-color: var(--ds-admin-sidebar-bg); -} diff --git a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.spec.ts b/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.spec.ts deleted file mode 100644 index 5949a98f711..00000000000 --- a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { DsoPageEditButtonComponent } from './dso-page-edit-button.component'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { Item } from '../../../core/shared/item.model'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; -import { of as observableOf } from 'rxjs'; -import { TranslateModule } from '@ngx-translate/core'; -import { RouterTestingModule } from '@angular/router/testing'; -import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; -import { By } from '@angular/platform-browser'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; - -describe('DsoPageEditButtonComponent', () => { - let component: DsoPageEditButtonComponent; - let fixture: ComponentFixture; - - let authorizationService: AuthorizationDataService; - let dso: DSpaceObject; - - beforeEach(waitForAsync(() => { - dso = Object.assign(new Item(), { - id: 'test-item', - _links: { - self: { href: 'test-item-selflink' } - } - }); - authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) - }); - TestBed.configureTestingModule({ - declarations: [DsoPageEditButtonComponent], - imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule], - providers: [ - { provide: AuthorizationDataService, useValue: authorizationService } - ] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(DsoPageEditButtonComponent); - component = fixture.componentInstance; - component.dso = dso; - component.pageRoute = 'test'; - fixture.detectChanges(); - }); - - it('should check the authorization of the current user', () => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanEditMetadata, dso.self); - }); - - describe('when the user is authorized', () => { - beforeEach(() => { - (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(true)); - component.ngOnInit(); - fixture.detectChanges(); - }); - - it('should render a link', () => { - const link = fixture.debugElement.query(By.css('a')); - expect(link).not.toBeNull(); - }); - }); - - describe('when the user is not authorized', () => { - beforeEach(() => { - (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false)); - component.ngOnInit(); - fixture.detectChanges(); - }); - - it('should not render a link', () => { - const link = fixture.debugElement.query(By.css('a')); - expect(link).toBeNull(); - }); - }); -}); diff --git a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.ts b/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.ts deleted file mode 100644 index 1879581d23d..00000000000 --- a/src/app/shared/dso-page/dso-page-edit-button/dso-page-edit-button.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; -import { Observable } from 'rxjs'; -import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; - -@Component({ - selector: 'ds-dso-page-edit-button', - templateUrl: './dso-page-edit-button.component.html', - styleUrls: ['./dso-page-edit-button.component.scss'] -}) -/** - * Display a button linking to the edit page of a DSpaceObject - */ -export class DsoPageEditButtonComponent implements OnInit { - /** - * The DSpaceObject to display a button to the edit page for - */ - @Input() dso: DSpaceObject; - - /** - * The prefix of the route to the edit page (before the object's UUID, e.g. "items") - */ - @Input() pageRoute: string; - - /** - * A message for the tooltip on the button - * Supports i18n keys - */ - @Input() tooltipMsg: string; - - /** - * Whether or not the current user is authorized to edit the DSpaceObject - */ - isAuthorized$: Observable; - - constructor(protected authorizationService: AuthorizationDataService) { } - - ngOnInit() { - this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, this.dso.self); - } - -} diff --git a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.html b/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.html deleted file mode 100644 index 0e2e35dcb7d..00000000000 --- a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.html +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.scss b/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.scss deleted file mode 100644 index e8b7d689a35..00000000000 --- a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.btn-dark { - background-color: var(--ds-admin-sidebar-bg); -} diff --git a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.spec.ts b/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.spec.ts deleted file mode 100644 index 9839507d576..00000000000 --- a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { DsoPageVersionButtonComponent } from './dso-page-version-button.component'; -import { Item } from '../../../core/shared/item.model'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; -import { Observable, of, of as observableOf } from 'rxjs'; -import { TranslateModule } from '@ngx-translate/core'; -import { RouterTestingModule } from '@angular/router/testing'; -import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; -import { By } from '@angular/platform-browser'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; - -describe('DsoPageVersionButtonComponent', () => { - let component: DsoPageVersionButtonComponent; - let fixture: ComponentFixture; - - let authorizationService: AuthorizationDataService; - let versionHistoryService: VersionHistoryDataService; - - let dso: Item; - let tooltipMsg: Observable; - - const authorizationServiceSpy = jasmine.createSpyObj('authorizationService', ['isAuthorized']); - - const versionHistoryServiceSpy = jasmine.createSpyObj('versionHistoryService', - ['getVersions', 'getLatestVersionFromHistory$', 'isLatest$', 'hasDraftVersion$'] - ); - - beforeEach(waitForAsync(() => { - dso = Object.assign(new Item(), { - id: 'test-item', - _links: { - self: { href: 'test-item-selflink' }, - version: { href: 'test-item-version-selflink' }, - }, - }); - tooltipMsg = of('tooltip-msg'); - - TestBed.configureTestingModule({ - declarations: [DsoPageVersionButtonComponent], - imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule], - providers: [ - { provide: AuthorizationDataService, useValue: authorizationServiceSpy }, - { provide: VersionHistoryDataService, useValue: versionHistoryServiceSpy }, - ] - }).compileComponents(); - - authorizationService = TestBed.inject(AuthorizationDataService); - versionHistoryService = TestBed.inject(VersionHistoryDataService); - - versionHistoryServiceSpy.hasDraftVersion$.and.returnValue(observableOf(true)); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(DsoPageVersionButtonComponent); - component = fixture.componentInstance; - component.dso = dso; - component.tooltipMsg$ = tooltipMsg; - fixture.detectChanges(); - }); - - it('should check the authorization of the current user', () => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanCreateVersion, dso.self); - }); - - it('should check if the item has a draft version', () => { - expect(versionHistoryServiceSpy.hasDraftVersion$).toHaveBeenCalledWith(dso._links.version.href); - }); - - describe('when the user is authorized', () => { - beforeEach(() => { - authorizationServiceSpy.isAuthorized.and.returnValue(observableOf(true)); - component.ngOnInit(); - fixture.detectChanges(); - }); - - it('should render a button', () => { - const button = fixture.debugElement.query(By.css('button')); - expect(button).not.toBeNull(); - }); - }); - - describe('when the user is not authorized', () => { - beforeEach(() => { - authorizationServiceSpy.isAuthorized.and.returnValue(observableOf(false)); - component.ngOnInit(); - fixture.detectChanges(); - }); - - it('should render a button', () => { - const button = fixture.debugElement.query(By.css('button')); - expect(button).toBeNull(); - }); - }); - -}); diff --git a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.ts b/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.ts deleted file mode 100644 index cf07953c757..00000000000 --- a/src/app/shared/dso-page/dso-page-version-button/dso-page-version-button.component.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; -import { Observable, of } from 'rxjs'; -import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; -import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; -import { Item } from '../../../core/shared/item.model'; -import { map, startWith, switchMap } from 'rxjs/operators'; - -@Component({ - selector: 'ds-dso-page-version-button', - templateUrl: './dso-page-version-button.component.html', - styleUrls: ['./dso-page-version-button.component.scss'] -}) -/** - * Display a button linking to the edit page of a DSpaceObject - */ -export class DsoPageVersionButtonComponent implements OnInit { - /** - * The item for which display a button to create a new version - */ - @Input() dso: Item; - - /** - * A message for the tooltip on the button - * Supports i18n keys - */ - @Input() tooltipMsgCreate: string; - - /** - * A message for the tooltip on the button (when is disabled) - * Supports i18n keys - */ - @Input() tooltipMsgHasDraft: string; - - /** - * Emits an event that triggers the creation of the new version - */ - @Output() newVersionEvent = new EventEmitter(); - - /** - * Whether or not the current user is authorized to create a new version of the DSpaceObject - */ - isAuthorized$: Observable; - - disableNewVersionButton$: Observable; - - tooltipMsg$: Observable; - - constructor( - protected authorizationService: AuthorizationDataService, - protected versionHistoryService: VersionHistoryDataService, - ) { - } - - /** - * Creates a new version for the current item - */ - createNewVersion() { - this.newVersionEvent.emit(); - } - - ngOnInit() { - this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, this.dso.self); - - this.disableNewVersionButton$ = this.versionHistoryService.hasDraftVersion$(this.dso._links.version.href).pipe( - // button is disabled if hasDraftVersion = true, and enabled if hasDraftVersion = false or null - // (hasDraftVersion is null when a version history does not exist) - map((res) => Boolean(res)), - startWith(true), - ); - - this.tooltipMsg$ = this.disableNewVersionButton$.pipe( - switchMap((hasDraftVersion) => of(hasDraftVersion ? this.tooltipMsgHasDraft : this.tooltipMsgCreate)), - ); - } - -} diff --git a/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts b/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts new file mode 100644 index 00000000000..0a02481d583 --- /dev/null +++ b/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts @@ -0,0 +1,92 @@ +import { DsoVersioningModalService } from './dso-versioning-modal.service'; +import { waitForAsync } from '@angular/core/testing'; +import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { Version } from '../../../core/shared/version.model'; +import { Item } from '../../../core/shared/item.model'; +import { MetadataMap } from '../../../core/shared/metadata.models'; +import { createRelationshipsObservable } from '../../../item-page/simple/item-types/shared/item.component.spec'; +import { buildPaginatedList } from '../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { EMPTY, of as observableOf } from 'rxjs'; + +fdescribe('DsoVersioningModalService', () => { + let service: DsoVersioningModalService; + let modalService; + let versionService; + let versionHistoryService; + let itemVersionShared; + let router; + let workspaceItemDataService; + let itemService; + + const mockItem: Item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: new MetadataMap(), + relationships: createRelationshipsObservable(), + _links: { + self: { + href: 'item-href' + }, + version: { + href: 'version-href' + } + } + }); + + beforeEach(waitForAsync(() => { + modalService = jasmine.createSpyObj('modalService', { + open: {componentInstance: {firstVersion: {}, versionNumber: {}, createVersionEvent: EMPTY}} + }); + versionService = jasmine.createSpyObj('versionService', { + findByHref: createSuccessfulRemoteDataObject$(new Version()), + }); + versionHistoryService = jasmine.createSpyObj('versionHistoryService', { + createVersion: createSuccessfulRemoteDataObject$(new Version()), + hasDraftVersion$: observableOf(false) + }); + itemVersionShared = jasmine.createSpyObj('itemVersionShared', ['notifyCreateNewVersion']); + router = jasmine.createSpyObj('router', ['navigateByUrl']); + workspaceItemDataService = jasmine.createSpyObj('workspaceItemDataService', ['findByItem']); + itemService = jasmine.createSpyObj('itemService', ['findByHref']); + + service = new DsoVersioningModalService( + modalService, + versionService, + versionHistoryService, + itemVersionShared, + router, + workspaceItemDataService, + itemService + ); + })); + describe('when onCreateNewVersion() is called', () => { + it('should call versionService.findByHref', () => { + service.openCreateVersionModal(mockItem); + expect(versionService.findByHref).toHaveBeenCalledWith('version-href'); + }); + }); + + describe('isNewVersionButtonDisabled', () => { + it('should call versionHistoryService.hasDraftVersion$', () => { + service.isNewVersionButtonDisabled(mockItem); + expect(versionHistoryService.hasDraftVersion$).toHaveBeenCalledWith(mockItem._links.version.href); + }); + }); + + describe('getVersioningTooltipMessage', () => { + it('should return the create message when isNewVersionButtonDisabled returns false', (done) => { + spyOn(service, 'isNewVersionButtonDisabled').and.returnValue(observableOf(false)); + service.getVersioningTooltipMessage(mockItem, 'draft-message', 'create-message').subscribe((message) => { + expect(message).toEqual('create-message'); + done(); + }); + }); + it('should return the draft message when isNewVersionButtonDisabled returns true', (done) => { + spyOn(service, 'isNewVersionButtonDisabled').and.returnValue(observableOf(true)); + service.getVersioningTooltipMessage(mockItem, 'draft-message', 'create-message').subscribe((message) => { + expect(message).toEqual('draft-message'); + done(); + }); + }); + }); +}); diff --git a/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.ts b/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.ts new file mode 100644 index 00000000000..e1b827a50b2 --- /dev/null +++ b/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.ts @@ -0,0 +1,98 @@ +import { + ItemVersionsSummaryModalComponent +} from '../../item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Version } from '../../../core/shared/version.model'; +import { map, startWith, switchMap, tap } from 'rxjs/operators'; +import { Item } from '../../../core/shared/item.model'; +import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { VersionDataService } from '../../../core/data/version-data.service'; +import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; +import { ItemVersionsSharedService } from '../../item/item-versions/item-versions-shared.service'; +import { Router } from '@angular/router'; +import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; +import { ItemDataService } from '../../../core/data/item-data.service'; +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; + +/** + * Service to take care of all the functionality related to the version creation modal + */ +@Injectable({ + providedIn: 'root' +}) +export class DsoVersioningModalService { + + constructor( + protected modalService: NgbModal, + protected versionService: VersionDataService, + protected versionHistoryService: VersionHistoryDataService, + protected itemVersionShared: ItemVersionsSharedService, + protected router: Router, + protected workspaceItemDataService: WorkspaceitemDataService, + protected itemService: ItemDataService, + ) { + } + + /** + * Open the create version modal for the provided dso + */ + openCreateVersionModal(dso): void { + + const item = dso; + const versionHref = item._links.version.href; + + // Open modal + const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent); + + // Show current version in modal + this.versionService.findByHref(versionHref).pipe(getFirstCompletedRemoteData()).subscribe((res: RemoteData) => { + // if res.hasNoContent then the item is unversioned + activeModal.componentInstance.firstVersion = res.hasNoContent; + activeModal.componentInstance.versionNumber = (res.hasNoContent ? undefined : res.payload.version); + }); + + // On createVersionEvent emitted create new version and notify + activeModal.componentInstance.createVersionEvent.pipe( + switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)), + getFirstCompletedRemoteData(), + // show success/failure notification + tap((res: RemoteData) => { + this.itemVersionShared.notifyCreateNewVersion(res); + }), + // get workspace item + getFirstSucceededRemoteDataPayload(), + switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)), + getFirstSucceededRemoteDataPayload(), + switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)), + getFirstSucceededRemoteDataPayload(), + ).subscribe((wsItem) => { + const wsiId = wsItem.id; + const route = 'workspaceitems/' + wsiId + '/edit'; + this.router.navigateByUrl(route); + }); + } + + /** + * Checks if the new version button should be disabled for the provided dso + */ + isNewVersionButtonDisabled(dso): Observable { + return this.versionHistoryService.hasDraftVersion$(dso._links.version.href).pipe( + // button is disabled if hasDraftVersion = true, and enabled if hasDraftVersion = false or null + // (hasDraftVersion is null when a version history does not exist) + map((res) => Boolean(res)), + startWith(true), + ); + } + + /** + * Checks and returns the tooltip that needs to be used for the create version button tooltip + */ + getVersioningTooltipMessage(dso, tooltipMsgHasDraft, tooltipMsgCreate): Observable { + return this.isNewVersionButtonDisabled(dso).pipe( + switchMap((hasDraftVersion) => of(hasDraftVersion ? tooltipMsgHasDraft : tooltipMsgCreate)), + ); + } +} diff --git a/src/app/shared/menu/initial-menus-state.ts b/src/app/shared/menu/initial-menus-state.ts index 7b900540b6f..086c7c30fa7 100644 --- a/src/app/shared/menu/initial-menus-state.ts +++ b/src/app/shared/menu/initial-menus-state.ts @@ -5,7 +5,8 @@ import { MenusState } from './menu.reducer'; */ export enum MenuID { ADMIN = 'admin-sidebar', - PUBLIC = 'public' + PUBLIC = 'public', + DSO_EDIT = 'dso-edit' } /** @@ -36,5 +37,14 @@ export const initialMenusState: MenusState = { visible: true, sections: {}, sectionToSubsectionIndex: {} - } + }, + [MenuID.DSO_EDIT]: + { + id: MenuID.DSO_EDIT, + collapsed: true, + previewCollapsed: true, + visible: false, + sections: {}, + sectionToSubsectionIndex: {} + }, }; diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.html b/src/app/shared/menu/menu-item/link-menu-item.component.html index b2cc73bcf47..4dacc14cbe6 100644 --- a/src/app/shared/menu/menu-item/link-menu-item.component.html +++ b/src/app/shared/menu/menu-item/link-menu-item.component.html @@ -1,6 +1,6 @@ {}; } diff --git a/src/app/shared/menu/menu-item/models/search.model.ts b/src/app/shared/menu/menu-item/models/search.model.ts index e8eeda5501e..d2f728ba9fb 100644 --- a/src/app/shared/menu/menu-item/models/search.model.ts +++ b/src/app/shared/menu/menu-item/models/search.model.ts @@ -6,6 +6,7 @@ import { MenuItemModel } from './menu-item.model'; */ export class SearchMenuItemModel implements MenuItemModel { type = MenuItemType.SEARCH; + disabled: boolean; placeholder: string; action: string; } diff --git a/src/app/shared/menu/menu-item/models/text.model.ts b/src/app/shared/menu/menu-item/models/text.model.ts index bbaf7804d99..d50ec821adc 100644 --- a/src/app/shared/menu/menu-item/models/text.model.ts +++ b/src/app/shared/menu/menu-item/models/text.model.ts @@ -6,5 +6,6 @@ import { MenuItemModel } from './menu-item.model'; */ export class TextMenuItemModel implements MenuItemModel { type = MenuItemType.TEXT; + disabled: boolean; text: string; } diff --git a/src/app/shared/menu/menu-item/onclick-menu-item.component.html b/src/app/shared/menu/menu-item/onclick-menu-item.component.html index fd0192ad5fc..5e8aecbd441 100644 --- a/src/app/shared/menu/menu-item/onclick-menu-item.component.html +++ b/src/app/shared/menu/menu-item/onclick-menu-item.component.html @@ -1,4 +1,5 @@ -{{item.text | translate}} +{{item.text | translate}} diff --git a/src/app/shared/menu/menu-item/onclick-menu-item.component.ts b/src/app/shared/menu/menu-item/onclick-menu-item.component.ts index 002bfbc8192..ba47e2ad4a8 100644 --- a/src/app/shared/menu/menu-item/onclick-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/onclick-menu-item.component.ts @@ -14,13 +14,16 @@ import { OnClickMenuItemModel } from './models/onclick.model'; @rendersMenuItemForType(MenuItemType.ONCLICK) export class OnClickMenuItemComponent { item: OnClickMenuItemModel; + constructor(@Inject('itemModelProvider') item: OnClickMenuItemModel) { this.item = item; } public activate(event: any) { - event.preventDefault(); - this.item.function(); - event.stopPropagation(); + if (!this.item.disabled) { + event.preventDefault(); + this.item.function(); + event.stopPropagation(); + } } } diff --git a/src/app/shared/menu/menu-item/text-menu-item.component.html b/src/app/shared/menu/menu-item/text-menu-item.component.html index 7ba353e5e74..11c44204026 100644 --- a/src/app/shared/menu/menu-item/text-menu-item.component.html +++ b/src/app/shared/menu/menu-item/text-menu-item.component.html @@ -1 +1 @@ -{{item.text | translate}} \ No newline at end of file +{{item.text | translate}} diff --git a/src/app/shared/menu/menu-section/menu-section.component.ts b/src/app/shared/menu/menu-section/menu-section.component.ts index fcd96c65f1d..8c845a58d68 100644 --- a/src/app/shared/menu/menu-section/menu-section.component.ts +++ b/src/app/shared/menu/menu-section/menu-section.component.ts @@ -64,7 +64,9 @@ export class MenuSectionComponent implements OnInit, OnDestroy { */ toggleSection(event: Event) { event.preventDefault(); - this.menuService.toggleActiveSection(this.menuID, this.section.id); + if (!this.section.model?.disabled) { + this.menuService.toggleActiveSection(this.menuID, this.section.id); + } } /** @@ -73,7 +75,9 @@ export class MenuSectionComponent implements OnInit, OnDestroy { */ activateSection(event: Event) { event.preventDefault(); - this.menuService.activateSection(this.menuID, this.section.id); + if (!this.section.model?.disabled) { + this.menuService.activateSection(this.menuID, this.section.id); + } } /** diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 715ee66a99b..0454646c96c 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -157,8 +157,6 @@ import { SidebarSearchListElementComponent } from './object-list/sidebar-search- import { CollectionSidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/collection/collection-sidebar-search-list-element.component'; import { CommunitySidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/community/community-sidebar-search-list-element.component'; import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component'; -import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component'; -import { DsoPageVersionButtonComponent } from './dso-page/dso-page-version-button/dso-page-version-button.component'; import { HoverClassDirective } from './hover-class.directive'; import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component'; import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component'; @@ -177,6 +175,13 @@ import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/ import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component'; import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; +import { + DsoEditMenuSectionComponent +} from './dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component'; +import { DsoEditMenuComponent } from './dso-page/dso-edit-menu/dso-edit-menu.component'; +import { + DsoEditMenuExpandableSectionComponent +} from './dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -402,18 +407,20 @@ const ENTRY_COMPONENTS = [ OnClickMenuItemComponent, TextMenuItemComponent, ScopeSelectorModalComponent, + DsoEditMenuSectionComponent, + DsoEditMenuExpandableSectionComponent, ]; const SHARED_ITEM_PAGE_COMPONENTS = [ MetadataFieldWrapperComponent, MetadataValuesComponent, - DsoPageEditButtonComponent, - DsoPageVersionButtonComponent, ItemAlertsComponent, GenericItemPageFieldComponent, MetadataRepresentationListComponent, RelatedItemsComponent, - + DsoEditMenuSectionComponent, + DsoEditMenuComponent, + DsoEditMenuExpandableSectionComponent, ]; const PROVIDERS = [ From 96ba1c931992340dbc5e611e26a9acf9b736ad2b Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 21 Sep 2022 14:28:53 +0200 Subject: [PATCH 023/758] 94390: Implement feedback --- .../admin-sidebar-section.component.ts | 2 +- src/app/collection-page/collection-page-routing.module.ts | 1 - src/app/community-page/community-page-routing.module.ts | 1 - src/app/item-page/item-page-routing.module.ts | 1 - .../dso-edit-menu-expandable-section.component.scss | 4 ++-- .../dso-page/dso-edit-menu/dso-edit-menu.component.ts | 6 +----- 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts index 620681c654d..97b16c05225 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts @@ -49,7 +49,7 @@ export class AdminSidebarSectionComponent extends MenuSectionComponent implement navigate(event: any): void { event.preventDefault(); - if (this.isDisabled) { + if (!this.isDisabled) { this.router.navigate(this.itemModel.link); } } diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index c5759f833fa..3101347990e 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -93,7 +93,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; LinkService, CreateCollectionPageGuard, CollectionPageAdministratorGuard, - DSOEditMenuResolver ] }) export class CollectionPageRoutingModule { diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index ac90f3618c5..2c97c99081e 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -75,7 +75,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; LinkService, CreateCommunityPageGuard, CommunityPageAdministratorGuard, - DSOEditMenuResolver ] }) export class CommunityPageRoutingModule { diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 1c4da9a2672..069936ebd37 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -90,7 +90,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; LinkService, ItemPageAdministratorGuard, VersionResolver, - DSOEditMenuResolver ] }) diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss index 61ec6347f95..ad6166398ca 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.scss @@ -16,11 +16,11 @@ ul.dropdown-menu { color: white; &.disabled { - color: $btn-link-disabled-color; + color: var(--bs-btn-link-disabled-color); } } .disabled { - color: $btn-link-disabled-color; + color: var(--bs-btn-link-disabled-color); } } diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts index d754e07b328..f222966ed85 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts @@ -24,13 +24,9 @@ export class DsoEditMenuComponent extends MenuComponent { constructor(protected menuService: MenuService, protected injector: Injector, public authorizationService: AuthorizationDataService, - public route: ActivatedRoute, - private authService: AuthService, + public route: ActivatedRoute ) { super(menuService, injector, authorizationService, route); } - ngOnInit(): void { - super.ngOnInit(); - } } From a92060c45ac9b2caca449a0a9f75fadde8997543 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 22 Sep 2022 12:15:34 +0200 Subject: [PATCH 024/758] 94390: Add comment to array flattening --- src/app/shared/dso-page/dso-edit-menu.resolver.ts | 1 + 1 file changed, 1 insertion(+) 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 003e61df954..d4615843585 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -45,6 +45,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection if (dsoRD.hasSucceeded) { const dso = dsoRD.payload; return combineLatest(this.getDsoMenus(dso, route, state)).pipe( + // Menu sections are retrieved as an array of arrays and flattened into a single array map((combinedMenus) => [].concat.apply([], combinedMenus)), map((menus) => this.addDsoUuidToMenuIDs(menus, dso)), map((menus) => { From b7c1e76b7f70894660843285d48324226d220a91 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 22 Sep 2022 16:10:43 +0200 Subject: [PATCH 025/758] 94390: Remove fdescribe --- .../dso-versioning-modal.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts b/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts index 0a02481d583..fc5c1dafc97 100644 --- a/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts +++ b/src/app/shared/dso-page/dso-versioning-modal-service/dso-versioning-modal.service.spec.ts @@ -9,7 +9,7 @@ import { buildPaginatedList } from '../../../core/data/paginated-list.model'; import { PageInfo } from '../../../core/shared/page-info.model'; import { EMPTY, of as observableOf } from 'rxjs'; -fdescribe('DsoVersioningModalService', () => { +describe('DsoVersioningModalService', () => { let service: DsoVersioningModalService; let modalService; let versionService; From d68f38e848ec06c02f4d9445fe1c9fdbea764fa8 Mon Sep 17 00:00:00 2001 From: lotte Date: Thu, 22 Sep 2022 17:07:09 +0200 Subject: [PATCH 026/758] 94233: Fixed tests --- src/app/shared/sass-helper/css-variable.actions.ts | 4 +--- src/app/shared/sass-helper/css-variable.reducer.ts | 2 +- src/app/shared/sass-helper/css-variable.service.ts | 3 +-- src/app/shared/testing/css-variable-service.stub.ts | 13 +++++++++++++ src/modules/app/server-init.service.ts | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/app/shared/sass-helper/css-variable.actions.ts b/src/app/shared/sass-helper/css-variable.actions.ts index 93225f94262..2d58a2978b8 100644 --- a/src/app/shared/sass-helper/css-variable.actions.ts +++ b/src/app/shared/sass-helper/css-variable.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { type } from '../ngrx/type'; import { KeyValuePair } from '../key-value-pair.model'; @@ -38,9 +39,6 @@ export class AddAllCSSVariablesAction implements Action { export class ClearCSSVariablesAction implements Action { type = CSSVariableActionTypes.CLEAR; - - constructor() { - } } export type CSSVariableAction = AddCSSVariableAction | AddAllCSSVariablesAction | ClearCSSVariablesAction; diff --git a/src/app/shared/sass-helper/css-variable.reducer.ts b/src/app/shared/sass-helper/css-variable.reducer.ts index 405cbf5df41..449a936b4e2 100644 --- a/src/app/shared/sass-helper/css-variable.reducer.ts +++ b/src/app/shared/sass-helper/css-variable.reducer.ts @@ -19,7 +19,7 @@ export function cssVariablesReducer(state = initialState, action: CSSVariableAct return Object.assign({}, state, { [variable.name]: variable.value }); } case CSSVariableActionTypes.ADD_ALL: { const variables = action.payload; - return Object.assign({}, state, ...variables.map(({ key, value }: KeyValuePair) => {return {[key]: value}})); + return Object.assign({}, state, ...variables.map(({ key, value }: KeyValuePair) => {return {[key]: value};})); } case CSSVariableActionTypes.CLEAR: { return initialState; } diff --git a/src/app/shared/sass-helper/css-variable.service.ts b/src/app/shared/sass-helper/css-variable.service.ts index 9ba9dfca3c9..af40c634fd6 100644 --- a/src/app/shared/sass-helper/css-variable.service.ts +++ b/src/app/shared/sass-helper/css-variable.service.ts @@ -87,8 +87,7 @@ export class CSSVariableService { * @return array> * ex; [{key: "--color-accent", value: "#b9f500"}, {key: "--color-text", value: "#252525"}, ...] */ - getCSSVariablesFromStylesheets(document: Document): KeyValuePair[] - { + getCSSVariablesFromStylesheets(document: Document): KeyValuePair[] { // styleSheets is array-like, so we convert it to an array. // Filter out any stylesheets not on this domain return [...document.styleSheets] diff --git a/src/app/shared/testing/css-variable-service.stub.ts b/src/app/shared/testing/css-variable-service.stub.ts index f72e3384553..47be5c0ff47 100644 --- a/src/app/shared/testing/css-variable-service.stub.ts +++ b/src/app/shared/testing/css-variable-service.stub.ts @@ -1,4 +1,5 @@ import { Observable, of as observableOf } from 'rxjs'; +import { KeyValuePair } from '../key-value-pair.model'; const variables = { '--bs-sm-min': '576px,', @@ -19,4 +20,16 @@ export class CSSVariableServiceStub { addCSSVariable(name: string, value: string): void { /**/ } + + addCSSVariables(variables: KeyValuePair[]): void { + /**/ + } + + clearCSSVariables(): void { + /**/ + } + + getCSSVariablesFromStylesheets(document: Document): void { + /**/ + } } diff --git a/src/modules/app/server-init.service.ts b/src/modules/app/server-init.service.ts index e93c692cd7f..9f6aa65921e 100644 --- a/src/modules/app/server-init.service.ts +++ b/src/modules/app/server-init.service.ts @@ -18,7 +18,7 @@ import { LocaleService } from '../../app/core/locale/locale.service'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { MetadataService } from '../../app/core/metadata/metadata.service'; import { BreadcrumbsService } from '../../app/breadcrumbs/breadcrumbs.service'; -import { CSSVariableService } from '../../app/shared/sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../sass-helper/css-variable.service'; import { ThemeService } from '../../app/shared/theme-support/theme.service'; import { take } from 'rxjs/operators'; From 97bceffb0272d095f1c1826b2daabca26034c4a4 Mon Sep 17 00:00:00 2001 From: lotte Date: Thu, 22 Sep 2022 17:14:55 +0200 Subject: [PATCH 027/758] 94233: Fixed lint issue --- src/app/shared/testing/css-variable-service.stub.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/testing/css-variable-service.stub.ts b/src/app/shared/testing/css-variable-service.stub.ts index 47be5c0ff47..2f5c6479457 100644 --- a/src/app/shared/testing/css-variable-service.stub.ts +++ b/src/app/shared/testing/css-variable-service.stub.ts @@ -21,7 +21,7 @@ export class CSSVariableServiceStub { /**/ } - addCSSVariables(variables: KeyValuePair[]): void { + addCSSVariables(variablesToAdd: KeyValuePair[]): void { /**/ } From f7619249630f9df9bf5731eb7793ecbc27551d19 Mon Sep 17 00:00:00 2001 From: lotte Date: Thu, 22 Sep 2022 17:16:35 +0200 Subject: [PATCH 028/758] 94233: removed empty file --- src/app/shared/sass-helper/css-variable.utils.ts | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/app/shared/sass-helper/css-variable.utils.ts diff --git a/src/app/shared/sass-helper/css-variable.utils.ts b/src/app/shared/sass-helper/css-variable.utils.ts deleted file mode 100644 index 05e3074f98a..00000000000 --- a/src/app/shared/sass-helper/css-variable.utils.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Uses code from https://css-tricks.com/how-to-get-all-custom-properties-on-a-page-in-javascript/ - -import { KeyValuePair } from '../key-value-pair.model'; - From af982471953836051dd51fb391929f5b3f8c38a3 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Thu, 22 Sep 2022 18:18:42 +0200 Subject: [PATCH 029/758] add check to ensure document.styleSheets is defined --- src/app/core/core.module.ts | 2 - .../sass-helper/css-variable.service.ts | 60 ++++++++++--------- src/modules/app/server-init.service.ts | 2 - 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index f80a2a020e2..90cefd54c70 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -23,7 +23,6 @@ import { NotificationsService } from '../shared/notifications/notifications.serv import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service'; import { ObjectSelectService } from '../shared/object-select/object-select.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { CSSVariableService } from '../shared/sass-helper/css-variable.service'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { UploaderService } from '../shared/uploader/uploader.service'; import { SectionFormOperationsService } from '../submission/sections/form/section-form-operations.service'; @@ -257,7 +256,6 @@ const PROVIDERS = [ DefaultChangeAnalyzer, ArrayMoveChangeAnalyzer, ObjectSelectService, - CSSVariableService, MenuService, ObjectUpdatesService, SearchService, diff --git a/src/app/shared/sass-helper/css-variable.service.ts b/src/app/shared/sass-helper/css-variable.service.ts index af40c634fd6..0190a05036f 100644 --- a/src/app/shared/sass-helper/css-variable.service.ts +++ b/src/app/shared/sass-helper/css-variable.service.ts @@ -5,7 +5,7 @@ import { AddAllCSSVariablesAction, AddCSSVariableAction, ClearCSSVariablesAction import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { Observable } from 'rxjs'; -import { hasValue } from '../empty.util'; +import { hasValue, isNotEmpty } from '../empty.util'; import { KeyValuePair } from '../key-value-pair.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { CSSVariablesState } from './css-variable.reducer'; @@ -13,7 +13,9 @@ import { CSSVariablesState } from './css-variable.reducer'; /** * This service deals with adding and retrieving CSS variables to and from the store */ -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class CSSVariableService { isSameDomain = (styleSheet) => { // Internal style blocks won't have an href value @@ -88,31 +90,35 @@ export class CSSVariableService { * ex; [{key: "--color-accent", value: "#b9f500"}, {key: "--color-text", value: "#252525"}, ...] */ getCSSVariablesFromStylesheets(document: Document): KeyValuePair[] { - // styleSheets is array-like, so we convert it to an array. - // Filter out any stylesheets not on this domain - return [...document.styleSheets] - .filter(this.isSameDomain) - .reduce( - (finalArr, sheet) => - finalArr.concat( - // cssRules is array-like, so we convert it to an array - [...sheet.cssRules].filter(this.isStyleRule).reduce((propValArr, rule: any) => { - const props = [...rule.style] - .map((propName) => { - return { - key: propName.trim(), - value: rule.style.getPropertyValue(propName).trim() - } as KeyValuePair; - } - ) - // Discard any props that don't start with "--". Custom props are required to. - .filter(({ key }: KeyValuePair) => key.indexOf('--') === 0); - - return [...propValArr, ...props]; - }, []) - ), - [] - ); + if (isNotEmpty(document.styleSheets)) { + // styleSheets is array-like, so we convert it to an array. + // Filter out any stylesheets not on this domain + return [...document.styleSheets] + .filter(this.isSameDomain) + .reduce( + (finalArr, sheet) => + finalArr.concat( + // cssRules is array-like, so we convert it to an array + [...sheet.cssRules].filter(this.isStyleRule).reduce((propValArr, rule: any) => { + const props = [...rule.style] + .map((propName) => { + return { + key: propName.trim(), + value: rule.style.getPropertyValue(propName).trim() + } as KeyValuePair; + } + ) + // Discard any props that don't start with "--". Custom props are required to. + .filter(({ key }: KeyValuePair) => key.indexOf('--') === 0); + + return [...propValArr, ...props]; + }, []) + ), + [] + ); + } else { + return []; + } } } diff --git a/src/modules/app/server-init.service.ts b/src/modules/app/server-init.service.ts index 9f6aa65921e..903bd91b7ce 100644 --- a/src/modules/app/server-init.service.ts +++ b/src/modules/app/server-init.service.ts @@ -18,7 +18,6 @@ import { LocaleService } from '../../app/core/locale/locale.service'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { MetadataService } from '../../app/core/metadata/metadata.service'; import { BreadcrumbsService } from '../../app/breadcrumbs/breadcrumbs.service'; -import { CSSVariableService } from '../../sass-helper/css-variable.service'; import { ThemeService } from '../../app/shared/theme-support/theme.service'; import { take } from 'rxjs/operators'; @@ -37,7 +36,6 @@ export class ServerInitService extends InitService { protected angulartics2DSpace: Angulartics2DSpace, protected metadata: MetadataService, protected breadcrumbsService: BreadcrumbsService, - protected cssService: CSSVariableService, protected themeService: ThemeService, ) { super( From 738a85ffc9e464dc787c9bad1fb06b8060c97538 Mon Sep 17 00:00:00 2001 From: myrza1 Date: Fri, 23 Sep 2022 05:23:30 +0600 Subject: [PATCH 030/758] Update kk.json5 Update part 1 --- src/assets/i18n/kk.json5 | 773 +++++++++++++-------------------------- 1 file changed, 263 insertions(+), 510 deletions(-) diff --git a/src/assets/i18n/kk.json5 b/src/assets/i18n/kk.json5 index 56651ceaae4..b78df8ac5e5 100644 --- a/src/assets/i18n/kk.json5 +++ b/src/assets/i18n/kk.json5 @@ -211,8 +211,7 @@ "admin.registries.bitstream-formats.table.supportLevel.head": "Қолдау деңгейі", // "admin.registries.bitstream-formats.title": "Bitstream Format Registry", - // TODO Source message changed - Revise the translation - "admin.registries.bitstream-formats.title": "DSpace Angular ::Бит ағыны форматтарының тізілімі", + "admin.registries.bitstream-formats.title": "Бит ағыны форматтарының тізілімі", @@ -253,8 +252,7 @@ "admin.registries.metadata.schemas.table.namespace": "Аттар кеңістігі", // "admin.registries.metadata.title": "Metadata Registry", - // TODO Source message changed - Revise the translation - "admin.registries.metadata.title": "DSpace Angular :: Метадеректер тізілімі", + "admin.registries.metadata.title": "Метадеректер тізілімі", @@ -325,15 +323,13 @@ "admin.registries.schema.notification.field.edited": "Сәтті өңделген метадеректер өрісі\"{{field}}\"", // "admin.registries.schema.notification.success": "Success", - "admin.registries.schema.notification.success": "Успех", + "admin.registries.schema.notification.success": "Сәтті", // "admin.registries.schema.return": "Back", - // TODO Source message changed - Revise the translation "admin.registries.schema.return": "Қайтару", // "admin.registries.schema.title": "Metadata Schema Registry", - // TODO Source message changed - Revise the translation - "admin.registries.schema.title": "DSpace Angular:: метадеректер тізбегінің тізілімі", + "admin.registries.schema.title": "Метадеректер тізбегінің тізілімі", @@ -350,12 +346,10 @@ "admin.access-control.epeople.actions.stop-impersonating": "Адам туралы ойлауды доғарыңыз", // "admin.access-control.epeople.breadcrumbs": "EPeople", - // TODO New key - Add a translation - "admin.access-control.epeople.breadcrumbs": "EPeople", + "admin.access-control.epeople.breadcrumbs": "Адамдар", // "admin.access-control.epeople.title": "EPeople", - // TODO Source message changed - Revise the translation - "admin.access-control.epeople.title": "DSpace Angular :: Адамдар", + "admin.access-control.epeople.title": "Адамдар", // "admin.access-control.epeople.head": "EPeople", "admin.access-control.epeople.head": "Адамдар", @@ -398,8 +392,7 @@ "admin.access-control.epeople.table.edit.buttons.edit": "Өңдеу \"{{name}}\"", // "admin.access-control.epeople.table.edit.buttons.edit-disabled": "You are not authorized to edit this group", - // TODO New key - Add a translation - "admin.access-control.epeople.table.edit.buttons.edit-disabled": "You are not authorized to edit this group", + "admin.access-control.epeople.table.edit.buttons.edit-disabled": "Сіз бұл топты өңдеуге құқығыңыз жоқ", // "admin.access-control.epeople.table.edit.buttons.remove": "Delete \"{{name}}\"", "admin.access-control.epeople.table.edit.buttons.remove": "Жою\"{{name}}\"", @@ -432,8 +425,7 @@ "admin.access-control.epeople.form.requireCertificate": "Сертификат қажет", // "admin.access-control.epeople.form.return": "Back", - // TODO New key - Add a translation - "admin.access-control.epeople.form.return": "Back", + "admin.access-control.epeople.form.return": "Артқа", // "admin.access-control.epeople.form.notification.created.success": "Successfully created EPerson \"{{name}}\"", "admin.access-control.epeople.form.notification.created.success": "Сәтті құрылған Эпперсон \"{{name}}\"", @@ -469,8 +461,7 @@ "admin.access-control.epeople.form.table.name": "Аты", // "admin.access-control.epeople.form.table.collectionOrCommunity": "Collection/Community", - // TODO New key - Add a translation - "admin.access-control.epeople.form.table.collectionOrCommunity": "Collection/Community", + "admin.access-control.epeople.form.table.collectionOrCommunity": "Жинақ/Қауымдастық", // "admin.access-control.epeople.form.memberOfNoGroups": "This EPerson is not a member of any groups", "admin.access-control.epeople.form.memberOfNoGroups": "Бұл адам ешқандай топтың мүшесі емес", @@ -487,28 +478,22 @@ // "admin.access-control.groups.title": "Groups", - // TODO Source message changed - Revise the translation - "admin.access-control.groups.title": "DSpace Angular :: Топтар", + "admin.access-control.groups.title": "Топтар", // "admin.access-control.groups.breadcrumbs": "Groups", - // TODO New key - Add a translation - "admin.access-control.groups.breadcrumbs": "Groups", + "admin.access-control.groups.breadcrumbs": "Топтар", // "admin.access-control.groups.singleGroup.breadcrumbs": "Edit Group", - // TODO New key - Add a translation - "admin.access-control.groups.singleGroup.breadcrumbs": "Edit Group", + "admin.access-control.groups.singleGroup.breadcrumbs": "Топты өңдеу", // "admin.access-control.groups.title.singleGroup": "Edit Group", - // TODO Source message changed - Revise the translation - "admin.access-control.groups.title.singleGroup": "DSpace Angular:: редакциялау тобы", + "admin.access-control.groups.title.singleGroup": "Топты редакциялау", // "admin.access-control.groups.title.addGroup": "New Group", - // TODO Source message changed - Revise the translation - "admin.access-control.groups.title.addGroup": "DSpace Angular :: Жаңа топ", + "admin.access-control.groups.title.addGroup": "Жаңа топ қосу", // "admin.access-control.groups.addGroup.breadcrumbs": "New Group", - // TODO New key - Add a translation - "admin.access-control.groups.addGroup.breadcrumbs": "New Group", + "admin.access-control.groups.addGroup.breadcrumbs": "Жаңы топ", // "admin.access-control.groups.head": "Groups", "admin.access-control.groups.head": "Топтар", @@ -723,8 +708,7 @@ "admin.access-control.groups.form.subgroups-list.table.name": "Аты", // "admin.access-control.groups.form.subgroups-list.table.collectionOrCommunity": "Collection/Community", - // TODO New key - Add a translation - "admin.access-control.groups.form.subgroups-list.table.collectionOrCommunity": "Collection/Community", + "admin.access-control.groups.form.subgroups-list.table.collectionOrCommunity": "Жинақ/Қауымдастық", // "admin.access-control.groups.form.subgroups-list.table.edit": "Remove / Add", "admin.access-control.groups.form.subgroups-list.table.edit": "Жою / Қосу", @@ -733,7 +717,6 @@ "admin.access-control.groups.form.subgroups-list.table.edit.buttons.remove": "Аты бар кіші топты жою \"{{name}}\"", // "admin.access-control.groups.form.subgroups-list.table.edit.buttons.add": "Add subgroup with name \"{{name}}\"", - // TODO Source message changed - Revise the translation "admin.access-control.groups.form.subgroups-list.table.edit.buttons.add": "Атауы бар кіші топты қосыңыз \"{{name}}\"", // "admin.access-control.groups.form.subgroups-list.table.edit.currentGroup": "Current group", @@ -764,7 +747,6 @@ "admin.access-control.groups.form.subgroups-list.no-subgroups-yet": "Топта әлі кіші топтар жоқ.", // "admin.access-control.groups.form.return": "Back", - // TODO Source message changed - Revise the translation "admin.access-control.groups.form.return": "Топтарға оралу", @@ -856,46 +838,37 @@ "admin.metadata-import.page.error.addFile": "Алдымен файлды таңдаңыз!", // "admin.metadata-import.page.validateOnly": "Validate Only", - // TODO New key - Add a translation - "admin.metadata-import.page.validateOnly": "Validate Only", + "admin.metadata-import.page.validateOnly": "Тек Тексеру", // "admin.metadata-import.page.validateOnly.hint": "When selected, the uploaded CSV will be validated. You will receive a report of detected changes, but no changes will be saved.", - // TODO New key - Add a translation - "admin.metadata-import.page.validateOnly.hint": "When selected, the uploaded CSV will be validated. You will receive a report of detected changes, but no changes will be saved.", + "admin.metadata-import.page.validateOnly.hint": "Бұл параметрді таңдаған кезде жүктелген CSV файлы тексеріледі. Сіз анықталған өзгерістер туралы есеп аласыз, бірақ ешқандай өзгеріс сақталмайды.", // "auth.errors.invalid-user": "Invalid email address or password.", - // TODO New key - Add a translation - "auth.errors.invalid-user": "Invalid email address or password.", + "auth.errors.invalid-user": "Электрондық пошта мекенжайы немесе пароль дұрыс емес.", // "auth.messages.expired": "Your session has expired. Please log in again.", - // TODO New key - Add a translation - "auth.messages.expired": "Your session has expired. Please log in again.", + "auth.messages.expired": "Сеанстың жарамдылық мерзімі аяқталды. Тағы бір рет кіріңіз.", // "auth.messages.token-refresh-failed": "Refreshing your session token failed. Please log in again.", - // TODO New key - Add a translation - "auth.messages.token-refresh-failed": "Refreshing your session token failed. Please log in again.", + "auth.messages.token-refresh-failed": "Сеанс таңбалауышын жаңарту мүмкін емес. Тағы бір рет кіріңіз.", // "bitstream.download.page": "Now downloading {{bitstream}}..." , - // TODO New key - Add a translation - "bitstream.download.page": "Now downloading {{bitstream}}..." , + "bitstream.download.page": "Қазір жүктеу {{bitstream}}..." , // "bitstream.download.page.back": "Back" , - // TODO New key - Add a translation - "bitstream.download.page.back": "Back" , + "bitstream.download.page.back": "Артқа" , // "bitstream.edit.authorizations.link": "Edit bitstream's Policies", - // TODO New key - Add a translation - "bitstream.edit.authorizations.link": "Edit bitstream's Policies", + "bitstream.edit.authorizations.link": "Бит ағынының саясатын өңдеу", // "bitstream.edit.authorizations.title": "Edit bitstream's Policies", - // TODO New key - Add a translation - "bitstream.edit.authorizations.title": "Edit bitstream's Policies", + "bitstream.edit.authorizations.title": "Бит ағынының саясатын өңдеу", // "bitstream.edit.return": "Back", // TODO New key - Add a translation @@ -944,36 +917,28 @@ "bitstream.edit.notifications.error.format.title": "Бит ағынының форматын сақтау кезінде қате пайда болды", // "bitstream.edit.form.iiifLabel.label": "IIIF Label", - // TODO New key - Add a translation - "bitstream.edit.form.iiifLabel.label": "IIIF Label", + "bitstream.edit.form.iiifLabel.label": "IIIF Жапсырма", // "bitstream.edit.form.iiifLabel.hint": "Canvas label for this image. If not provided default label will be used.", - // TODO New key - Add a translation - "bitstream.edit.form.iiifLabel.hint": "Canvas label for this image. If not provided default label will be used.", + "bitstream.edit.form.iiifLabel.hint": "Бұл кескінге арналған кенеп жапсырмасы. Егер көрсетілмесе, әдепкі белгі қолданылады.", // "bitstream.edit.form.iiifToc.label": "IIIF Table of Contents", - // TODO New key - Add a translation - "bitstream.edit.form.iiifToc.label": "IIIF Table of Contents", + "bitstream.edit.form.iiifToc.label": "IIIF Мазмұны", // "bitstream.edit.form.iiifToc.hint": "Adding text here makes this the start of a new table of contents range.", - // TODO New key - Add a translation - "bitstream.edit.form.iiifToc.hint": "Adding text here makes this the start of a new table of contents range.", + "bitstream.edit.form.iiifToc.hint": "Мұнда мәтін қосу мұны жаңа мазмұн кестесінің басталуына әкеледі.", // "bitstream.edit.form.iiifWidth.label": "IIIF Canvas Width", - // TODO New key - Add a translation - "bitstream.edit.form.iiifWidth.label": "IIIF Canvas Width", + "bitstream.edit.form.iiifWidth.label": "IIIF Кенептің ені", // "bitstream.edit.form.iiifWidth.hint": "The canvas width should usually match the image width.", - // TODO New key - Add a translation - "bitstream.edit.form.iiifWidth.hint": "The canvas width should usually match the image width.", + "bitstream.edit.form.iiifWidth.hint": "Кенептің ені әдетте кескіннің еніне сәйкес келуі керек.", // "bitstream.edit.form.iiifHeight.label": "IIIF Canvas Height", - // TODO New key - Add a translation - "bitstream.edit.form.iiifHeight.label": "IIIF Canvas Height", + "bitstream.edit.form.iiifHeight.label": "IIIF Кенептің биіктігі", // "bitstream.edit.form.iiifHeight.hint": "The canvas height should usually match the image height.", - // TODO New key - Add a translation - "bitstream.edit.form.iiifHeight.hint": "The canvas height should usually match the image height.", + "bitstream.edit.form.iiifHeight.hint": "Кенептің биіктігі әдетте кескіннің биіктігіне сәйкес келуі керек.", // "bitstream.edit.notifications.saved.content": "Your changes to this bitstream were saved.", @@ -986,85 +951,66 @@ "bitstream.edit.title": "Бит ағынын өзгерту", // "bitstream-request-a-copy.alert.canDownload1": "You already have access to this file. If you want to download the file, click ", - // TODO New key - Add a translation - "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": "Сіз бұл файлға қол жеткізе аласыз. Егер сіз файлды жүктегіңіз келсе, нұқыңыз ", // "bitstream-request-a-copy.alert.canDownload2": "here", - // TODO New key - Add a translation - "bitstream-request-a-copy.alert.canDownload2": "here", + "bitstream-request-a-copy.alert.canDownload2": "мында", // "bitstream-request-a-copy.header": "Request a copy of the file", - // TODO New key - Add a translation - "bitstream-request-a-copy.header": "Request a copy of the file", + "bitstream-request-a-copy.header": "Файлдың көшірмесін сұраңыз", // "bitstream-request-a-copy.intro": "Enter the following information to request a copy for the following item: ", - // TODO New key - Add a translation - "bitstream-request-a-copy.intro": "Enter the following information to request a copy for the following item: ", + "bitstream-request-a-copy.intro": "Келесі элементтің көшірмесін сұрау үшін келесі ақпаратты енгізіңіз: ", // "bitstream-request-a-copy.intro.bitstream.one": "Requesting the following file: ", - // TODO New key - Add a translation - "bitstream-request-a-copy.intro.bitstream.one": "Requesting the following file: ", + "bitstream-request-a-copy.intro.bitstream.one": "Келесі файлды сұрау: ", + // "bitstream-request-a-copy.intro.bitstream.all": "Requesting all files. ", - // TODO New key - Add a translation - "bitstream-request-a-copy.intro.bitstream.all": "Requesting all files. ", + "bitstream-request-a-copy.intro.bitstream.all": "Барлық файлдарды сұрау. ", // "bitstream-request-a-copy.name.label": "Name *", - // TODO New key - Add a translation - "bitstream-request-a-copy.name.label": "Name *", + "bitstream-request-a-copy.name.label": "Аты *", // "bitstream-request-a-copy.name.error": "The name is required", - // TODO New key - Add a translation - "bitstream-request-a-copy.name.error": "The name is required", + "bitstream-request-a-copy.name.error": "Аты міндетті", // "bitstream-request-a-copy.email.label": "Your e-mail address *", - // TODO New key - Add a translation - "bitstream-request-a-copy.email.label": "Your e-mail address *", + "bitstream-request-a-copy.email.label": "Сіздің электрондық пошта мекенжайыңыз *", // "bitstream-request-a-copy.email.hint": "This email address is used for sending the file.", - // TODO New key - Add a translation - "bitstream-request-a-copy.email.hint": "This email address is used for sending the file.", + "bitstream-request-a-copy.email.hint": "Бұл электрондық пошта мекенжайы файлды жіберу үшін қолданылады.", // "bitstream-request-a-copy.email.error": "Please enter a valid email address.", - // TODO New key - Add a translation - "bitstream-request-a-copy.email.error": "Please enter a valid email address.", + "bitstream-request-a-copy.email.error": "Жарамды электрондық пошта мекенжайын енгізіңіз.", // "bitstream-request-a-copy.allfiles.label": "Files", - // TODO New key - Add a translation - "bitstream-request-a-copy.allfiles.label": "Files", + "bitstream-request-a-copy.allfiles.label": "Файлдар", // "bitstream-request-a-copy.files-all-false.label": "Only the requested file", - // TODO New key - Add a translation - "bitstream-request-a-copy.files-all-false.label": "Only the requested file", + "bitstream-request-a-copy.files-all-false.label": "Тек сұралған файл", // "bitstream-request-a-copy.files-all-true.label": "All files (of this item) in restricted access", - // TODO New key - Add a translation - "bitstream-request-a-copy.files-all-true.label": "All files (of this item) in restricted access", + "bitstream-request-a-copy.files-all-true.label": "Барлық файлдар (осы элемент) шектеулі қол жетімді", // "bitstream-request-a-copy.message.label": "Message", - // TODO New key - Add a translation - "bitstream-request-a-copy.message.label": "Message", + "bitstream-request-a-copy.message.label": "Хабарлар", // "bitstream-request-a-copy.return": "Back", - // TODO New key - Add a translation - "bitstream-request-a-copy.return": "Back", + "bitstream-request-a-copy.return": "Артқа", // "bitstream-request-a-copy.submit": "Request copy", - // TODO New key - Add a translation - "bitstream-request-a-copy.submit": "Request copy", + "bitstream-request-a-copy.submit": "Көшірмесін сұрау", // "bitstream-request-a-copy.submit.success": "The item request was submitted successfully.", - // TODO New key - Add a translation - "bitstream-request-a-copy.submit.success": "The item request was submitted successfully.", + "bitstream-request-a-copy.submit.success": "Өнімге сұраныс сәтті жіберілді.", // "bitstream-request-a-copy.submit.error": "Something went wrong with submitting the item request.", - // TODO New key - Add a translation - "bitstream-request-a-copy.submit.error": "Something went wrong with submitting the item request.", + "bitstream-request-a-copy.submit.error": "Өнімге сұраныс жіберумен бір нәрсе дұрыс болмады.", // "browse.back.all-results": "All browse results", - // TODO New key - Add a translation - "browse.back.all-results": "All browse results", + "browse.back.all-results": "Барлық қарау нәтижелері", // "browse.comcol.by.author": "By Author", "browse.comcol.by.author": "Авторы", @@ -1109,20 +1055,16 @@ "browse.metadata.title.breadcrumbs": "Тақырыбы бойынша қарау", // "pagination.next.button": "Next", - // TODO New key - Add a translation - "pagination.next.button": "Next", + "pagination.next.button": "Келесі", // "pagination.previous.button": "Previous", - // TODO New key - Add a translation - "pagination.previous.button": "Previous", + "pagination.previous.button": "Алдыңғысы", // "pagination.next.button.disabled.tooltip": "No more pages of results", - // TODO New key - Add a translation - "pagination.next.button.disabled.tooltip": "No more pages of results", + "pagination.next.button.disabled.tooltip": "Нәтижелері бар беттер жоқ", // "browse.startsWith": ", starting with {{ startsWith }}", - // TODO New key - Add a translation - "browse.startsWith": ", starting with {{ startsWith }}", + "browse.startsWith": ", {{ startsWith }} деп басталуымен", // "browse.startsWith.choose_start": "(Choose start)", "browse.startsWith.choose_start": "(басталуын таңдаңыз)", @@ -1131,12 +1073,10 @@ "browse.startsWith.choose_year": "(Жылды таңдаңыз)", // "browse.startsWith.choose_year.label": "Choose the issue year", - // TODO New key - Add a translation - "browse.startsWith.choose_year.label": "Choose the issue year", + "browse.startsWith.choose_year.label": "Шығарылған жылын таңдаңыз", // "browse.startsWith.jump": "Filter results by year or month", - // TODO Source message changed - Revise the translation - "browse.startsWith.jump": "Индекстегі нүктеге өту:", + "browse.startsWith.jump": "Нәтижелерді жылдар немесе айлар бойынша сүзу", // "browse.startsWith.months.april": "April", "browse.startsWith.months.april": "Сәуір", @@ -1169,8 +1109,7 @@ "browse.startsWith.months.none": "(Айды таңдаңыз)", // "browse.startsWith.months.none.label": "Choose the issue month", - // TODO New key - Add a translation - "browse.startsWith.months.none.label": "Choose the issue month", + "browse.startsWith.months.none.label": "Шығарылған айды таңдаңыз", // "browse.startsWith.months.november": "November", "browse.startsWith.months.november": "Қараша", @@ -1182,28 +1121,22 @@ "browse.startsWith.months.september": "Қыркүйек", // "browse.startsWith.submit": "Browse", - // TODO Source message changed - Revise the translation - "browse.startsWith.submit": "Go", + "browse.startsWith.submit": "Алға", // "browse.startsWith.type_date": "Filter results by date", - // TODO Source message changed - Revise the translation "browse.startsWith.type_date": "Немесе күнді енгізіңіз (жыл-ай):", // "browse.startsWith.type_date.label": "Or type in a date (year-month) and click on the Browse button", - // TODO New key - Add a translation - "browse.startsWith.type_date.label": "Or type in a date (year-month) and click on the Browse button", + "browse.startsWith.type_date.label": "Немесе күнді (жыл-ай) енгізіп, "шолу"түймесін басыңыз.", // "browse.startsWith.type_text": "Filter results by typing the first few letters", - // TODO Source message changed - Revise the translation "browse.startsWith.type_text": "Немесе алғашқы бірнеше әріптерді енгізіңіз:", // "browse.title": "Browsing {{ collection }} by {{ field }}{{ startsWith }} {{ value }}", - // TODO Source message changed - Revise the translation "browse.title": "{{collection}} {{field}} {{value}} бойынша қараңыз", // "browse.title.page": "Browsing {{ collection }} by {{ field }} {{ value }}", - // TODO New key - Add a translation - "browse.title.page": "Browsing {{ collection }} by {{ field }} {{ value }}", + "browse.title.page": "{{ collection }} -ны {{ field }} {{ value }} бойынша қараңыз", // "chips.remove": "Remove chip", @@ -1321,12 +1254,10 @@ // "collection.edit.logo.delete.title": "Delete logo", - // TODO New key - Add a translation - "collection.edit.logo.delete.title": "Delete logo", + "collection.edit.logo.delete.title": "Логотипті өшіру", // "collection.edit.logo.delete-undo.title": "Undo delete", - // TODO New key - Add a translation - "collection.edit.logo.delete-undo.title": "Undo delete", + "collection.edit.logo.delete-undo.title": "Өшірмеу", // "collection.edit.logo.label": "Collection logo", "collection.edit.logo.label": "Топтама логотипі", @@ -1355,7 +1286,6 @@ "collection.edit.notifications.success": "Сәтті өзгертілген топтама", // "collection.edit.return": "Back", - // TODO Source message changed - Revise the translation "collection.edit.return": "Қайтару", @@ -1559,74 +1489,73 @@ // "collection.source.controls.head": "Harvest Controls", - // TODO New key - Add a translation - "collection.source.controls.head": "Harvest Controls", + "collection.source.controls.head": "Өнім жинауды бақылау құралдары", + // "collection.source.controls.test.submit.error": "Something went wrong with initiating the testing of the settings", - // TODO New key - Add a translation - "collection.source.controls.test.submit.error": "Something went wrong with initiating the testing of the settings", + "collection.source.controls.test.submit.error": "Параметрлерді тексерудің басталуымен бір нәрсе дұрыс болмады", + // "collection.source.controls.test.failed": "The script to test the settings has failed", - // TODO New key - Add a translation - "collection.source.controls.test.failed": "The script to test the settings has failed", + "collection.source.controls.test.failed": "Параметрлерді тексеру үшін сценарий сәтсіз аяқталды", + // "collection.source.controls.test.completed": "The script to test the settings has successfully finished", - // TODO New key - Add a translation - "collection.source.controls.test.completed": "The script to test the settings has successfully finished", + "collection.source.controls.test.completed": "Параметрлерді тексеру сценарийі сәтті аяқталды", + // "collection.source.controls.test.submit": "Test configuration", - // TODO New key - Add a translation - "collection.source.controls.test.submit": "Test configuration", + "collection.source.controls.test.submit": "Сынақ конфигурациясы", + // "collection.source.controls.test.running": "Testing configuration...", - // TODO New key - Add a translation - "collection.source.controls.test.running": "Testing configuration...", + "collection.source.controls.test.running": "Тестілеу конфигурациясы...", + // "collection.source.controls.import.submit.success": "The import has been successfully initiated", - // TODO New key - Add a translation - "collection.source.controls.import.submit.success": "The import has been successfully initiated", + "collection.source.controls.import.submit.success": "Импорт сәтті басталды", + // "collection.source.controls.import.submit.error": "Something went wrong with initiating the import", - // TODO New key - Add a translation - "collection.source.controls.import.submit.error": "Something went wrong with initiating the import", + "collection.source.controls.import.submit.error": "Импортты бастау кезінде бірдеңе дұрыс болмады", + // "collection.source.controls.import.submit": "Import now", - // TODO New key - Add a translation - "collection.source.controls.import.submit": "Import now", + "collection.source.controls.import.submit": "Қазір импорттаңыз", + // "collection.source.controls.import.running": "Importing...", - // TODO New key - Add a translation - "collection.source.controls.import.running": "Importing...", + "collection.source.controls.import.running": "Импорт...", + // "collection.source.controls.import.failed": "An error occurred during the import", - // TODO New key - Add a translation - "collection.source.controls.import.failed": "An error occurred during the import", + "collection.source.controls.import.failed": "Импорттау кезінде қате кетті", + // "collection.source.controls.import.completed": "The import completed", - // TODO New key - Add a translation - "collection.source.controls.import.completed": "The import completed", + "collection.source.controls.import.completed": "Импорт аяқталды", + // "collection.source.controls.reset.submit.success": "The reset and reimport has been successfully initiated", - // TODO New key - Add a translation - "collection.source.controls.reset.submit.success": "The reset and reimport has been successfully initiated", + "collection.source.controls.reset.submit.success": "Қалпына келтіру және қайта импорттау сәтті басталды", + // "collection.source.controls.reset.submit.error": "Something went wrong with initiating the reset and reimport", - // TODO New key - Add a translation - "collection.source.controls.reset.submit.error": "Something went wrong with initiating the reset and reimport", + "collection.source.controls.reset.submit.error": "Қалпына келтіру және қайта импорттау кезінде бір нәрсе дұрыс болмады", + // "collection.source.controls.reset.failed": "An error occurred during the reset and reimport", - // TODO New key - Add a translation - "collection.source.controls.reset.failed": "An error occurred during the reset and reimport", + "collection.source.controls.reset.failed": "Қалпына келтіру және қайта импорттау кезінде қате пайда болды", + // "collection.source.controls.reset.completed": "The reset and reimport completed", - // TODO New key - Add a translation - "collection.source.controls.reset.completed": "The reset and reimport completed", + "collection.source.controls.reset.completed": "Қалпына келтіру және қайта импорттау аяқталды", + // "collection.source.controls.reset.submit": "Reset and reimport", - // TODO New key - Add a translation - "collection.source.controls.reset.submit": "Reset and reimport", + "collection.source.controls.reset.submit": "Қалпына келтіру және қайта импорттау", + // "collection.source.controls.reset.running": "Resetting and reimporting...", - // TODO New key - Add a translation - "collection.source.controls.reset.running": "Resetting and reimporting...", + "collection.source.controls.reset.running": "Қалпына келтіру және қайта импорттау...", + // "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": "Өнім мәртебесі:", + // "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": "Өнім жинаудың басталу уақыты:", + // "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": "Соңғы рет өнім жиналды:", + // "collection.source.controls.harvest.message": "Harvest info:", - // TODO New key - Add a translation - "collection.source.controls.harvest.message": "Harvest info:", + "collection.source.controls.harvest.message": "Өнім хабары:", + // "collection.source.controls.harvest.no-information": "N/A", - // TODO New key - Add a translation - "collection.source.controls.harvest.no-information": "N/A", + "collection.source.controls.harvest.no-information": "Ақпарат жоқ", // "collection.source.update.notifications.error.content": "The provided settings have been tested and didn't work.", @@ -1775,108 +1704,84 @@ // "comcol-role.edit.no-group": "None", - // TODO New key - Add a translation - "comcol-role.edit.no-group": "None", + "comcol-role.edit.no-group": "Ешкім", // "comcol-role.edit.create": "Create", - // TODO New key - Add a translation - "comcol-role.edit.create": "Create", + "comcol-role.edit.create": "Құру", // "comcol-role.edit.create.error.title": "Failed to create a group for the '{{ role }}' role", - // TODO New key - Add a translation - "comcol-role.edit.create.error.title": "Failed to create a group for the '{{ role }}' role", + "comcol-role.edit.create.error.title": "'{{ role }}' рөлі үшін топ құру мүмкін емес", // "comcol-role.edit.restrict": "Restrict", - // TODO New key - Add a translation - "comcol-role.edit.restrict": "Restrict", + "comcol-role.edit.restrict": "Шектеу", // "comcol-role.edit.delete": "Delete", - // TODO New key - Add a translation - "comcol-role.edit.delete": "Delete", + "comcol-role.edit.delete": "Жою", // "comcol-role.edit.delete.error.title": "Failed to delete the '{{ role }}' role's group", - // TODO New key - Add a translation - "comcol-role.edit.delete.error.title": "Failed to delete the '{{ role }}' role's group", + "comcol-role.edit.delete.error.title": "'{{ role }}' рөл тобын жою мүмкін емес", // "comcol-role.edit.community-admin.name": "Administrators", - // TODO New key - Add a translation - "comcol-role.edit.community-admin.name": "Administrators", + "comcol-role.edit.community-admin.name": "Әкімшілер", // "comcol-role.edit.collection-admin.name": "Administrators", - // TODO New key - Add a translation - "comcol-role.edit.collection-admin.name": "Administrators", + "comcol-role.edit.collection-admin.name": "Әкімшілер", // "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).", - // TODO New key - Add a translation - "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": "Қауымдастық әкімшілері ішкі қауымдастықтарды немесе коллекцияларды құра алады және оларды басқара алады немесе сол ішкі қауымдастықтарға немесе коллекцияларға басқару тағайындай алады. Сонымен қатар, олар кез-келген кірістірілген коллекцияларға элементтерді кім жібере алатындығын, элементтердің метадеректерін өңдеуді (жібергеннен кейін) және басқа коллекциялардан бар элементтерді қосуды (сәйкестендіруді) шешеді (авторизация жағдайында).", // "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).", - // TODO New key - Add a translation - "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": "Коллекция әкімшілері элементтерді коллекцияға кім жібере алатындығын, элементтің метадеректерін өңдей алатындығын (жібергеннен кейін) және басқа коллекциялардан бар элементтерді осы коллекцияға қосуды (сәйкестендіруді) шешеді (осы коллекцияға авторизация берілген жағдайда).", // "comcol-role.edit.submitters.name": "Submitters", - // TODO New key - Add a translation - "comcol-role.edit.submitters.name": "Submitters", + "comcol-role.edit.submitters.name": "Жіберушілер", // "comcol-role.edit.submitters.description": "The E-People and Groups that have permission to submit new items to this collection.", - // TODO New key - Add a translation - "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": "Осы жинаққа жаңа элементтерді жіберуге рұқсаты бар электрондық пайдаланушылар мен топтар.", // "comcol-role.edit.item_read.name": "Default item read access", - // TODO New key - Add a translation - "comcol-role.edit.item_read.name": "Default item read access", + "comcol-role.edit.item_read.name": "Оқуға арналған әдепкі элементке кіру", // "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.", - // TODO New key - Add a translation - "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": "Осы жинақта ұсынылған жаңа материалдарды оқи алатын электрондық пайдаланушылар мен топтар. Бұл рөлдегі өзгерістердің кері күші жоқ. Жүйеде бар элементтер оларды қосу кезінде оқуға рұқсаты бар адамдар үшін әлі де қол жетімді болады.", // "comcol-role.edit.item_read.anonymous-group": "Default read for incoming items is currently set to Anonymous.", - // TODO New key - Add a translation - "comcol-role.edit.item_read.anonymous-group": "Default read for incoming items is currently set to Anonymous.", + "comcol-role.edit.item_read.anonymous-group": "Қазіргі уақытта кіретін элементтер үшін әдепкі оқу анонимді болып табылады.", // "comcol-role.edit.bitstream_read.name": "Default bitstream read access", - // TODO New key - Add a translation - "comcol-role.edit.bitstream_read.name": "Default bitstream read access", + "comcol-role.edit.bitstream_read.name": "Әдепкі бит ағынын оқуға қол жеткізу", // "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).", - // TODO New key - Add a translation - "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": "Қауымдастық әкімшілері ішкі қауымдастықтарды немесе коллекцияларды құра алады және оларды басқара алады немесе сол ішкі қауымдастықтарға немесе коллекцияларға басқару тағайындай алады. Сонымен қатар, олар кез-келген кірістірілген коллекцияларға элементтерді кім жібере алатындығын, элементтердің метадеректерін өңдеуді (жібергеннен кейін) және басқа коллекциялардан бар элементтерді қосуды (сәйкестендіруді) шешеді (авторизация жағдайында).", // "comcol-role.edit.bitstream_read.anonymous-group": "Default read for incoming bitstreams is currently set to Anonymous.", - // TODO New key - Add a translation - "comcol-role.edit.bitstream_read.anonymous-group": "Default read for incoming bitstreams is currently set to Anonymous.", + "comcol-role.edit.bitstream_read.anonymous-group": "Қазіргі уақытта кіріс бит ағыны үшін әдепкі оқу анонимді болып табылады.", // "comcol-role.edit.editor.name": "Editors", - // TODO New key - Add a translation - "comcol-role.edit.editor.name": "Editors", + "comcol-role.edit.editor.name": "Редакторлар", // "comcol-role.edit.editor.description": "Editors are able to edit the metadata of incoming submissions, and then accept or reject them.", - // TODO New key - Add a translation - "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": "Редакторлар кіріс материалдардың метадеректерін өңдей алады, содан кейін оларды қабылдай немесе қабылдамай алады.", // "comcol-role.edit.finaleditor.name": "Final editors", - // TODO New key - Add a translation - "comcol-role.edit.finaleditor.name": "Final editors", + "comcol-role.edit.finaleditor.name": "Соңғы редакторлар", // "comcol-role.edit.finaleditor.description": "Final editors are able to edit the metadata of incoming submissions, but will not be able to reject them.", - // TODO New key - Add a translation - "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": "Соңғы редакторлар кіріс материалдардың метадеректерін өңдей алады, бірақ оларды қабылдамай алады.", // "comcol-role.edit.reviewer.name": "Reviewers", - // TODO New key - Add a translation - "comcol-role.edit.reviewer.name": "Reviewers", + "comcol-role.edit.reviewer.name": "Рецензенттер", // "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.", - // TODO New key - Add a translation - "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": "Рецензенттер келіп түскен материалдарды қабылдауы немесе қабылдамауы мүмкін. Алайда, олар жіберу метадеректерін өңдей алмайды.", @@ -1922,16 +1827,13 @@ // "cookies.consent.accept-all": "Accept all", - // TODO New key - Add a translation - "cookies.consent.accept-all": "Accept all", + "cookies.consent.accept-all": "Мен бәрін қабылдаймын", // "cookies.consent.accept-selected": "Accept selected", - // TODO New key - Add a translation - "cookies.consent.accept-selected": "Accept selected", + "cookies.consent.accept-selected": "Таңдалғанды қабылдау", // "cookies.consent.app.opt-out.description": "This app is loaded by default (but you can opt out)", - // TODO New key - Add a translation - "cookies.consent.app.opt-out.description": "This app is loaded by default (but you can opt out)", + "cookies.consent.app.opt-out.description": "Бұл бағдарлама әдепкі бойынша жүктеледі (бірақ сіз одан бас тарта аласыз)", // "cookies.consent.app.opt-out.title": "(opt-out)", "cookies.consent.app.opt-out.title": "(opt-out)", @@ -1958,8 +1860,7 @@ "cookies.consent.content-notice.description": "Біз сіздің жеке ақпаратыңызды келесі мақсаттарда жинаймыз және өңдейміз: Аутентификация, Параметрлері, Растау және Статистикалар.
Көбірек білу үшін, біздің {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": "Біз сіздің жеке мәліметтеріңізді келесі мақсаттар үшін жинаймыз және өңдейміз: Аутентификация, Қалаулар, Растау және статистикасы.", // "cookies.consent.content-notice.learnMore": "Customize", "cookies.consent.content-notice.learnMore": "Баптау", @@ -2067,34 +1968,27 @@ // "deny-request-copy.email.message": "Dear {{ recipientName }},\nIn response to your request I regret to inform you that it's not possible to send you a copy of the file(s) you have requested, concerning the document: \"{{ itemUrl }}\" ({{ itemName }}), of which I am an author.\n\nBest regards,\n{{ authorName }} <{{ authorEmail }}>", - // TODO New key - Add a translation - "deny-request-copy.email.message": "Dear {{ recipientName }},\nIn response to your request I regret to inform you that it's not possible to send you a copy of the file(s) you have requested, concerning the document: \"{{ itemUrl }}\" ({{ itemName }}), of which I am an author.\n\nBest regards,\n{{ authorName }} <{{ authorEmail }}>", + "deny-request-copy.email.message": "Құрметті {{ recipientName }}, \nСіздің сұрауыңызға жауап ретінде Мен сізге құжатқа қатысты сіз сұраған файлдардың көшірмесін жіберу мүмкін емес екенін өкінішпен айтамын: \"{{ itemUrl }}\" ({{ itemName }}), оның авторы мен.\n\nІзгі тілектермен,\N{{ authorName }} <{{ authorEmail }}>", // "deny-request-copy.email.subject": "Request copy of document", - // TODO New key - Add a translation - "deny-request-copy.email.subject": "Request copy of document", + "deny-request-copy.email.subject": "Құжаттың көшірмесін сұрау", // "deny-request-copy.error": "An error occurred", - // TODO New key - Add a translation - "deny-request-copy.error": "An error occurred", + "deny-request-copy.error": "Қате кетті", // "deny-request-copy.header": "Deny document copy request", - // TODO New key - Add a translation - "deny-request-copy.header": "Deny document copy request", + "deny-request-copy.header": "Құжатты көшіру сұрауын қабылдамау", // "deny-request-copy.intro": "This message will be sent to the applicant of the request", - // TODO New key - Add a translation - "deny-request-copy.intro": "This message will be sent to the applicant of the request", + "deny-request-copy.intro": "Бұл хабарлама өтініш берушіге жіберіледі", // "deny-request-copy.success": "Successfully denied item request", - // TODO New key - Add a translation - "deny-request-copy.success": "Successfully denied item request", + "deny-request-copy.success": "Бөлікке сұраныс сәтті қабылданбады", // "dso.name.untitled": "Untitled", - // TODO New key - Add a translation - "dso.name.untitled": "Untitled", + "dso.name.untitled": "Атауы жоқ", @@ -2132,8 +2026,7 @@ "dso-selector.edit.item.head": "Элементті өзгерту", // "dso-selector.error.title": "An error occurred searching for a {{ type }}", - // TODO New key - Add a translation - "dso-selector.error.title": "An error occurred searching for a {{ type }}", + "dso-selector.error.title": "{{ type }} іздеу кезінде қате пайда болды", // "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", "dso-selector.export-metadata.dspaceobject.head": "Метадеректерді экспорттау", @@ -2145,36 +2038,28 @@ "dso-selector.placeholder": "{{ type }} іздеу", // "dso-selector.select.collection.head": "Select a collection", - // TODO New key - Add a translation - "dso-selector.select.collection.head": "Select a collection", + "dso-selector.select.collection.head": "Жинақты таңдаңыз", // "dso-selector.set-scope.community.head": "Select a search scope", - // TODO New key - Add a translation - "dso-selector.set-scope.community.head": "Select a search scope", + "dso-selector.set-scope.community.head": "Іздеу аймағын таңдаңыз", // "dso-selector.set-scope.community.button": "Search all of DSpace", - // TODO New key - Add a translation - "dso-selector.set-scope.community.button": "Search all of DSpace", + "dso-selector.set-scope.community.button": "DSpace арқылы іздеу", // "dso-selector.set-scope.community.input-header": "Search for a community or collection", - // TODO New key - Add a translation - "dso-selector.set-scope.community.input-header": "Search for a community or collection", + "dso-selector.set-scope.community.input-header": "Қауымдастық немесе коллекция бойынша іздеу", // "dso-selector.claim.item.head": "Profile tips", - // TODO New key - Add a translation - "dso-selector.claim.item.head": "Profile tips", + "dso-selector.claim.item.head": "Профиль бойынша кеңестер", // "dso-selector.claim.item.body": "These are existing profiles that may be related to you. If you recognize yourself in one of these profiles, select it and on the detail page, among the options, choose to claim it. Otherwise you can create a new profile from scratch using the button below.", - // TODO New key - Add a translation - "dso-selector.claim.item.body": "These are existing profiles that may be related to you. If you recognize yourself in one of these profiles, select it and on the detail page, among the options, choose to claim it. Otherwise you can create a new profile from scratch using the button below.", + "dso-selector.claim.item.body": "Бұл сізге байланысты болуы мүмкін профильдер. Егер сіз осы профильдердің бірінде өзіңізді танитын болсаңыз, опциялар арасында оны және ақпарат бетінде таңдаңыз, оны жариялау үшін таңдаңыз. Әйтпесе, төмендегі батырманы пайдаланып нөлден жаңа профиль жасай аласыз.", // "dso-selector.claim.item.not-mine-label": "None of these are mine", - // TODO New key - Add a translation - "dso-selector.claim.item.not-mine-label": "None of these are mine", + "dso-selector.claim.item.not-mine-label": "Олардың ешқайсысы маған тиесілі емес", // "dso-selector.claim.item.create-from-scratch": "Create a new one", - // TODO New key - Add a translation - "dso-selector.claim.item.create-from-scratch": "Create a new one", + "dso-selector.claim.item.create-from-scratch": "Жаңасын жасаңыз", // "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}", "confirmation-modal.export-metadata.header": "{{ dsoName }} метадеректерін экспорттау", @@ -2201,20 +2086,16 @@ "confirmation-modal.delete-eperson.confirm": "Жою", // "confirmation-modal.delete-profile.header": "Delete Profile", - // TODO New key - Add a translation - "confirmation-modal.delete-profile.header": "Delete Profile", + "confirmation-modal.delete-profile.header": "Профильді өшіру", // "confirmation-modal.delete-profile.info": "Are you sure you want to delete your profile", - // TODO New key - Add a translation - "confirmation-modal.delete-profile.info": "Are you sure you want to delete your profile", + "confirmation-modal.delete-profile.info": "Сіз өзіңіздің профиліңізді жойғыңыз келетініне сенімдісіз", // "confirmation-modal.delete-profile.cancel": "Cancel", - // TODO New key - Add a translation - "confirmation-modal.delete-profile.cancel": "Cancel", + "confirmation-modal.delete-profile.cancel": "Алып тастау", // "confirmation-modal.delete-profile.confirm": "Delete", - // TODO New key - Add a translation - "confirmation-modal.delete-profile.confirm": "Delete", + "confirmation-modal.delete-profile.confirm": "Жою", // "error.bitstream": "Error fetching bitstream", @@ -2254,8 +2135,7 @@ "error.search-results": "Іздеу нәтижелерін алу қатесі", // "error.invalid-search-query": "Search query is not valid. Please check Solr query syntax best practices for further information about this error.", - // TODO New key - Add a translation - "error.invalid-search-query": "Search query is not valid. Please check Solr query syntax best practices for further information about this error.", + "error.invalid-search-query": "Іздеу сұранысы жарамсыз. Solr query syntax осы қате туралы қосымша ақпарат алу үшін нұсқаулықпен танысыңыз.", // "error.sub-collections": "Error fetching sub-collections", "error.sub-collections":"Ішкі жинақтарды алу қатесі", @@ -2279,25 +2159,20 @@ "error.validation.filerequired": "Файлды жүктеп салу міндетті", // "error.validation.required": "This field is required", - // TODO New key - Add a translation - "error.validation.required": "This field is required", + "error.validation.required": "Бұл өріс міндетті болып табылады", // "error.validation.NotValidEmail": "This E-mail is not a valid email", - // TODO New key - Add a translation - "error.validation.NotValidEmail": "This E-mail is not a valid email", + "error.validation.NotValidEmail": "Бұл электрондық пошта жарамды емес", // "error.validation.emailTaken": "This E-mail is already taken", - // TODO New key - Add a translation - "error.validation.emailTaken": "This E-mail is already taken", + "error.validation.emailTaken": "Бұл электрондық пошта қазірдің өзінде жіберілді", // "error.validation.groupExists": "This group already exists", - // TODO New key - Add a translation - "error.validation.groupExists": "This group already exists", + "error.validation.groupExists": "Бұл топ қазірдің өзінде бар", // "feed.description": "Syndication feed", - // TODO New key - Add a translation - "feed.description": "Syndication feed", + "feed.description": "Синдикация арнасы", // "file-section.error.header": "Error obtaining files for this item", @@ -2324,8 +2199,7 @@ "footer.link.end-user-agreement":"Соңғы пайдаланушы келісімі", // "footer.link.feedback":"Send Feedback", - // TODO New key - Add a translation - "footer.link.feedback":"Send Feedback", + "footer.link.feedback":"Пікір жіберу", @@ -2333,8 +2207,7 @@ "forgot-email.form.header": "Парольді Ұмыттыңыз Ба", // "forgot-email.form.info": "Enter the email address associated with the account.", - // TODO Source message changed - Revise the translation - "forgot-email.form.info": "Электрондық пошта жаңартуларына арналған жинақтарға жазылу және DSpace қызметіне жаңа элементтерді жіберу үшін тіркелгіні тіркеңіз.", + "forgot-email.form.info": "Тіркелгіге қатысты электрондық пошта мекенжайын енгізіңіз.", // "forgot-email.form.email": "Email Address *", "forgot-email.form.email": "Электрондық пошта *", @@ -2346,27 +2219,22 @@ "forgot-email.form.email.error.pattern": "Жарамды электрондық пошта мекенжайын толтырыңыз", // "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": "Бұл мекенжай тексеріледі және сіздің логин атыңыз ретінде пайдаланылады.", // "forgot-email.form.submit": "Reset password", - // TODO Source message changed - Revise the translation "forgot-email.form.submit": "Жіберу", // "forgot-email.form.success.head": "Password reset email sent", - // TODO Source message changed - Revise the translation "forgot-email.form.success.head": "Растау электрондық поштасы жіберілді", // "forgot-email.form.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", "forgot-email.form.success.content": "Арнайы URL мекенжайы және қосымша нұсқаулары бар электрондық пошта {{ email }} мекенжайына жіберілді.", // "forgot-email.form.error.head": "Error when trying to reset password", - // TODO Source message changed - Revise the translation "forgot-email.form.error.head": "Электрондық поштаны тіркеу кезіндегі қате", // "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": "Келесі электрондық пошта мекенжайына байланысты есептік жазбаның құпия сөзін қалпына келтіру кезінде қате пайда болды: {{ email }}", @@ -2503,177 +2371,135 @@ "form.submit": "Жіберу", // "form.repeatable.sort.tip": "Drop the item in the new position", - // TODO New key - Add a translation - "form.repeatable.sort.tip": "Drop the item in the new position", + "form.repeatable.sort.tip": "Элементті жаңа орынға қойыңыз", // "grant-deny-request-copy.deny": "Don't send copy", - // TODO New key - Add a translation - "grant-deny-request-copy.deny": "Don't send copy", + "grant-deny-request-copy.deny": "Көшірмесін жібермеңіз", // "grant-deny-request-copy.email.back": "Back", - // TODO New key - Add a translation - "grant-deny-request-copy.email.back": "Back", + "grant-deny-request-copy.email.back": "Артқа", // "grant-deny-request-copy.email.message": "Message", - // TODO New key - Add a translation - "grant-deny-request-copy.email.message": "Message", + "grant-deny-request-copy.email.message": "Хабарлар", // "grant-deny-request-copy.email.message.empty": "Please enter a message", - // TODO New key - Add a translation - "grant-deny-request-copy.email.message.empty": "Please enter a message", + "grant-deny-request-copy.email.message.empty": "Хабарламаны енгізіңіз", // "grant-deny-request-copy.email.permissions.info": "You may use this occasion to reconsider the access restrictions on the document, to avoid having to respond to these requests. If you’d like to ask the repository administrators to remove these restrictions, please check the box below.", - // TODO New key - Add a translation - "grant-deny-request-copy.email.permissions.info": "You may use this occasion to reconsider the access restrictions on the document, to avoid having to respond to these requests. If you’d like to ask the repository administrators to remove these restrictions, please check the box below.", + "grant-deny-request-copy.email.permissions.info": "Сіз бұл сұрауларға жауап бермеу үшін құжатқа кіру шектеулерін қайта қарау үшін осы мүмкіндікті пайдалана аласыз. Егер сіз репозиторий әкімшілерінен осы шектеулерді алып тастауды сұрағыңыз келсе, төмендегі құсбелгіні қойыңыз.", // "grant-deny-request-copy.email.permissions.label": "Change to open access", - // TODO New key - Add a translation - "grant-deny-request-copy.email.permissions.label": "Change to open access", + "grant-deny-request-copy.email.permissions.label": "Ашық қол жетімділікке ауысу", // "grant-deny-request-copy.email.send": "Send", - // TODO New key - Add a translation - "grant-deny-request-copy.email.send": "Send", + "grant-deny-request-copy.email.send": "Жіберу", // "grant-deny-request-copy.email.subject": "Subject", - // TODO New key - Add a translation - "grant-deny-request-copy.email.subject": "Subject", + "grant-deny-request-copy.email.subject": "Тақырыбы", // "grant-deny-request-copy.email.subject.empty": "Please enter a subject", - // TODO New key - Add a translation - "grant-deny-request-copy.email.subject.empty": "Please enter a subject", + "grant-deny-request-copy.email.subject.empty": "Тақырыпты енгізіңіз", // "grant-deny-request-copy.grant": "Send copy", - // TODO New key - Add a translation - "grant-deny-request-copy.grant": "Send copy", + "grant-deny-request-copy.grant": "Көшірмесін жіберу", // "grant-deny-request-copy.header": "Document copy request", - // TODO New key - Add a translation - "grant-deny-request-copy.header": "Document copy request", + "grant-deny-request-copy.header": "Құжатты көшіруге сұрау салу", // "grant-deny-request-copy.home-page": "Take me to the home page", - // TODO New key - Add a translation - "grant-deny-request-copy.home-page": "Take me to the home page", + "grant-deny-request-copy.home-page": "Мені басты бетке апарыңыз", // "grant-deny-request-copy.intro1": "If you are one of the authors of the document {{ name }}, then please use one of the options below to respond to the user's request.", - // TODO New key - Add a translation - "grant-deny-request-copy.intro1": "If you are one of the authors of the document {{ name }}, then please use one of the options below to respond to the user's request.", + "grant-deny-request-copy.intro1": "Егер Сіз {{ name }} құжат авторларының бірі болсаңыз, пайдаланушының сұрауына жауап беру үшін төмендегі нұсқалардың бірін қолданыңыз.", // "grant-deny-request-copy.intro2": "After choosing an option, you will be presented with a suggested email reply which you may edit.", - // TODO New key - Add a translation - "grant-deny-request-copy.intro2": "After choosing an option, you will be presented with a suggested email reply which you may edit.", + "grant-deny-request-copy.intro2": "Опцияны таңдағаннан кейін Сізге электрондық пошта арқылы ұсынылған жауап ұсынылады, оны өңдеуге болады.", // "grant-deny-request-copy.processed": "This request has already been processed. You can use the button below to get back to the home page.", - // TODO New key - Add a translation - "grant-deny-request-copy.processed": "This request has already been processed. You can use the button below to get back to the home page.", + "grant-deny-request-copy.processed": "Бұл сұрау қазірдің өзінде өңделді. Басты бетке оралу үшін төмендегі батырманы пайдалануға болады.", // "grant-request-copy.email.message": "Dear {{ recipientName }},\nIn response to your request I have the pleasure to send you in attachment a copy of the file(s) concerning the document: \"{{ itemUrl }}\" ({{ itemName }}), of which I am an author.\n\nBest regards,\n{{ authorName }} <{{ authorEmail }}>", - // TODO New key - Add a translation - "grant-request-copy.email.message": "Dear {{ recipientName }},\nIn response to your request I have the pleasure to send you in attachment a copy of the file(s) concerning the document: \"{{ itemUrl }}\" ({{ itemName }}), of which I am an author.\n\nBest regards,\n{{ authorName }} <{{ authorEmail }}>", + "grant-request-copy.email.message": "Құрметті {{ recipientName }},\nСіздің сұрауыңызға жауап ретінде Мен сізге құжатқа қатысты файлдың көшірмесін тіркемеде жібергеніме қуаныштымын: \"{{ itemUrl }}\" ({{ itemName }}), оның авторы мен.\n\nІзгі тілектермен,\N{{ authorName }} <{{ authorEmail }}>", // "grant-request-copy.email.subject": "Request copy of document", - // TODO New key - Add a translation - "grant-request-copy.email.subject": "Request copy of document", + "grant-request-copy.email.subject": "Құжаттың көшірмесін сұрау", // "grant-request-copy.error": "An error occurred", - // TODO New key - Add a translation - "grant-request-copy.error": "An error occurred", + "grant-request-copy.error": "Қате кетті", // "grant-request-copy.header": "Grant document copy request", - // TODO New key - Add a translation - "grant-request-copy.header": "Grant document copy request", + "grant-request-copy.header": "Құжаттың көшірмесін ұсынуға сұрау салу", // "grant-request-copy.intro": "This message will be sent to the applicant of the request. The requested document(s) will be attached.", - // TODO New key - Add a translation - "grant-request-copy.intro": "This message will be sent to the applicant of the request. The requested document(s) will be attached.", + "grant-request-copy.intro": "Бұл хабарлама өтініш берушіге жіберіледі. Сұралған құжат(тар) қоса беріледі.", // "grant-request-copy.success": "Successfully granted item request", - // TODO New key - Add a translation - "grant-request-copy.success": "Successfully granted item request", + "grant-request-copy.success": "Өнімге сәтті сұраныс", // "health.breadcrumbs": "Health", - // TODO New key - Add a translation - "health.breadcrumbs": "Health", + "health.breadcrumbs": "Күйі", // "health-page.heading" : "Health", - // TODO New key - Add a translation - "health-page.heading" : "Health", + "health-page.heading" : "Күйі", // "health-page.info-tab" : "Info", - // TODO New key - Add a translation - "health-page.info-tab" : "Info", + "health-page.info-tab" : "Ақпарат", // "health-page.status-tab" : "Status", - // TODO New key - Add a translation - "health-page.status-tab" : "Status", + "health-page.status-tab" : "Мәртебесі", // "health-page.error.msg": "The health check service is temporarily unavailable", - // TODO New key - Add a translation - "health-page.error.msg": "The health check service is temporarily unavailable", + "health-page.error.msg": "Тексеру қызметі уақытша қол жетімді емес", // "health-page.property.status": "Status code", - // TODO New key - Add a translation - "health-page.property.status": "Status code", + "health-page.property.status": "Күй коды", // "health-page.section.db.title": "Database", - // TODO New key - Add a translation - "health-page.section.db.title": "Database", + "health-page.section.db.title": "Дереккөз", // "health-page.section.geoIp.title": "GeoIp", - // TODO New key - Add a translation "health-page.section.geoIp.title": "GeoIp", // "health-page.section.solrAuthorityCore.title": "Sor: authority core", - // TODO New key - Add a translation - "health-page.section.solrAuthorityCore.title": "Sor: authority core", + "health-page.section.solrAuthorityCore.title": "Sor: биліктің өзегі", // "health-page.section.solrOaiCore.title": "Sor: oai core", - // TODO New key - Add a translation - "health-page.section.solrOaiCore.title": "Sor: oai core", + "health-page.section.solrOaiCore.title": "Sor: oai өзегі", // "health-page.section.solrSearchCore.title": "Sor: search core", - // TODO New key - Add a translation - "health-page.section.solrSearchCore.title": "Sor: search core", + "health-page.section.solrSearchCore.title": "Sor: іздеу өзегі", // "health-page.section.solrStatisticsCore.title": "Sor: statistics core", - // TODO New key - Add a translation - "health-page.section.solrStatisticsCore.title": "Sor: statistics core", + "health-page.section.solrStatisticsCore.title": "Sor: статистиканың өзегі", // "health-page.section-info.app.title": "Application Backend", - // TODO New key - Add a translation - "health-page.section-info.app.title": "Application Backend", + "health-page.section-info.app.title": "Қосымшаның серверлік бөлігі", // "health-page.section-info.java.title": "Java", - // TODO New key - Add a translation "health-page.section-info.java.title": "Java", // "health-page.status": "Status", - // TODO New key - Add a translation - "health-page.status": "Status", + "health-page.status": "Күйі мәртебесі", // "health-page.status.ok.info": "Operational", - // TODO New key - Add a translation - "health-page.status.ok.info": "Operational", + "health-page.status.ok.info": "Жақсы күйде", // "health-page.status.error.info": "Problems detected", - // TODO New key - Add a translation - "health-page.status.error.info": "Problems detected", + "health-page.status.error.info": "Анықталған мәселелер", // "health-page.status.warning.info": "Possible issues detected", - // TODO New key - Add a translation - "health-page.status.warning.info": "Possible issues detected", + "health-page.status.warning.info": "Анықталған мүмкін проблемалар", // "health-page.title": "Health", - // TODO New key - Add a translation - "health-page.title": "Health", + "health-page.title": "Күйі", // "health-page.section.no-issues": "No issues detected", - // TODO New key - Add a translation - "health-page.section.no-issues": "No issues detected", + "health-page.section.no-issues": "Ешқандай проблема табылған жоқ", // "home.description": "", @@ -2683,11 +2509,9 @@ "home.breadcrumbs": "Басты", // "home.search-form.placeholder": "Search the repository ...", - // TODO New key - Add a translation - "home.search-form.placeholder": "Search the repository ...", + "home.search-form.placeholder": "Репозиторидең іздеу ...", // "home.title": "Home", - // TODO Source message changed - Revise the translation "home.title": "Басты", // "home.top-level-communities.head": "Communities in DSpace", @@ -2732,56 +2556,43 @@ "info.privacy.title": "Құпиялылық туралы мәлімдеме", // "info.feedback.breadcrumbs": "Feedback", - // TODO New key - Add a translation - "info.feedback.breadcrumbs": "Feedback", + "info.feedback.breadcrumbs": "Кері байланыс", // "info.feedback.head": "Feedback", - // TODO New key - Add a translation - "info.feedback.head": "Feedback", + "info.feedback.head": "Кері байланыс", // "info.feedback.title": "Feedback", - // TODO New key - Add a translation - "info.feedback.title": "Feedback", + "info.feedback.title": "Кері байланыс", // "info.feedback.info": "Thanks for sharing your feedback about the DSpace system. Your comments are appreciated!", - // TODO New key - Add a translation - "info.feedback.info": "Thanks for sharing your feedback about the DSpace system. Your comments are appreciated!", + "info.feedback.info": "DSpace жүйесі туралы пікіріңізді бөліскеніңіз үшін рахмет. Біз сіздің пікірлеріңізді бағалаймыз!", // "info.feedback.email_help": "This address will be used to follow up on your feedback.", - // TODO New key - Add a translation - "info.feedback.email_help": "This address will be used to follow up on your feedback.", + "info.feedback.email_help": "Бұл мекен-жай сіздің пікірлеріңізді бақылау үшін қолданылады.", // "info.feedback.send": "Send Feedback", - // TODO New key - Add a translation - "info.feedback.send": "Send Feedback", + "info.feedback.send": "Пікір жіберу", // "info.feedback.comments": "Comments", - // TODO New key - Add a translation - "info.feedback.comments": "Comments", + "info.feedback.comments": "Түсініктеме", // "info.feedback.email-label": "Your Email", - // TODO New key - Add a translation - "info.feedback.email-label": "Your Email", + "info.feedback.email-label": "Сіздің пошта", // "info.feedback.create.success" : "Feedback Sent Successfully!", - // TODO New key - Add a translation - "info.feedback.create.success" : "Feedback Sent Successfully!", + "info.feedback.create.success" : "Кері Байланыс Сәтті Жіберілді!", // "info.feedback.error.email.required" : "A valid email address is required", - // TODO New key - Add a translation - "info.feedback.error.email.required" : "A valid email address is required", + "info.feedback.error.email.required" : "Жарамды электрондық пошта мекенжайы қажет", // "info.feedback.error.message.required" : "A comment is required", - // TODO New key - Add a translation - "info.feedback.error.message.required" : "A comment is required", + "info.feedback.error.message.required" : "Түсініктеме қажет", // "info.feedback.page-label" : "Page", - // TODO New key - Add a translation - "info.feedback.page-label" : "Page", + "info.feedback.page-label" : "Бет", // "info.feedback.page_help" : "Tha page related to your feedback", - // TODO New key - Add a translation - "info.feedback.page_help" : "Tha page related to your feedback", + "info.feedback.page_help" : "Сіздің пікіріңізге байланысты бет", @@ -2964,8 +2775,7 @@ "item.edit.breadcrumbs": "Элементті өңдеу", // "item.edit.tabs.disabled.tooltip": "You're not authorized to access this tab", - // TODO New key - Add a translation - "item.edit.tabs.disabled.tooltip": "You're not authorized to access this tab", + "item.edit.tabs.disabled.tooltip": "Бұл қойындыға кіру үшін рұқсат етілмеген", // "item.edit.tabs.mapper.head": "Collection Mapper", @@ -3114,16 +2924,13 @@ // "item.edit.move.cancel": "Back", - // TODO Source message changed - Revise the translation - "item.edit.move.cancel": "Cancel", + "item.edit.move.cancel": "Артқа", // "item.edit.move.save-button": "Save", - // TODO New key - Add a translation - "item.edit.move.save-button": "Save", + "item.edit.move.save-button": "Сақтау", // "item.edit.move.discard-button": "Discard", - // TODO New key - Add a translation - "item.edit.move.discard-button": "Discard", + "item.edit.move.discard-button": "Тастау", // "item.edit.move.description": "Select the collection you wish to move this item to. To narrow down the list of displayed collections, you can enter a search query in the box.", "item.edit.move.description": "Осы элементті жылжытқыңыз келетін коллекцияны таңдаңыз. Көрсетілген жинақтардың тізімін тарылту үшін өріске іздеу сұрауын енгізуге болады.", @@ -3161,23 +2968,18 @@ "item.edit.private.cancel":"Болдырмау", // "item.edit.private.confirm": "Make it non-discoverable", - // TODO Source message changed - Revise the translation "item.edit.private.confirm": "Мұны жеке жаса", // "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": "Сіз бұл затты мұрағатта жабу керек екеніне сенімдісіз бе?", // "item.edit.private.error": "An error occurred while making the item non-discoverable", - // TODO Source message changed - Revise the translation "item.edit.private.error": "Элементті жабу кезінде қате пайда болды", // "item.edit.private.header": "Make item non-discoverable: {{ id }}", - // TODO New key - Add a translation - "item.edit.private.header": "Make item non-discoverable: {{ id }}", + "item.edit.private.header": "Элементті табу мүмкін емес: {{ id }}", // "item.edit.private.success": "The item is now non-discoverable", - // TODO Source message changed - Revise the translation "item.edit.private.success": "Тауар қазір жеке", @@ -3186,23 +2988,18 @@ "item.edit.public.cancel": "Болдырмау", // "item.edit.public.confirm": "Make it discoverable", - // TODO Source message changed - Revise the translation "item.edit.public.confirm": "Мұны көпшілікке жария ету", // "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": "Сіз бұл материалды мұрағатта жариялау керек екеніне сенімдісіз бе?", // "item.edit.public.error": "An error occurred while making the item discoverable", - // TODO Source message changed - Revise the translation "item.edit.public.error": "Элементті жариялау кезінде қате пайда болды", // "item.edit.public.header": "Make item discoverable: {{ id }}", - // TODO New key - Add a translation - "item.edit.public.header": "Make item discoverable: {{ id }}", + "item.edit.public.header": "Элементті анықтауға қол жетімді етіңіз: {{ id }}", // "item.edit.public.success": "The item is now discoverable", - // TODO Source message changed - Revise the translation "item.edit.public.success": "Тауар қазір көпшілікке қол жетімді", @@ -3214,7 +3011,6 @@ "item.edit.reinstate.confirm": "Қалпына келтіру", // "item.edit.reinstate.description": "Are you sure this item should be reinstated to the archive?", - // TODO Source message changed - Revise the translation "item.edit.reinstate.description": "Сіз бұл элементті мұрағатта қалпына келтіру керек екеніне сенімдісіз бе?", // "item.edit.reinstate.error": "An error occurred while reinstating the item", @@ -3275,8 +3071,7 @@ // "item.edit.return": "Back", - // TODO New key - Add a translation - "item.edit.return": "Back", + "item.edit.return": "Артқа", // "item.edit.tabs.bitstreams.head": "Bitstreams", @@ -3328,19 +3123,15 @@ "item.edit.tabs.status.buttons.move.label": "Элементті басқа жинаққа жылжытыңыз", // "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.label": "Make item non-discoverable", - // TODO Source message changed - Revise the translation "item.edit.tabs.status.buttons.private.label": "Затты жеке ету", // "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.label": "Make item discoverable", - // TODO Source message changed - Revise the translation "item.edit.tabs.status.buttons.public.label": "Өнімді жалпыға қол жетімді ету", // "item.edit.tabs.status.buttons.reinstate.button": "Reinstate...", @@ -3416,8 +3207,7 @@ "item.edit.withdraw.success": "Тауар сәтті алынды", // "item.orcid.return": "Back", - // TODO New key - Add a translation - "item.orcid.return": "Back", + "item.orcid.return": "Артқа", // "item.listelement.badge": "Item", @@ -3445,16 +3235,13 @@ "item.search.results.head": "Өнімді іздеу нәтижелері", // "item.search.title": "Item Search", - // TODO Source message changed - Revise the translation - "item.search.title": "DSpace Angular :: элементтерді іздеу", + "item.search.title": "Элементтерді іздеу", // "item.truncatable-part.show-more": "Show more", - // TODO New key - Add a translation - "item.truncatable-part.show-more": "Show more", + "item.truncatable-part.show-more": "Толығырақ көрсету", // "item.truncatable-part.show-less": "Collapse", - // TODO New key - Add a translation - "item.truncatable-part.show-less": "Collapse", + "item.truncatable-part.show-less": "Жинақтау", @@ -3471,12 +3258,10 @@ "item.page.collections": "Жинақтар", // "item.page.collections.loading": "Loading...", - // TODO New key - Add a translation - "item.page.collections.loading": "Loading...", + "item.page.collections.loading": "Жүктелуде...", // "item.page.collections.load-more": "Load more", - // TODO New key - Add a translation - "item.page.collections.load-more": "Load more", + "item.page.collections.load-more": "Толығырақ жүктеу", // "item.page.date": "Date", "item.page.date": "Кездесуге", @@ -3512,12 +3297,10 @@ "item.page.link.simple": "Тауардың қарапайым беті", // "item.page.orcid.title": "ORCID", - // TODO New key - Add a translation "item.page.orcid.title": "ORCID", // "item.page.orcid.tooltip": "Open ORCID setting page", - // TODO New key - Add a translation - "item.page.orcid.tooltip": "Open ORCID setting page", + "item.page.orcid.tooltip": "ORCID параметрлері бетін ашыңыз", // "item.page.person.search.title": "Articles by this author", "item.page.person.search.title": "Осы автордың мақалалары", @@ -3559,24 +3342,19 @@ "item.page.filesection.license.bundle" : "Лицензиялық пакет", // "item.page.return": "Back", - // TODO New key - Add a translation - "item.page.return": "Back", + "item.page.return": "Артқа", // "item.page.version.create": "Create new version", - // TODO New key - Add a translation - "item.page.version.create": "Create new version", + "item.page.version.create": "Жаңа нұсқасын жасау", // "item.page.version.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history", - // TODO New key - Add a translation - "item.page.version.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history", + "item.page.version.hasDraft": "Жаңа нұсқаны жасау мүмкін емес, өйткені нұсқа тарихында аяқталмаған жіберу бар", // "item.page.claim.button": "Claim", - // TODO New key - Add a translation - "item.page.claim.button": "Claim", + "item.page.claim.button": "Талабы", // "item.page.claim.tooltip": "Claim this item as profile", - // TODO New key - Add a translation - "item.page.claim.tooltip": "Claim this item as profile", + "item.page.claim.tooltip": "Бұл элементті профиль ретінде көрсетіңіз", // "item.preview.dc.identifier.uri": "Identifier:", "item.preview.dc.identifier.uri": "Идентификатор:", @@ -3603,35 +3381,27 @@ "item.preview.dc.title": "Атауы:", // "item.preview.dc.type": "Type:", - // TODO New key - Add a translation - "item.preview.dc.type": "Type:", + "item.preview.dc.type": "Типі:", // "item.preview.oaire.citation.issue" : "Issue", - // TODO New key - Add a translation - "item.preview.oaire.citation.issue" : "Issue", + "item.preview.oaire.citation.issue" : "Шығарылымы", // "item.preview.oaire.citation.volume" : "Volume", - // TODO New key - Add a translation - "item.preview.oaire.citation.volume" : "Volume", + "item.preview.oaire.citation.volume" : "Көлемі", // "item.preview.dc.relation.issn" : "ISSN", - // TODO New key - Add a translation "item.preview.dc.relation.issn" : "ISSN", // "item.preview.dc.identifier.isbn" : "ISBN", - // TODO New key - Add a translation "item.preview.dc.identifier.isbn" : "ISBN", // "item.preview.dc.identifier": "Identifier:", - // TODO New key - Add a translation - "item.preview.dc.identifier": "Identifier:", + "item.preview.dc.identifier": "Идентификатор:", // "item.preview.dc.relation.ispartof" : "Journal or Serie", - // TODO New key - Add a translation - "item.preview.dc.relation.ispartof" : "Journal or Serie", + "item.preview.dc.relation.ispartof" : "Журнал немесе серия", // "item.preview.dc.identifier.doi" : "DOI", - // TODO New key - Add a translation "item.preview.dc.identifier.doi" : "DOI", // "item.preview.person.familyName": "Surname:", @@ -3644,28 +3414,22 @@ "item.preview.person.identifier.orcid": "ORCID:", // "item.preview.project.funder.name": "Funder:", - // TODO New key - Add a translation - "item.preview.project.funder.name": "Funder:", + "item.preview.project.funder.name": "Демеуші:", // "item.preview.project.funder.identifier": "Funder Identifier:", - // TODO New key - Add a translation - "item.preview.project.funder.identifier": "Funder Identifier:", + "item.preview.project.funder.identifier": "Демеуші идентификаторы:", // "item.preview.oaire.awardNumber": "Funding ID:", - // TODO New key - Add a translation - "item.preview.oaire.awardNumber": "Funding ID:", + "item.preview.oaire.awardNumber": "Қаржыландыру сәйкестендіргіші:", // "item.preview.dc.title.alternative": "Acronym:", - // TODO New key - Add a translation - "item.preview.dc.title.alternative": "Acronym:", + "item.preview.dc.title.alternative": "Акроним:", // "item.preview.dc.coverage.spatial": "Jurisdiction:", - // TODO New key - Add a translation - "item.preview.dc.coverage.spatial": "Jurisdiction:", + "item.preview.dc.coverage.spatial": "Юрисдикция:", // "item.preview.oaire.fundingStream": "Funding Stream:", - // TODO New key - Add a translation - "item.preview.oaire.fundingStream": "Funding Stream:", + "item.preview.oaire.fundingStream": "Қаржыландыру ағыны:", @@ -3699,8 +3463,7 @@ "item.version.history.selected": "Таңдалған нұсқа", // "item.version.history.selected.alert": "You are currently viewing version {{version}} of the item.", - // TODO New key - Add a translation - "item.version.history.selected.alert": "You are currently viewing version {{version}} of the item.", + "item.version.history.selected.alert": "Осы кезде сіз элементтің {{version}} нұсқасын қарап шығасыз.", // "item.version.history.table.version": "Version", "item.version.history.table.version": "Нұсқа", @@ -3718,44 +3481,34 @@ "item.version.history.table.summary": "Қысқаша мазмұны", // "item.version.history.table.workspaceItem": "Workspace item", - // TODO New key - Add a translation - "item.version.history.table.workspaceItem": "Workspace item", + "item.version.history.table.workspaceItem": "Жұмыс аймағының элементі", // "item.version.history.table.workflowItem": "Workflow item", - // TODO New key - Add a translation - "item.version.history.table.workflowItem": "Workflow item", + "item.version.history.table.workflowItem": "Жұмыс процесінің элементі", // "item.version.history.table.actions": "Action", - // TODO New key - Add a translation - "item.version.history.table.actions": "Action", + "item.version.history.table.actions": "Әрекет", // "item.version.history.table.action.editWorkspaceItem": "Edit workspace item", - // TODO New key - Add a translation - "item.version.history.table.action.editWorkspaceItem": "Edit workspace item", + "item.version.history.table.action.editWorkspaceItem": "Жұмыс аймағының элементін өңдеу", // "item.version.history.table.action.editSummary": "Edit summary", - // TODO New key - Add a translation - "item.version.history.table.action.editSummary": "Edit summary", + "item.version.history.table.action.editSummary": "Қорытындыны өзгерту", // "item.version.history.table.action.saveSummary": "Save summary edits", - // TODO New key - Add a translation - "item.version.history.table.action.saveSummary": "Save summary edits", + "item.version.history.table.action.saveSummary": "Қорытынды түзетулерді сақтау", // "item.version.history.table.action.discardSummary": "Discard summary edits", - // TODO New key - Add a translation - "item.version.history.table.action.discardSummary": "Discard summary edits", + "item.version.history.table.action.discardSummary": "Қорытынды түзетулерді болдырмау", // "item.version.history.table.action.newVersion": "Create new version from this one", - // TODO New key - Add a translation - "item.version.history.table.action.newVersion": "Create new version from this one", + "item.version.history.table.action.newVersion": "Осыдан жаңа нұсқа жасаңыз", // "item.version.history.table.action.deleteVersion": "Delete version", - // TODO New key - Add a translation - "item.version.history.table.action.deleteVersion": "Delete version", + "item.version.history.table.action.deleteVersion": "Нұсқаны өшіру", // "item.version.history.table.action.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history", - // TODO New key - Add a translation - "item.version.history.table.action.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history", + "item.version.history.table.action.hasDraft": "Жаңа нұсқаны жасау мүмкін емес, өйткені нұсқа тарихында аяқталмаған бағынымдар бар", // "item.version.notice": "This is not the latest version of this item. The latest version can be found here.", From 9aca40cc33a3761a24f501d623ba9a814492b5f1 Mon Sep 17 00:00:00 2001 From: myrza1 Date: Mon, 26 Sep 2022 02:23:48 +0600 Subject: [PATCH 031/758] Update kk.json5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit submission.import-external.preview.title.Project төменнен жоғары тоқтадым --- src/assets/i18n/kk.json5 | 744 +++++++++++++-------------------------- 1 file changed, 254 insertions(+), 490 deletions(-) diff --git a/src/assets/i18n/kk.json5 b/src/assets/i18n/kk.json5 index b78df8ac5e5..84d3ba907de 100644 --- a/src/assets/i18n/kk.json5 +++ b/src/assets/i18n/kk.json5 @@ -2597,7 +2597,6 @@ // "item.alerts.private": "This item is non-discoverable", - // TODO Source message changed - Revise the translation "item.alerts.private": "Бұл элемент жеке", // "item.alerts.withdrawn": "This item has been withdrawn", @@ -4361,7 +4360,7 @@ "person.page.email": "Электрондық мекенжайы", // "person.page.firstname": "First Name", - "person.page.firstname":"Имя", + "person.page.firstname":"Аты", // "person.page.jobtitle": "Job Title", "person.page.jobtitle": "Жұмыс атауы", @@ -4370,8 +4369,7 @@ "person.page.lastname": "Фамилия", // "person.page.name": "Name", - // TODO New key - Add a translation - "person.page.name": "Name", + "person.page.name": "Аты", // "person.page.link.full": "Show all metadata", "person.page.link.full": "Барлық метамәліметті көрсету", @@ -4383,18 +4381,16 @@ "person.page.staffid": "Қызметкерлердің жеке куәлігі", // "person.page.titleprefix": "Person: ", - "person.page.titleprefix":"Адам: ", + "person.page.titleprefix": "Адам: ", // "person.search.results.head": "Person Search Results", "person.search.results.head": "Тұлға іздеу нәтижелері", // "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": "Тұлға іздеу нәтижелері", // "person.search.title": "Person Search", - // TODO Source message changed - Revise the translation - "person.search.title": "DSpace Angular :: Адамды іздеу", + "person.search.title": "Адамды іздеу", @@ -4731,8 +4727,7 @@ "project.search.results.head": "Жобаны іздеу нәтижелері", // "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": "Жобаны іздеу нәтижелері", @@ -4764,12 +4759,10 @@ "publication.search.results.head": "Жарияланымдарды іздеу нәтижелері", // "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": "Жарияланымды іздеу нәтижелері", // "publication.search.title": "Publication Search", - // TODO Source message changed - Revise the translation - "publication.search.title": "DSpace Angular :: Жарияланымдарды іздеу", + "publication.search.title": "Жарияланымдарды іздеу", // "media-viewer.next": "Next", @@ -5150,15 +5143,13 @@ "search.switch-configuration.title": "Көрсету", // "search.title": "Search", - // TODO Source message changed - Revise the translation - "search.title": "DSpace Angular :: Іздеу", + "search.title": "Іздеу", // "search.breadcrumbs": "Search", "search.breadcrumbs": "Іздеу", // "search.search-form.placeholder": "Search the repository ...", - // TODO New key - Add a translation - "search.search-form.placeholder": "Search the repository ...", + "search.search-form.placeholder": " Репозиторидең іздеу ...", // "search.filters.applied.f.author": "Author", @@ -5260,8 +5251,7 @@ "search.filters.filter.creativeWorkKeywords.placeholder": "Тақырыбы", // "search.filters.filter.creativeWorkKeywords.label": "Search subject", - // TODO New key - Add a translation - "search.filters.filter.creativeWorkKeywords.label": "Search subject", + "search.filters.filter.creativeWorkKeywords.label": "Іздеу тақырыбы", // "search.filters.filter.creativeWorkPublisher.head": "Publisher", "search.filters.filter.creativeWorkPublisher.head": "Баспагер", @@ -5303,7 +5293,6 @@ "search.filters.filter.dateSubmitted.label": "Search date submitted", // "search.filters.filter.discoverable.head": "Non-discoverable", - // TODO Source message changed - Revise the translation "search.filters.filter.discoverable.head": "Жеке", // "search.filters.filter.withdrawn.head": "Withdrawn", @@ -5479,8 +5468,7 @@ "search.filters.reset": "Сүзгілерді қалпына келтіру", // "search.filters.search.submit": "Submit", - // TODO New key - Add a translation - "search.filters.search.submit": "Submit", + "search.filters.search.submit": "Орындау", @@ -5488,12 +5476,10 @@ "search.form.search":"Іздеу", // "search.form.search_dspace": "All repository", - // TODO Source message changed - Revise the translation - "search.form.search_dspace": "Іздеу кеңістігі", + "search.form.search_dspace": "Бүкіл репозиторий", // "search.form.scope.all": "All of DSpace", - // TODO New key - Add a translation - "search.form.scope.all": "All of DSpace", + "search.form.scope.all": "Бүкіл DSpace", @@ -5836,8 +5822,7 @@ "submission.import-external.preview.title.Person": "Person Preview", // "submission.import-external.preview.title.Project": "Project Preview", - // TODO New key - Add a translation - "submission.import-external.preview.title.Project": "Project Preview", + "submission.import-external.preview.title.Project": "Жобаны алдын ала қарау", // "submission.import-external.preview.subtitle": "The metadata below was imported from an external source. It will be pre-filled when you start the submission.", "submission.import-external.preview.subtitle": "Төмендегі метадеректер сыртқы көзден импортталды. Сіз жіберуді бастаған кезде ол алдын-ала толтырылады.", @@ -5870,60 +5855,46 @@ "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Volume": "Журналдың қашықтағы томын импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.isProjectOfPublication": "Project", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.isProjectOfPublication": "Project", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.isProjectOfPublication": "Жоба", // "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": "Қашықтағы элементті импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Event": "Import remote event", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Event": "Import remote event", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Event": "Қашықтағы оқиғаны импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Product": "Import remote product", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Product": "Import remote product", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Product": "Қашықтағы өнімді импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Equipment": "Import remote equipment", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Equipment": "Import remote equipment", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Equipment": "Қашықтағы жабдықты импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.OrgUnit": "Import remote organizational unit", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.OrgUnit": "Import remote organizational unit", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.OrgUnit": "Қашықтағы ұйымдастыру бөлімшесінің импорты", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Funding": "Import remote fund", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Funding": "Import remote fund", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Funding": "Қашықтағы қорды импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Person": "Import remote person", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Person": "Import remote person", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Person": "Қашықтағы пайдаланушыны импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Patent": "Import remote patent", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Patent": "Import remote patent", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Patent": "Патентті қашықтан импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Project": "Import remote project", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Project": "Import remote project", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Project": "Қашықтағы жобаны импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Publication": "Import remote publication", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-button-title.Publication": "Import remote publication", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Publication": "Қашықтағы жарияланымды импорттау", // "submission.sections.describe.relationship-lookup.external-source.import-modal.isProjectOfPublication.added.new-entity": "New Entity Added!", - // TODO New key - Add a translation - "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": "Жаңа Нысан Қосылды!", // "submission.sections.describe.relationship-lookup.external-source.import-modal.isProjectOfPublication.title": "Project", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-modal.isProjectOfPublication.title": "Project", + "submission.sections.describe.relationship-lookup.external-source.import-modal.isProjectOfPublication.title": "Жоба", // "submission.sections.describe.relationship-lookup.external-source.import-modal.head.openAIREFunding": "Funding OpenAIRE API", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.external-source.import-modal.head.openAIREFunding": "Funding OpenAIRE API", + "submission.sections.describe.relationship-lookup.external-source.import-modal.head.openAIREFunding": "Демеуші OpenAIRE API", // "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.title": "Import Remote Author", "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.title": "Қашықтағы авторды импорттау", @@ -6019,8 +5990,7 @@ "submission.sections.describe.relationship-lookup.search-tab.search": "Бару", // "submission.sections.describe.relationship-lookup.search-tab.search-form.placeholder": "Search...", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.search-form.placeholder": "Search...", + "submission.sections.describe.relationship-lookup.search-tab.search-form.placeholder": "Іздеу...", // "submission.sections.describe.relationship-lookup.search-tab.select-all": "Select all", "submission.sections.describe.relationship-lookup.search-tab.select-all": "Барлығын таңдау", @@ -6036,6 +6006,7 @@ // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Local Journals ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Жергілікті журналдар ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Project": "Local Projects ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.Project": "Жергілікті жобалар ({{ count }})", @@ -6059,11 +6030,13 @@ // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalIssueOfPublication": "Local Journal Issues ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalIssueOfPublication": "Жергілікті журнал мәселелері ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalIssue": "Local Journal Issues ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalIssue": "Жергілікті журнал мәселелері ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalVolumeOfPublication": "Local Journal Volumes ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalVolumeOfPublication": "Жергілікті журнал томдары ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalVolume": "Local Journal Volumes ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalVolume": "Жергілікті журнал томдары ({{ count }})", @@ -6071,7 +6044,7 @@ "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaJournal":"Sherpa журналдары ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa Publishers ({{ count }})", - "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa Publishers ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa баспалары ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.orcid": "ORCID ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.orcid": "ORCID ({{ count }})", @@ -6095,50 +6068,44 @@ "submission.sections.describe.relationship-lookup.search-tab.tab-title.isChildOrgUnitOf": "Ұйымдастырушылық бірліктерді іздеу", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.openAIREFunding": "Funding OpenAIRE API", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.openAIREFunding": "Funding OpenAIRE API", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.openAIREFunding": "Демеуші OpenAIRE API", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isProjectOfPublication": "Projects", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isProjectOfPublication": "Projects", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isProjectOfPublication": "Жобалар", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfProject": "Funder of the Project", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfProject": "Funder of the Project", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfProject": "Жоба демеушісі", // "submission.sections.describe.relationship-lookup.selection-tab.title.openAIREFunding": "Funding OpenAIRE API", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.selection-tab.title.openAIREFunding": "Funding OpenAIRE API", + "submission.sections.describe.relationship-lookup.selection-tab.title.openAIREFunding": "Демеуші OpenAIRE API", // "submission.sections.describe.relationship-lookup.selection-tab.title.isProjectOfPublication": "Project", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.selection-tab.title.isProjectOfPublication": "Project", + "submission.sections.describe.relationship-lookup.selection-tab.title.isProjectOfPublication": "Жоба", // "submission.sections.describe.relationship-lookup.title.isProjectOfPublication": "Projects", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.title.isProjectOfPublication": "Projects", + "submission.sections.describe.relationship-lookup.title.isProjectOfPublication": "Жобалар", // "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfProject": "Funder of the Project", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfProject": "Funder of the Project", + "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfProject": "Жоба демеушісі", // "submission.sections.describe.relationship-lookup.selection-tab.search-form.placeholder": "Search...", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.selection-tab.search-form.placeholder": "Search...", + "submission.sections.describe.relationship-lookup.selection-tab.search-form.placeholder": "Іздеу...", // "submission.sections.describe.relationship-lookup.selection-tab.tab-title": "Current Selection ({{ count }})", "submission.sections.describe.relationship-lookup.selection-tab.tab-title":"Ағымдағы таңдау ({{ count }})", // "submission.sections.describe.relationship-lookup.title.isJournalIssueOfPublication": "Journal Issues", "submission.sections.describe.relationship-lookup.title.isJournalIssueOfPublication": "Журналдың шығарылымдары", + // "submission.sections.describe.relationship-lookup.title.JournalIssue": "Journal Issues", "submission.sections.describe.relationship-lookup.title.JournalIssue":"Журналдың шығарылымдары", // "submission.sections.describe.relationship-lookup.title.isJournalVolumeOfPublication": "Journal Volumes", "submission.sections.describe.relationship-lookup.title.isJournalVolumeOfPublication":"Журнал томдары", + // "submission.sections.describe.relationship-lookup.title.JournalVolume": "Journal Volumes", "submission.sections.describe.relationship-lookup.title.JournalVolume": "Журнал томдары", @@ -6150,6 +6117,7 @@ // "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfPublication": "Funding Agency", "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfPublication": "Қаржыландыру агенттігі", + // "submission.sections.describe.relationship-lookup.title.Project": "Projects", "submission.sections.describe.relationship-lookup.title.Project": "Жобалар", @@ -6194,6 +6162,7 @@ // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalVolumeOfPublication": "Selected Journal Volume", "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalVolumeOfPublication": "Журналдың таңдалған көлемі", + // "submission.sections.describe.relationship-lookup.selection-tab.title.Project": "Selected Projects", "submission.sections.describe.relationship-lookup.selection-tab.title.Project": "Таңдаулы жобалар", @@ -6217,6 +6186,7 @@ // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalIssueOfPublication": "Selected Issue", "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalIssueOfPublication": "Таңдалған сұрақ", + // "submission.sections.describe.relationship-lookup.selection-tab.title.JournalVolume": "Selected Journal Volume", "submission.sections.describe.relationship-lookup.selection-tab.title.JournalVolume":"Журналдың таңдалған көлемі", @@ -6225,6 +6195,7 @@ // "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingOfPublication": "Selected Funding", "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingOfPublication": "Таңдалған қаржыландыру", + // "submission.sections.describe.relationship-lookup.selection-tab.title.JournalIssue": "Selected Issue", "submission.sections.describe.relationship-lookup.selection-tab.title.JournalIssue": "Таңдалған шығарылым", @@ -6304,8 +6275,7 @@ "submission.sections.general.add-more": "Тағы қосу", // "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": "Пішіндегі қателіктерге байланысты Депозит аяқталуы мүмкін емес.
Депозитті аяқтау үшін барлық қажетті өрістерді толтырыңыз.", // "submission.sections.general.collection": "Collection", "submission.sections.general.collection": "Жинақ", @@ -6373,67 +6343,53 @@ "submission.sections.submit.progressbar.license": "Депозиттік лицензия", // "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": "Sherpa саясаттары", // "submission.sections.submit.progressbar.upload": "Upload files", "submission.sections.submit.progressbar.upload": "Файлдарды жүктеу", // "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": "Баспагердің ашық қол жеткізу саясаты туралы ақпарат", // "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": "Баспагердің саясаты туралы ақпарат жоқ. Егер сіздің жұмысыңыз ISSN-мен байланысты болса, баспагердің кез-келген тиісті ашық кіру саясатымен танысу үшін оны жоғарыда енгізіңіз.", // "submission.sections.status.errors.title": "Errors", "submission.sections.status.errors.title": "Қателер", // "submission.sections.status.valid.title": "Valid", - // TODO New key - Add a translation - "submission.sections.status.valid.title": "Valid", + "submission.sections.status.valid.title": "Жарамды", // "submission.sections.status.warnings.title": "Warnings", - // TODO New key - Add a translation - "submission.sections.status.warnings.title": "Warnings", + "submission.sections.status.warnings.title": "Ескертулер", // "submission.sections.status.errors.aria": "has errors", - // TODO New key - Add a translation - "submission.sections.status.errors.aria": "has errors", + "submission.sections.status.errors.aria": "қателер бар", // "submission.sections.status.valid.aria": "is valid", - // TODO New key - Add a translation - "submission.sections.status.valid.aria": "is valid", + "submission.sections.status.valid.aria": "жарамды болып табылады", // "submission.sections.status.warnings.aria": "has warnings", - // TODO New key - Add a translation - "submission.sections.status.warnings.aria": "has warnings", + "submission.sections.status.warnings.aria": "ескертулер бар", // "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": "Қосымша ақпарат", // "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": "Қосымша ақпарат", // "submission.sections.toggle.open": "Open section", - // TODO New key - Add a translation - "submission.sections.toggle.open": "Open section", + "submission.sections.toggle.open": "Бөлімді ашу", // "submission.sections.toggle.close": "Close section", - // TODO New key - Add a translation - "submission.sections.toggle.close": "Close section", + "submission.sections.toggle.close": "Бөлімді жабу", // "submission.sections.toggle.aria.open": "Expand {{sectionHeader}} section", - // TODO New key - Add a translation - "submission.sections.toggle.aria.open": "Expand {{sectionHeader}} section", + "submission.sections.toggle.aria.open": "Бөлімді ашу {{sectionHeader}}", // "submission.sections.toggle.aria.close": "Collapse {{sectionHeader}} section", - // TODO New key - Add a translation - "submission.sections.toggle.aria.close": "Collapse {{sectionHeader}} section", + "submission.sections.toggle.aria.close": "Бөлімді кішірейту {{sectionHeader}}", // "submission.sections.upload.delete.confirm.cancel": "Cancel", "submission.sections.upload.delete.confirm.cancel": "Болдырмау", @@ -6463,26 +6419,22 @@ "submission.sections.upload.form.access-condition-label": "Кіру шартының түрі", // "submission.sections.upload.form.access-condition-hint": "Select an access condition to apply on the bitstream once the item is deposited", - // TODO New key - Add a translation - "submission.sections.upload.form.access-condition-hint": "Select an access condition to apply on the bitstream once the item is deposited", + "submission.sections.upload.form.access-condition-hint": "Элементті сақтағаннан кейін бит ағынына қолданылатын кіру шартын таңдаңыз", // "submission.sections.upload.form.date-required": "Date is required.", "submission.sections.upload.form.date-required": "Күні міндетті.", // "submission.sections.upload.form.date-required-from": "Grant access from date is required.", - // TODO New key - Add a translation - "submission.sections.upload.form.date-required-from": "Grant access from date is required.", + "submission.sections.upload.form.date-required-from": "Күннен бастап қол жеткізуді қамтамасыз ету қажет.", // "submission.sections.upload.form.date-required-until": "Grant access until date is required.", - // TODO New key - Add a translation - "submission.sections.upload.form.date-required-until": "Grant access until date is required.", + "submission.sections.upload.form.date-required-until": "Күн қажет болғанша қол жеткізіңіз.", // "submission.sections.upload.form.from-label": "Grant access from", "submission.sections.upload.form.from-label": "Қатынауды ұсыну бірі", // "submission.sections.upload.form.from-hint": "Select the date from which the related access condition is applied", - // TODO New key - Add a translation - "submission.sections.upload.form.from-hint": "Select the date from which the related access condition is applied", + "submission.sections.upload.form.from-hint": "Тиісті кіру шарты қолданылатын күнді таңдаңыз", // "submission.sections.upload.form.from-placeholder": "From", "submission.sections.upload.form.from-placeholder": "Кімнен", @@ -6497,11 +6449,10 @@ "submission.sections.upload.form.until-label": "Рұқсат беруге болғанша", // "submission.sections.upload.form.until-hint": "Select the date until which the related access condition is applied", - // TODO New key - Add a translation - "submission.sections.upload.form.until-hint": "Select the date until which the related access condition is applied", + "submission.sections.upload.form.until-hint": "Тиісті кіру шарты қолданылатын күнді таңдаңыз", // "submission.sections.upload.form.until-placeholder": "Until", - "submission.sections.upload.form.until-placeholder":"Әзірге", + "submission.sections.upload.form.until-placeholder": "Әзірге", // "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": "{{collectionName}} жинағына жүктелген файлдар келесі топқа (топтарға) сәйкес қол жетімді болады:", @@ -6531,85 +6482,65 @@ "submission.sections.upload.upload-successful": "Жүктеу сәтті өтті", // "submission.sections.accesses.form.discoverable-description": "When checked, this item will be discoverable in search/browse. When unchecked, the item will only be available via a direct link and will never appear in search/browse.", - // TODO New key - Add a translation - "submission.sections.accesses.form.discoverable-description": "When checked, this item will be discoverable in search/browse. When unchecked, the item will only be available via a direct link and will never appear in search/browse.", + "submission.sections.accesses.form.discoverable-description": "Егер бұл құсбелгі қойылса, бұл элемент іздеу/қарау үшін қол жетімді болады. Егер құсбелгі алынып тасталса, элемент тек тікелей сілтеме арқылы қол жетімді болады және іздеу/қарау кезінде ешқашан пайда болмайды.", // "submission.sections.accesses.form.discoverable-label": "Discoverable", - // TODO New key - Add a translation - "submission.sections.accesses.form.discoverable-label": "Discoverable", + "submission.sections.accesses.form.discoverable-label": "Анықталатын", // "submission.sections.accesses.form.access-condition-label": "Access condition type", - // TODO New key - Add a translation - "submission.sections.accesses.form.access-condition-label": "Access condition type", + "submission.sections.accesses.form.access-condition-label": "Кіру шартының түрі", // "submission.sections.accesses.form.access-condition-hint": "Select an access condition to apply on the item once it is deposited", - // TODO New key - Add a translation - "submission.sections.accesses.form.access-condition-hint": "Select an access condition to apply on the item once it is deposited", + "submission.sections.accesses.form.access-condition-hint": "Депозитке салынғаннан кейін өнімге қолданылатын қол жеткізу шартын таңдаңыз", // "submission.sections.accesses.form.date-required": "Date is required.", - // TODO New key - Add a translation - "submission.sections.accesses.form.date-required": "Date is required.", + "submission.sections.accesses.form.date-required": "Күні міндетті.", // "submission.sections.accesses.form.date-required-from": "Grant access from date is required.", - // TODO New key - Add a translation - "submission.sections.accesses.form.date-required-from": "Grant access from date is required.", + "submission.sections.accesses.form.date-required-from": "Күннен бастап қол жеткізуді қамтамасыз ету қажет.", // "submission.sections.accesses.form.date-required-until": "Grant access until date is required.", - // TODO New key - Add a translation - "submission.sections.accesses.form.date-required-until": "Grant access until date is required.", + "submission.sections.accesses.form.date-required-until": "Күн қажет болғанша қол жеткізіңіз.", // "submission.sections.accesses.form.from-label": "Grant access from", - // TODO New key - Add a translation - "submission.sections.accesses.form.from-label": "Grant access from", + "submission.sections.accesses.form.from-label": "Қол жеткізуді қамтамасыз етіңіз", // "submission.sections.accesses.form.from-hint": "Select the date from which the related access condition is applied", - // TODO New key - Add a translation - "submission.sections.accesses.form.from-hint": "Select the date from which the related access condition is applied", + "submission.sections.accesses.form.from-hint": "Тиісті кіру шарты қолданылатын күнді таңдаңыз", // "submission.sections.accesses.form.from-placeholder": "From", - // TODO New key - Add a translation - "submission.sections.accesses.form.from-placeholder": "From", + "submission.sections.accesses.form.from-placeholder": "Бастап", // "submission.sections.accesses.form.group-label": "Group", - // TODO New key - Add a translation - "submission.sections.accesses.form.group-label": "Group", + "submission.sections.accesses.form.group-label": "Топ", // "submission.sections.accesses.form.group-required": "Group is required.", - // TODO New key - Add a translation - "submission.sections.accesses.form.group-required": "Group is required.", + "submission.sections.accesses.form.group-required": "Топ қажет.", // "submission.sections.accesses.form.until-label": "Grant access until", - // TODO New key - Add a translation - "submission.sections.accesses.form.until-label": "Grant access until", + "submission.sections.accesses.form.until-label": "Дейін қол жеткізуді қамтамасыз ету", // "submission.sections.accesses.form.until-hint": "Select the date until which the related access condition is applied", - // TODO New key - Add a translation - "submission.sections.accesses.form.until-hint": "Select the date until which the related access condition is applied", + "submission.sections.accesses.form.until-hint": "Тиісті кіру шарты қолданылатын күнді таңдаңыз", // "submission.sections.accesses.form.until-placeholder": "Until", - // TODO New key - Add a translation - "submission.sections.accesses.form.until-placeholder": "Until", + "submission.sections.accesses.form.until-placeholder": "Осы уақытқа дейін", // "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": "Мен жоғарыда аталған лицензияны растаймын", // "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": "Лицензияны қабылдау керек", // "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": "Лицензияны қабылдау керек", // "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": "Жарияланым туралы ақпарат", // "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": "Атауы", // "submission.sections.sherpa.publication.information.issns": "ISSNs", // TODO New key - Add a translation @@ -6620,102 +6551,78 @@ "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": "Баспагер", // "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.romeoPub": "Romeo Баспагер", // "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.publication.information.zetoPub": "Zeto Баспагер", // "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": "Баспагердің саясаты", // "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": "Төмендегі ақпарат Шерпа Ромео арқылы табылды. Баспагердің саясатына сүйене отырып, ол сізге эмбарго қажет пе және / немесе қандай файлдарды жүктеуге рұқсат етілгені туралы ұсыныстар береді. Егер сізде сұрақтар туындаса, төменгі деректемедегі кері байланыс нысаны арқылы веб-сайтыңыздың әкімшісіне хабарласыңыз.", // "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": "Осы журналдың саясатымен рұқсат етілген ашық қол жетімділік жолдары төменде мақалалардың нұсқалары бойынша бөлінген. Толығырақ көру үшін жолды нұқыңыз", // "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": "Қосымша ақпарат алу үшін келесі сілтемелерге өтіңіз:", // "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": "Нұсқа", // "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.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": "Эмбарго Жоқ", // "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": "Ешкім", // "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": "Лицензия", // "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": "Алғышарттар", // "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": "Орналасқан жері", // "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": "Шарттары", // "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": "Жаңарту", // "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": "Жазуға арналған ақпарат", // "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": "Құрылған күні", // "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": "Соңғы өзгеріс", // "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": "Шерпе туралы ақпарат алу кезінде қате пайда болды", // "submission.submit.breadcrumbs": "New submission", - // TODO New key - Add a translation - "submission.submit.breadcrumbs": "New submission", + "submission.submit.breadcrumbs": "Жаңа көрініс", // "submission.submit.title": "New submission", - // TODO Source message changed - Revise the translation - "submission.submit.title":"Бағыну", + "submission.submit.title": "Жаңа көрініс", @@ -6805,45 +6712,35 @@ // "submission.workspace.generic.view": "View", - // TODO New key - Add a translation - "submission.workspace.generic.view": "View", + "submission.workspace.generic.view": "Көрініс", // "submission.workspace.generic.view-help": "Select this option to view the item's metadata.", - // TODO New key - Add a translation - "submission.workspace.generic.view-help": "Select this option to view the item's metadata.", + "submission.workspace.generic.view-help": "Элемент метадеректерін көру үшін осы параметрді таңдаңыз.", // "thumbnail.default.alt": "Thumbnail Image", - // TODO New key - Add a translation - "thumbnail.default.alt": "Thumbnail Image", + "thumbnail.default.alt": "Кішірейтілген сурет", // "thumbnail.default.placeholder": "No Thumbnail Available", - // TODO New key - Add a translation - "thumbnail.default.placeholder": "No Thumbnail Available", + "thumbnail.default.placeholder": "Нобайлар Жоқ", // "thumbnail.project.alt": "Project Logo", - // TODO New key - Add a translation - "thumbnail.project.alt": "Project Logo", + "thumbnail.project.alt": "Жобаның логотипі", // "thumbnail.project.placeholder": "Project Placeholder Image", - // TODO New key - Add a translation - "thumbnail.project.placeholder": "Project Placeholder Image", + "thumbnail.project.placeholder": "Жобаның толтырғыш суреті", // "thumbnail.orgunit.alt": "OrgUnit Logo", - // TODO New key - Add a translation - "thumbnail.orgunit.alt": "OrgUnit Logo", + "thumbnail.orgunit.alt": "Ұйым бөлімшесінің логотипі", // "thumbnail.orgunit.placeholder": "OrgUnit Placeholder Image", - // TODO New key - Add a translation - "thumbnail.orgunit.placeholder": "OrgUnit Placeholder Image", + "thumbnail.orgunit.placeholder": "Мекеме бірлігін толтырушы суреті", // "thumbnail.person.alt": "Profile Picture", - // TODO New key - Add a translation - "thumbnail.person.alt": "Profile Picture", + "thumbnail.person.alt": "Профиль суреті", // "thumbnail.person.placeholder": "No Profile Picture Available", - // TODO New key - Add a translation - "thumbnail.person.placeholder": "No Profile Picture Available", + "thumbnail.person.placeholder": "Профиль Суреті Қол Жетімді Емес", @@ -6882,8 +6779,7 @@ "uploader.drag-message": "Файлдарды осында сүйреңіз", // "uploader.delete.btn-title": "Delete", - // TODO New key - Add a translation - "uploader.delete.btn-title": "Delete", + "uploader.delete.btn-title": "Жою", // "uploader.or": ", or ", "uploader.or": ", немесе ", @@ -6906,25 +6802,21 @@ // "workspace.search.results.head": "Your submissions", - // TODO New key - Add a translation - "workspace.search.results.head": "Your submissions", + "workspace.search.results.head": "Сіздің жазылымдарыңыз", // "workflowAdmin.search.results.head": "Administer Workflow", "workflowAdmin.search.results.head": "Жұмыс процесін басқару", // "workflow.search.results.head": "Workflow tasks", - // TODO New key - Add a translation - "workflow.search.results.head": "Workflow tasks", + "workflow.search.results.head": "Жұмыс процесінің міндеттері", // "workflow-item.edit.breadcrumbs": "Edit workflowitem", - // TODO New key - Add a translation - "workflow-item.edit.breadcrumbs": "Edit workflowitem", + "workflow-item.edit.breadcrumbs": "Жұмыс процесінің элементін өңдеу", // "workflow-item.edit.title": "Edit workflowitem", - // TODO New key - Add a translation - "workflow-item.edit.title": "Edit workflowitem", + "workflow-item.edit.title": "Жұмыс процесінің элементін өңдеу", // "workflow-item.delete.notification.success.title": "Deleted", "workflow-item.delete.notification.success.title":"Жойылды", @@ -6973,525 +6865,397 @@ "workflow-item.send-back.button.cancel": "Алып тастау", // "workflow-item.send-back.button.confirm": "Send back", - // TODO Source message changed - Revise the translation "workflow-item.send-back.button.confirm": "Кері жіберу", // "workflow-item.view.breadcrumbs": "Workflow View", - // TODO New key - Add a translation - "workflow-item.view.breadcrumbs": "Workflow View", + "workflow-item.view.breadcrumbs": "Жұмыс процесін көру", // "workspace-item.view.breadcrumbs": "Workspace View", - // TODO New key - Add a translation - "workspace-item.view.breadcrumbs": "Workspace View", + "workspace-item.view.breadcrumbs": "Жұмыс аймағының түрі", // "workspace-item.view.title": "Workspace View", - // TODO New key - Add a translation - "workspace-item.view.title": "Workspace View", + "workspace-item.view.title": "Жұмыс аймағының түрі", // "idle-modal.header": "Session will expire soon", - // TODO New key - Add a translation - "idle-modal.header": "Session will expire soon", + "idle-modal.header": "Сессия жақында аяқталады", // "idle-modal.info": "For security reasons, user sessions expire after {{ timeToExpire }} minutes of inactivity. Your session will expire soon. Would you like to extend it or log out?", - // TODO New key - Add a translation - "idle-modal.info": "For security reasons, user sessions expire after {{ timeToExpire }} minutes of inactivity. Your session will expire soon. Would you like to extend it or log out?", + "idle-modal.info": "Қауіпсіздік мақсатында пайдаланушы сеанстары {{ timeToExpire }} әрекетсіздік минуттары арқылы аяқталады. Сіздің сессияңыз жақында аяқталады. Сіз оны ұзартқыңыз немесе жүйеден шыққыңыз келе ме?", // "idle-modal.log-out": "Log out", - // TODO New key - Add a translation - "idle-modal.log-out": "Log out", + "idle-modal.log-out": "Шығу", // "idle-modal.extend-session": "Extend session", - // TODO New key - Add a translation - "idle-modal.extend-session": "Extend session", + "idle-modal.extend-session": "Сеансты ұзарту", // "researcher.profile.action.processing" : "Processing...", - // TODO New key - Add a translation - "researcher.profile.action.processing" : "Processing...", + "researcher.profile.action.processing" : "Процессте...", // "researcher.profile.associated": "Researcher profile associated", - // TODO New key - Add a translation - "researcher.profile.associated": "Researcher profile associated", + "researcher.profile.associated": "Зерттеушінің байланысты профилі", // "researcher.profile.change-visibility.fail": "An unexpected error occurs while changing the profile visibility", - // TODO New key - Add a translation - "researcher.profile.change-visibility.fail": "An unexpected error occurs while changing the profile visibility", + "researcher.profile.change-visibility.fail": "Профиль көрінісі өзгерген кезде күтпеген қате пайда болады", // "researcher.profile.create.new": "Create new", - // TODO New key - Add a translation - "researcher.profile.create.new": "Create new", + "researcher.profile.create.new": "Жаңасың құру", // "researcher.profile.create.success": "Researcher profile created successfully", - // TODO New key - Add a translation - "researcher.profile.create.success": "Researcher profile created successfully", + "researcher.profile.create.success": "Зерттеуші профилі сәтті жасалды", // "researcher.profile.create.fail": "An error occurs during the researcher profile creation", - // TODO New key - Add a translation - "researcher.profile.create.fail": "An error occurs during the researcher profile creation", + "researcher.profile.create.fail": "Зерттеуші профилін құру кезінде қате пайда болады", // "researcher.profile.delete": "Delete", - // TODO New key - Add a translation - "researcher.profile.delete": "Delete", + "researcher.profile.delete": "Жою", // "researcher.profile.expose": "Expose", - // TODO New key - Add a translation - "researcher.profile.expose": "Expose", + "researcher.profile.expose": "Ашу", // "researcher.profile.hide": "Hide", - // TODO New key - Add a translation - "researcher.profile.hide": "Hide", + "researcher.profile.hide": "Жасыру", // "researcher.profile.not.associated": "Researcher profile not yet associated", - // TODO New key - Add a translation - "researcher.profile.not.associated": "Researcher profile not yet associated", + "researcher.profile.not.associated": "Зерттеуші профилі әлі байланысты емес", // "researcher.profile.view": "View", - // TODO New key - Add a translation - "researcher.profile.view": "View", + "researcher.profile.view": "Қөрімдігі", // "researcher.profile.private.visibility" : "PRIVATE", - // TODO New key - Add a translation - "researcher.profile.private.visibility" : "PRIVATE", + "researcher.profile.private.visibility" : "ЖАСЫРЫН", // "researcher.profile.public.visibility" : "PUBLIC", - // TODO New key - Add a translation - "researcher.profile.public.visibility" : "PUBLIC", + "researcher.profile.public.visibility" : "АШЫҚ", // "researcher.profile.status": "Status:", - // TODO New key - Add a translation - "researcher.profile.status": "Status:", + "researcher.profile.status": "Мәртебесі:", // "researcherprofile.claim.not-authorized": "You are not authorized to claim this item. For more details contact the administrator(s).", - // TODO New key - Add a translation - "researcherprofile.claim.not-authorized": "You are not authorized to claim this item. For more details contact the administrator(s).", + "researcherprofile.claim.not-authorized": "Сіз бұл өнімді талап етуге құқығыңыз жоқ. Қосымша ақпарат алу үшін әкімшіге хабарласыңыз.", // "researcherprofile.error.claim.body" : "An error occurred while claiming the profile, please try again later", - // TODO New key - Add a translation - "researcherprofile.error.claim.body" : "An error occurred while claiming the profile, please try again later", + "researcherprofile.error.claim.body" : "Профильді толтыру кезінде қате пайда болды, кейінірек қайталап көріңіз", // "researcherprofile.error.claim.title" : "Error", - // TODO New key - Add a translation - "researcherprofile.error.claim.title" : "Error", + "researcherprofile.error.claim.title" : "Қате", // "researcherprofile.success.claim.body" : "Profile claimed with success", - // TODO New key - Add a translation - "researcherprofile.success.claim.body" : "Profile claimed with success", + "researcherprofile.success.claim.body" : "Сәтті мәлімделген Профиль", // "researcherprofile.success.claim.title" : "Success", - // TODO New key - Add a translation - "researcherprofile.success.claim.title" : "Success", + "researcherprofile.success.claim.title" : "Сәтті", // "person.page.orcid.create": "Create an ORCID ID", - // TODO New key - Add a translation - "person.page.orcid.create": "Create an ORCID ID", + "person.page.orcid.create": "ORCID ID құру", // "person.page.orcid.granted-authorizations": "Granted authorizations", - // TODO New key - Add a translation - "person.page.orcid.granted-authorizations": "Granted authorizations", + "person.page.orcid.granted-authorizations": "Рұқсат берілген", // "person.page.orcid.grant-authorizations" : "Grant authorizations", - // TODO New key - Add a translation - "person.page.orcid.grant-authorizations" : "Grant authorizations", + "person.page.orcid.grant-authorizations" : "Рұқсат беру", // "person.page.orcid.link": "Connect to ORCID ID", - // TODO New key - Add a translation - "person.page.orcid.link": "Connect to ORCID ID", + "person.page.orcid.link": "ORCID идентификаторына қосылу", // "person.page.orcid.link.processing": "Linking profile to ORCID...", - // TODO New key - Add a translation - "person.page.orcid.link.processing": "Linking profile to ORCID...", + "person.page.orcid.link.processing": "Профильді ORCID-ке байланыстыру...", // "person.page.orcid.link.error.message": "Something went wrong while connecting the profile with ORCID. If the problem persists, contact the administrator.", - // TODO New key - Add a translation - "person.page.orcid.link.error.message": "Something went wrong while connecting the profile with ORCID. If the problem persists, contact the administrator.", + "person.page.orcid.link.error.message": "Профильді ORCID-ке қосқан кезде бірдеңе дұрыс болмады. Егер мәселе шешілмесе, әкімшіге хабарласыңыз.", // "person.page.orcid.orcid-not-linked-message": "The ORCID iD of this profile ({{ orcid }}) has not yet been connected to an account on the ORCID registry or the connection is expired.", - // TODO New key - Add a translation - "person.page.orcid.orcid-not-linked-message": "The ORCID iD of this profile ({{ orcid }}) has not yet been connected to an account on the ORCID registry or the connection is expired.", + "person.page.orcid.orcid-not-linked-message": "Осы профильдің ORCID идентификаторы ({{ orcid }}) әлі ORCID тізіліміндегі есептік жазбаға қосылмаған немесе қосылу мерзімі аяқталған.", // "person.page.orcid.unlink": "Disconnect from ORCID", - // TODO New key - Add a translation - "person.page.orcid.unlink": "Disconnect from ORCID", + "person.page.orcid.unlink": "ORCID өшіру", // "person.page.orcid.unlink.processing": "Processing...", "person.page.orcid.unlink.processing": "Процессте...", // "person.page.orcid.missing-authorizations": "Missing authorizations", - // TODO New key - Add a translation - "person.page.orcid.missing-authorizations": "Missing authorizations", + "person.page.orcid.missing-authorizations": "Рұқсаттары жоқ", // "person.page.orcid.missing-authorizations-message": "The following authorizations are missing:", - // TODO New key - Add a translation - "person.page.orcid.missing-authorizations-message": "The following authorizations are missing:", + "person.page.orcid.missing-authorizations-message": "Келесі рұқсаттар жоқ:", // "person.page.orcid.no-missing-authorizations-message": "Great! This box is empty, so you have granted all access rights to use all functions offers by your institution.", - // TODO New key - Add a translation - "person.page.orcid.no-missing-authorizations-message": "Great! This box is empty, so you have granted all access rights to use all functions offers by your institution.", + "person.page.orcid.no-missing-authorizations-message": "Тамаша! Бұл өріс бос, бұл сіздің мекемеңіздің барлық мүмкіндіктерін пайдалану үшін барлық қол жетімділік құқығын береді.", // "person.page.orcid.no-orcid-message": "No ORCID iD associated yet. By clicking on the button below it is possible to link this profile with an ORCID account.", - // TODO New key - Add a translation - "person.page.orcid.no-orcid-message": "No ORCID iD associated yet. By clicking on the button below it is possible to link this profile with an ORCID account.", + "person.page.orcid.no-orcid-message": "ORCID идентификаторы әлі қосылмаған. Төмендегі батырманы басу арқылы сіз бұл Профильді ORCID тіркелгісімен байланыстыра аласыз.", // "person.page.orcid.profile-preferences": "Profile preferences", - // TODO New key - Add a translation - "person.page.orcid.profile-preferences": "Profile preferences", + "person.page.orcid.profile-preferences": "Профиль параметрлері", // "person.page.orcid.funding-preferences": "Funding preferences", - // TODO New key - Add a translation - "person.page.orcid.funding-preferences": "Funding preferences", + "person.page.orcid.funding-preferences": "Қаржыландырудағы артықшылықтар", // "person.page.orcid.publications-preferences": "Publication preferences", - // TODO New key - Add a translation - "person.page.orcid.publications-preferences": "Publication preferences", + "person.page.orcid.publications-preferences": "Жарияланымдағы артықшылықтар", // "person.page.orcid.remove-orcid-message": "If you need to remove your ORCID, please contact the repository administrator", - // TODO New key - Add a translation - "person.page.orcid.remove-orcid-message": "If you need to remove your ORCID, please contact the repository administrator", + "person.page.orcid.remove-orcid-message": "Егер сізге ORCID жою қажет болса, репозиторий әкімшісіне хабарласыңыз", // "person.page.orcid.save.preference.changes": "Update settings", - // TODO New key - Add a translation - "person.page.orcid.save.preference.changes": "Update settings", + "person.page.orcid.save.preference.changes": "Параметрлерді жаңарту", // "person.page.orcid.sync-profile.affiliation" : "Affiliation", - // TODO New key - Add a translation - "person.page.orcid.sync-profile.affiliation" : "Affiliation", + "person.page.orcid.sync-profile.affiliation" : "Филиалы", // "person.page.orcid.sync-profile.biographical" : "Biographical data", - // TODO New key - Add a translation - "person.page.orcid.sync-profile.biographical" : "Biographical data", + "person.page.orcid.sync-profile.biographical" : "Өмірбаяндық мәліметтер", // "person.page.orcid.sync-profile.education" : "Education", - // TODO New key - Add a translation - "person.page.orcid.sync-profile.education" : "Education", + "person.page.orcid.sync-profile.education" : "Білімі", // "person.page.orcid.sync-profile.identifiers" : "Identifiers", - // TODO New key - Add a translation - "person.page.orcid.sync-profile.identifiers" : "Identifiers", + "person.page.orcid.sync-profile.identifiers" : "Идентификатор", // "person.page.orcid.sync-fundings.all" : "All fundings", - // TODO New key - Add a translation - "person.page.orcid.sync-fundings.all" : "All fundings", + "person.page.orcid.sync-fundings.all" : "Барлық құралдар", // "person.page.orcid.sync-fundings.mine" : "My fundings", - // TODO New key - Add a translation - "person.page.orcid.sync-fundings.mine" : "My fundings", + "person.page.orcid.sync-fundings.mine" : "Менің қаражатым", // "person.page.orcid.sync-fundings.my_selected" : "Selected fundings", - // TODO New key - Add a translation - "person.page.orcid.sync-fundings.my_selected" : "Selected fundings", + "person.page.orcid.sync-fundings.my_selected" : "Таңдалған құралдар", // "person.page.orcid.sync-fundings.disabled" : "Disabled", - // TODO New key - Add a translation - "person.page.orcid.sync-fundings.disabled" : "Disabled", + "person.page.orcid.sync-fundings.disabled" : "Қолжетімсіз", // "person.page.orcid.sync-publications.all" : "All publications", - // TODO New key - Add a translation - "person.page.orcid.sync-publications.all" : "All publications", + "person.page.orcid.sync-publications.all" : "Барлық жарияланымдар", // "person.page.orcid.sync-publications.mine" : "My publications", - // TODO New key - Add a translation - "person.page.orcid.sync-publications.mine" : "My publications", + "person.page.orcid.sync-publications.mine" : "Менің жарияланымдарым", // "person.page.orcid.sync-publications.my_selected" : "Selected publications", - // TODO New key - Add a translation - "person.page.orcid.sync-publications.my_selected" : "Selected publications", + "person.page.orcid.sync-publications.my_selected" : "Таңдаулы басылымдар", // "person.page.orcid.sync-publications.disabled" : "Disabled", - // TODO New key - Add a translation - "person.page.orcid.sync-publications.disabled" : "Disabled", + "person.page.orcid.sync-publications.disabled" : "Қолжетімсіз", // "person.page.orcid.sync-queue.discard" : "Discard the change and do not synchronize with the ORCID registry", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.discard" : "Discard the change and do not synchronize with the ORCID registry", + "person.page.orcid.sync-queue.discard" : "Өзгерісті болдырмаңыз және ORCID тізілімімен синхрондамаңыз", // "person.page.orcid.sync-queue.discard.error": "The discarding of the ORCID queue record failed", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.discard.error": "The discarding of the ORCID queue record failed", + "person.page.orcid.sync-queue.discard.error": "ORCID кезегінің жазбасын жою мүмкін емес", // "person.page.orcid.sync-queue.discard.success": "The ORCID queue record have been discarded successfully", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.discard.success": "The ORCID queue record have been discarded successfully", + "person.page.orcid.sync-queue.discard.success": "ORCID кезегінің жазбасы сәтті жойылды", // "person.page.orcid.sync-queue.empty-message": "The ORCID queue registry is empty", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.empty-message": "The ORCID queue registry is empty", + "person.page.orcid.sync-queue.empty-message": "ORCID кезегінің тізілімі бос", // "person.page.orcid.sync-queue.table.header.type" : "Type", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.table.header.type" : "Type", + "person.page.orcid.sync-queue.table.header.type" : "Түрі", // "person.page.orcid.sync-queue.table.header.description" : "Description", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.table.header.description" : "Description", + "person.page.orcid.sync-queue.table.header.description" : "Сипаттамасы", // "person.page.orcid.sync-queue.table.header.action" : "Action", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.table.header.action" : "Action", + "person.page.orcid.sync-queue.table.header.action" : "Әрекет", // "person.page.orcid.sync-queue.description.affiliation": "Affiliations", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.affiliation": "Affiliations", + "person.page.orcid.sync-queue.description.affiliation": "Филиалы", // "person.page.orcid.sync-queue.description.country": "Country", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.country": "Country", + "person.page.orcid.sync-queue.description.country": "ЕЛ", // "person.page.orcid.sync-queue.description.education": "Educations", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.education": "Educations", + "person.page.orcid.sync-queue.description.education": "Білімі", // "person.page.orcid.sync-queue.description.external_ids": "External ids", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.external_ids": "External ids", + "person.page.orcid.sync-queue.description.external_ids": "Қосымша ids", // "person.page.orcid.sync-queue.description.other_names": "Other names", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.other_names": "Other names", + "person.page.orcid.sync-queue.description.other_names": "Басқа атаулары", // "person.page.orcid.sync-queue.description.qualification": "Qualifications", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.qualification": "Qualifications", + "person.page.orcid.sync-queue.description.qualification": "Квалификациясы", // "person.page.orcid.sync-queue.description.researcher_urls": "Researcher urls", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.researcher_urls": "Researcher urls", + "person.page.orcid.sync-queue.description.researcher_urls": "Зерттеушінің URL мекенжайы", // "person.page.orcid.sync-queue.description.keywords": "Keywords", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.description.keywords": "Keywords", + "person.page.orcid.sync-queue.description.keywords": "Кілт сөздер", // "person.page.orcid.sync-queue.tooltip.insert": "Add a new entry in the ORCID registry", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.insert": "Add a new entry in the ORCID registry", + "person.page.orcid.sync-queue.tooltip.insert": "ORCID тізіліміне жаңа жазба қосыңыз", // "person.page.orcid.sync-queue.tooltip.update": "Update this entry on the ORCID registry", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.update": "Update this entry on the ORCID registry", + "person.page.orcid.sync-queue.tooltip.update": "Бұл жазбаны ORCID тізіліміне жаңартыңыз", // "person.page.orcid.sync-queue.tooltip.delete": "Remove this entry from the ORCID registry", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.delete": "Remove this entry from the ORCID registry", + "person.page.orcid.sync-queue.tooltip.delete": "Бұл жазбаны ORCID тізілімінен жойыңыз", // "person.page.orcid.sync-queue.tooltip.publication": "Publication", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.publication": "Publication", + "person.page.orcid.sync-queue.tooltip.publication": "Жарияланымдар", // "person.page.orcid.sync-queue.tooltip.project": "Project", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.project": "Project", + "person.page.orcid.sync-queue.tooltip.project": "Проект", // "person.page.orcid.sync-queue.tooltip.affiliation": "Affiliation", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.affiliation": "Affiliation", + "person.page.orcid.sync-queue.tooltip.affiliation": "Филиалы", // "person.page.orcid.sync-queue.tooltip.education": "Education", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.education": "Education", + "person.page.orcid.sync-queue.tooltip.education": "Білімі", // "person.page.orcid.sync-queue.tooltip.qualification": "Qualification", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.qualification": "Qualification", + "person.page.orcid.sync-queue.tooltip.qualification": "Квалификациясы", // "person.page.orcid.sync-queue.tooltip.other_names": "Other name", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.other_names": "Other name", + "person.page.orcid.sync-queue.tooltip.other_names": "Басқа атауы", // "person.page.orcid.sync-queue.tooltip.country": "Country", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.country": "Country", + "person.page.orcid.sync-queue.tooltip.country": "Ел", // "person.page.orcid.sync-queue.tooltip.keywords": "Keyword", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.keywords": "Keyword", + "person.page.orcid.sync-queue.tooltip.keywords": "Кілт сөз", // "person.page.orcid.sync-queue.tooltip.external_ids": "External identifier", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.external_ids": "External identifier", + "person.page.orcid.sync-queue.tooltip.external_ids": "Сыртқы идентификатор", // "person.page.orcid.sync-queue.tooltip.researcher_urls": "Researcher url", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.tooltip.researcher_urls": "Researcher url", + "person.page.orcid.sync-queue.tooltip.researcher_urls": "Зерттеушінің URL мекенжайы", // "person.page.orcid.sync-queue.send" : "Synchronize with ORCID registry", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send" : "Synchronize with ORCID registry", + "person.page.orcid.sync-queue.send" : "ORCID тізілімімен синхрондау", // "person.page.orcid.sync-queue.send.unauthorized-error.title": "The submission to ORCID failed for missing authorizations.", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.unauthorized-error.title": "The submission to ORCID failed for missing authorizations.", + "person.page.orcid.sync-queue.send.unauthorized-error.title": "Рұқсаттардың болмауына байланысты ORCID-ке жіберу сәтсіз аяқталды.", // "person.page.orcid.sync-queue.send.unauthorized-error.content": "Click here to grant again the required permissions. If the problem persists, contact the administrator", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.unauthorized-error.content": "Click here to grant again the required permissions. If the problem persists, contact the administrator", + "person.page.orcid.sync-queue.send.unauthorized-error.content": "Қажетті рұқсаттарды қайтадан беру үшін >мұнда басыңыз. Егер мәселе шешілмесе, әкімшіге хабарласыңыз", // "person.page.orcid.sync-queue.send.bad-request-error": "The submission to ORCID failed because the resource sent to ORCID registry is not valid", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.bad-request-error": "The submission to ORCID failed because the resource sent to ORCID registry is not valid", + "person.page.orcid.sync-queue.send.bad-request-error": "ORCID-ке жіберу сәтсіз аяқталды, өйткені ORCID тізіліміне жіберілген ресурс жарамсыз", // "person.page.orcid.sync-queue.send.error": "The submission to ORCID failed", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.error": "The submission to ORCID failed", + "person.page.orcid.sync-queue.send.error": "ORCID-ке жіберу сәтсіз аяқталды", // "person.page.orcid.sync-queue.send.conflict-error": "The submission to ORCID failed because the resource is already present on the ORCID registry", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.conflict-error": "The submission to ORCID failed because the resource is already present on the ORCID registry", + "person.page.orcid.sync-queue.send.conflict-error": "ORCID-ке жіберу сәтсіз аяқталды, өйткені ресурс ORCID тізілімінде бар", // "person.page.orcid.sync-queue.send.not-found-warning": "The resource does not exists anymore on the ORCID registry.", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.not-found-warning": "The resource does not exists anymore on the ORCID registry.", + "person.page.orcid.sync-queue.send.not-found-warning": "Ресурс енді ORCID тізілімінде жоқ.", // "person.page.orcid.sync-queue.send.success": "The submission to ORCID was completed successfully", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.success": "The submission to ORCID was completed successfully", + "person.page.orcid.sync-queue.send.success": "ORCID-ке жіберу сәтті аяқталды", // "person.page.orcid.sync-queue.send.validation-error": "The data that you want to synchronize with ORCID is not valid", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error": "The data that you want to synchronize with ORCID is not valid", + "person.page.orcid.sync-queue.send.validation-error": "ORCID-пен синхрондағыңыз келетін деректер жарамсыз", // "person.page.orcid.sync-queue.send.validation-error.amount-currency.required": "The amount's currency is required", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.amount-currency.required": "The amount's currency is required", + "person.page.orcid.sync-queue.send.validation-error.amount-currency.required": "Соманың валютасын көрсету талап етіледі", // "person.page.orcid.sync-queue.send.validation-error.external-id.required": "The resource to be sent requires at least one identifier", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.external-id.required": "The resource to be sent requires at least one identifier", + "person.page.orcid.sync-queue.send.validation-error.external-id.required": "Жіберілген ресурс үшін кем дегенде бір идентификатор қажет", // "person.page.orcid.sync-queue.send.validation-error.title.required": "The title is required", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.title.required": "The title is required", + "person.page.orcid.sync-queue.send.validation-error.title.required": "Аты міндетті", // "person.page.orcid.sync-queue.send.validation-error.type.required": "The dc.type is required", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.type.required": "The dc.type is required", + "person.page.orcid.sync-queue.send.validation-error.type.required": "dc.type міндетті", // "person.page.orcid.sync-queue.send.validation-error.start-date.required": "The start date is required", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.start-date.required": "The start date is required", + "person.page.orcid.sync-queue.send.validation-error.start-date.required": "Басталу күні міндетті", // "person.page.orcid.sync-queue.send.validation-error.funder.required": "The funder is required", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.funder.required": "The funder is required", + "person.page.orcid.sync-queue.send.validation-error.funder.required": "Қаржыландыру қажет", // "person.page.orcid.sync-queue.send.validation-error.country.invalid": "Invalid 2 digits ISO 3166 country", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.country.invalid": "Invalid 2 digits ISO 3166 country", + "person.page.orcid.sync-queue.send.validation-error.country.invalid": "Жарамсыз 2 сандық ISO 3166 елі", // "person.page.orcid.sync-queue.send.validation-error.organization.required": "The organization is required", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.organization.required": "The organization is required", + "person.page.orcid.sync-queue.send.validation-error.organization.required": "Ұйым міндетті", // "person.page.orcid.sync-queue.send.validation-error.organization.name-required": "The organization's name is required", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.organization.name-required": "The organization's name is required", + "person.page.orcid.sync-queue.send.validation-error.organization.name-required": "Ұйымның атауы міндетті", // "person.page.orcid.sync-queue.send.validation-error.publication.date-invalid" : "The publication date must be one year after 1900", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.publication.date-invalid" : "The publication date must be one year after 1900", + "person.page.orcid.sync-queue.send.validation-error.publication.date-invalid" : "Жариялау күні 1900 жылдан бір жыл артық болуы керек", // "person.page.orcid.sync-queue.send.validation-error.organization.address-required": "The organization to be sent requires an address", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.organization.address-required": "The organization to be sent requires an address", + "person.page.orcid.sync-queue.send.validation-error.organization.address-required": "Ұйымға жіберу үшін мекен-жай қажет", // "person.page.orcid.sync-queue.send.validation-error.organization.city-required": "The address of the organization to be sent requires a city", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.organization.city-required": "The address of the organization to be sent requires a city", + "person.page.orcid.sync-queue.send.validation-error.organization.city-required": "Жөнелтілетін ұйымның мекенжайы үшін қаланы көрсету қажет", // "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 New key - Add a translation - "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", + "person.page.orcid.sync-queue.send.validation-error.organization.country-required": "Жіберілетін ұйымның мекен-жайы жарамды болуы керек 2 санық 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", - // TODO New key - Add a translation - "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", + "person.page.orcid.sync-queue.send.validation-error.disambiguated-organization.required": "Ұйымдардың түсініксіздігін жою үшін идентификатор қажет. Қолдау көрсетілетін идентификаторлар - GRID, Ringgold, заңды тұлға идентификаторлары (LEI) және Crossref демеушілер тізілімінің идентификаторлары", // "person.page.orcid.sync-queue.send.validation-error.disambiguated-organization.value-required": "The organization's identifiers requires a value", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.disambiguated-organization.value-required": "The organization's identifiers requires a value", + "person.page.orcid.sync-queue.send.validation-error.disambiguated-organization.value-required": "Ұйым идентификаторлары мәндерді талап етеді", // "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.required": "The organization's identifiers requires a source", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.required": "The organization's identifiers requires a source", + "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.required": "Ұйым идентификаторлары үшін ақпарат көзі қажет", // "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.invalid": "The source of one of the organization identifiers is invalid. Supported sources are RINGGOLD, GRID, LEI and FUNDREF", - // TODO New key - Add a translation - "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.invalid": "The source of one of the organization identifiers is invalid. Supported sources are RINGGOLD, GRID, LEI and FUNDREF", + "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.invalid": "Ұйым идентификаторларының бірінің көзі жарамсыз. Қолдау көздері: RINGGOLD, GRID, LEI және FUNDREF", - // "person.page.orcid.synchronization-mode": "Synchronization mode", - // TODO New key - Add a translation - "person.page.orcid.synchronization-mode": "Synchronization mode", + // "person.page.orcid.synchronization-mode": "Synchronization mode",һ + "person.page.orcid.synchronization-mode": "Синхрондау режимі", // "person.page.orcid.synchronization-mode.batch": "Batch", - // TODO New key - Add a translation - "person.page.orcid.synchronization-mode.batch": "Batch", + "person.page.orcid.synchronization-mode.batch": "Партия", // "person.page.orcid.synchronization-mode.label": "Synchronization mode", - // TODO New key - Add a translation - "person.page.orcid.synchronization-mode.label": "Synchronization mode", + "person.page.orcid.synchronization-mode.label": "Синхрондау режимі", // "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 New key - Add a translation - "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).", + "person.page.orcid.synchronization-mode-message": "Orcid-пен синхрондау қалай жүретінін таңдаңыз. Опцияларға \"Қолмен\" (деректерді ORCID-ке қолмен жіберу керек) немесе \ "пакеттік\" (жүйе жоспарланған сценарий арқылы деректерді ORCID-ке жібереді) кіреді.", // "person.page.orcid.synchronization-mode-funding-message": "Select whether to send your linked Project entities to your ORCID record's list of funding information.", - // TODO New key - Add a translation - "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": "Сізге қатысты жоба нысандарын ORCID жазбаңызды қаржыландыру туралы ақпарат тізіміне жіберуді таңдаңыз.", // "person.page.orcid.synchronization-mode-publication-message": "Select whether to send your linked Publication entities to your ORCID record's list of works.", - // TODO New key - Add a translation - "person.page.orcid.synchronization-mode-publication-message": "Select whether to send your linked Publication entities to your ORCID record's list of works.", + "person.page.orcid.synchronization-mode-publication-message": "Қатысты жариялау нысандарын ORCID жазбаңыздың жұмыс тізіміне жіберу керек пе, жоқ па, соны таңдаңыз.", // "person.page.orcid.synchronization-mode-profile-message": "Select whether to send your biographical data or personal identifiers to your ORCID record.", - // TODO New key - Add a translation - "person.page.orcid.synchronization-mode-profile-message": "Select whether to send your biographical data or personal identifiers to your ORCID record.", + "person.page.orcid.synchronization-mode-profile-message": "Оқу жоспарын немесе жеке куәліктерді ORCID жазбаңызға жіберуді таңдаңыз.", // "person.page.orcid.synchronization-settings-update.success": "The synchronization settings have been updated successfully", - // TODO New key - Add a translation - "person.page.orcid.synchronization-settings-update.success": "The synchronization settings have been updated successfully", + "person.page.orcid.synchronization-settings-update.success": "Синхрондау параметрлері сәтті жаңартылды", // "person.page.orcid.synchronization-settings-update.error": "The update of the synchronization settings failed", - // TODO New key - Add a translation - "person.page.orcid.synchronization-settings-update.error": "The update of the synchronization settings failed", + "person.page.orcid.synchronization-settings-update.error": "Синхрондау баптауларын жаңарту мүмкін емес", // "person.page.orcid.synchronization-mode.manual": "Manual", "person.page.orcid.synchronization-mode.manual": "Қолмен", // "person.page.orcid.scope.authenticate": "Get your ORCID iD", - // TODO New key - Add a translation - "person.page.orcid.scope.authenticate": "Get your ORCID iD", + "person.page.orcid.scope.authenticate": "ORCID iD алу", // "person.page.orcid.scope.read-limited": "Read your information with visibility set to Trusted Parties", - // TODO New key - Add a translation - "person.page.orcid.scope.read-limited": "Read your information with visibility set to Trusted Parties", + "person.page.orcid.scope.read-limited": "Сенімді тараптар үшін көріну параметрімен сіздің ақпаратты оқыңыз", // "person.page.orcid.scope.activities-update": "Add/update your research activities", - // TODO New key - Add a translation - "person.page.orcid.scope.activities-update": "Add/update your research activities", + "person.page.orcid.scope.activities-update": "Зерттеу жұмыстарын қосыңыз/жаңартыңыз", // "person.page.orcid.scope.person-update": "Add/update other information about you", "person.page.orcid.scope.person-update": "Сіз туралы басқа ақпаратты қосу/жаңарту", // "person.page.orcid.unlink.success": "The disconnection between the profile and the ORCID registry was successful", - // TODO New key - Add a translation - "person.page.orcid.unlink.success": "The disconnection between the profile and the ORCID registry was successful", + "person.page.orcid.unlink.success": "Профиль мен ORCID тізілімі арасындағы ажырату сәтті өтті", // "person.page.orcid.unlink.error": "An error occurred while disconnecting between the profile and the ORCID registry. Try again", - // TODO New key - Add a translation - "person.page.orcid.unlink.error": "An error occurred while disconnecting between the profile and the ORCID registry. Try again", + "person.page.orcid.unlink.error": "Профиль мен ORCID тізілімі арасында өшіру кезінде қате пайда болды. Қайта көріңіз", // "person.orcid.sync.setting": "ORCID Synchronization settings", - // TODO New key - Add a translation - "person.orcid.sync.setting": "ORCID Synchronization settings", + "person.orcid.sync.setting": "ORCID синхрондау параметрлері", // "person.orcid.registry.queue": "ORCID Registry Queue", - // TODO New key - Add a translation - "person.orcid.registry.queue": "ORCID Registry Queue", + "person.orcid.registry.queue": "ORCID тіркеу кезегі", // "person.orcid.registry.auth": "ORCID Authorizations", - // TODO New key - Add a translation - "person.orcid.registry.auth": "ORCID Authorizations", + "person.orcid.registry.auth": "ORCID Ауторизациясы", // "home.recent-submissions.head": "Recent Submissions", - // TODO New key - Add a translation - "home.recent-submissions.head": "Recent Submissions", + "home.recent-submissions.head": "Соңғы материалдар", From 6a932ec6ac65ef6794b3945fc6be0dbd0fd30af1 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 27 Sep 2022 16:21:56 +0200 Subject: [PATCH 032/758] 94390: Remove unused import --- .../research-entities/item-pages/person/person.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts index 1ac70bad9df..e5e2a523389 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts @@ -1,7 +1,6 @@ import { Component } from '@angular/core'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; -import { MetadataValue } from '../../../../core/shared/metadata.models'; import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component'; @listableObjectComponent('Person', ViewMode.StandalonePage) From 3abfbf3f116bda499da300497774d76f18c3bb00 Mon Sep 17 00:00:00 2001 From: myrza1 Date: Wed, 28 Sep 2022 03:47:16 +0600 Subject: [PATCH 033/758] Update kk.json5 Finish translates --- src/assets/i18n/kk.json5 | 565 +++++++++++++-------------------------- 1 file changed, 190 insertions(+), 375 deletions(-) diff --git a/src/assets/i18n/kk.json5 b/src/assets/i18n/kk.json5 index 84d3ba907de..6faf7d4484c 100644 --- a/src/assets/i18n/kk.json5 +++ b/src/assets/i18n/kk.json5 @@ -370,8 +370,7 @@ "admin.access-control.epeople.search.button": "Іздеу", // "admin.access-control.epeople.search.placeholder": "Search people...", - // TODO New key - Add a translation - "admin.access-control.epeople.search.placeholder": "Search people...", + "admin.access-control.epeople.search.placeholder": "Адамдарды іздеу...", // "admin.access-control.epeople.button.add": "Add EPerson", "admin.access-control.epeople.button.add": "Адамды қосу", @@ -511,8 +510,7 @@ "admin.access-control.groups.search.button": "Іздеу", // "admin.access-control.groups.search.placeholder": "Search groups...", - // TODO New key - Add a translation - "admin.access-control.groups.search.placeholder": "Search groups...", + "admin.access-control.groups.search.placeholder": "Топты іздеу...", // "admin.access-control.groups.table.id": "ID", "admin.access-control.groups.table.id": "Жеке куәлік", @@ -521,8 +519,7 @@ "admin.access-control.groups.table.name": "Аты", // "admin.access-control.groups.table.collectionOrCommunity": "Collection/Community", - // TODO New key - Add a translation - "admin.access-control.groups.table.collectionOrCommunity": "Collection/Community", + "admin.access-control.groups.table.collectionOrCommunity": "Жинақ/Қауымдастық", // "admin.access-control.groups.table.members": "Members", "admin.access-control.groups.table.members": "Қатысушылар", @@ -566,8 +563,7 @@ "admin.access-control.groups.form.groupName": "Топтың атауы", // "admin.access-control.groups.form.groupCommunity": "Community or Collection", - // TODO New key - Add a translation - "admin.access-control.groups.form.groupCommunity": "Community or Collection", + "admin.access-control.groups.form.groupCommunity": "Жинақ немесе Қауымдастық", // "admin.access-control.groups.form.groupDescription": "Description", "admin.access-control.groups.form.groupDescription":"Сипаттама", @@ -642,19 +638,16 @@ "admin.access-control.groups.form.members-list.table.name": "Аты", // "admin.access-control.groups.form.members-list.table.identity": "Identity", - // TODO New key - Add a translation - "admin.access-control.groups.form.members-list.table.identity": "Identity", + "admin.access-control.groups.form.members-list.table.identity": "Сәйкестілік", // "admin.access-control.groups.form.members-list.table.email": "Email", - // TODO New key - Add a translation - "admin.access-control.groups.form.members-list.table.email": "Email", + "admin.access-control.groups.form.members-list.table.email": "Поштасы", // "admin.access-control.groups.form.members-list.table.netid": "NetID", - // TODO New key - Add a translation "admin.access-control.groups.form.members-list.table.netid": "NetID", // "admin.access-control.groups.form.members-list.table.edit": "Remove / Add", - "admin.access-control.groups.form.members-list.table.edit": "Жою/ Add", + "admin.access-control.groups.form.members-list.table.edit": "Жою/Қосу", // "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": "Қатысушыны аты-жөнімен жою \"{{name}}\"", @@ -871,8 +864,7 @@ "bitstream.edit.authorizations.title": "Бит ағынының саясатын өңдеу", // "bitstream.edit.return": "Back", - // TODO New key - Add a translation - "bitstream.edit.return": "Back", + "bitstream.edit.return": "Артқа", // "bitstream.edit.bitstream": "Bitstream: ", "bitstream.edit.bitstream": "Битстрим: ", @@ -1163,8 +1155,7 @@ "collection.delete.confirm": "Растау", // "collection.delete.processing": "Deleting", - // TODO New key - Add a translation - "collection.delete.processing": "Deleting", + "collection.delete.processing": "Өшірудемін", // "collection.delete.head": "Delete Collection", "collection.delete.head": "Топтаманы жою", @@ -1243,8 +1234,7 @@ "collection.edit.item-mapper.remove": "Таңдалған элементтердің салыстыруларын жою", // "collection.edit.item-mapper.search-form.placeholder": "Search items...", - // TODO New key - Add a translation - "collection.edit.item-mapper.search-form.placeholder": "Search items...", + "collection.edit.item-mapper.search-form.placeholder": "Элементі бойынша іздеу...", // "collection.edit.item-mapper.tabs.browse": "Browse mapped items", "collection.edit.item-mapper.tabs.browse": "Салыстырылған элементтерді қарау", @@ -1303,16 +1293,13 @@ "collection.edit.tabs.authorizations.title": "Топтаманы өзгерту - Авторизациялар", // "collection.edit.item.authorizations.load-bundle-button": "Load more bundles", - // TODO New key - Add a translation - "collection.edit.item.authorizations.load-bundle-button": "Load more bundles", + "collection.edit.item.authorizations.load-bundle-button": "Қосымша пакеттерді жүктеңіз", // "collection.edit.item.authorizations.load-more-button": "Load more", - // TODO New key - Add a translation - "collection.edit.item.authorizations.load-more-button": "Load more", + "collection.edit.item.authorizations.load-more-button": "Көбірек көрсет", // "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": "Бума үшін бит ағынының саясатын көрсету", // "collection.edit.tabs.metadata.head": "Edit Metadata", "collection.edit.tabs.metadata.head": "Метадеректерді өңдеу", @@ -1398,8 +1385,7 @@ "collection.edit.template.edit-button": "Өзгерту", // "collection.edit.template.error": "An error occurred retrieving the template item", - // TODO New key - Add a translation - "collection.edit.template.error": "An error occurred retrieving the template item", + "collection.edit.template.error": "Үлгі элементін алу кезінде қате пайда болды", // "collection.edit.template.head": "Edit Template Item for Collection \"{{ collection }}\"", "collection.edit.template.head": "Топтамаға арналған үлгі элементін өзгерту \"{{ collection }}\"", @@ -1408,8 +1394,7 @@ "collection.edit.template.label": "Үлгі элементі", // "collection.edit.template.loading": "Loading template item...", - // TODO New key - Add a translation - "collection.edit.template.loading": "Loading template item...", + "collection.edit.template.loading": "Үлгі элементін жүктеу...", // "collection.edit.template.notifications.delete.error": "Failed to delete the item template", "collection.edit.template.notifications.delete.error": "Элемент үлгісін жою мүмкін емес", @@ -1447,8 +1432,7 @@ "collection.form.title": "Атауы", // "collection.form.entityType": "Entity Type", - // TODO New key - Add a translation - "collection.form.entityType": "Entity Type", + "collection.form.entityType": "Нысан түрі", @@ -1467,8 +1451,7 @@ "collection.page.edit": "Жинақты өңдеңіз", // "collection.page.handle": "Permanent URI for this collection", - // TODO New key - Add a translation - "collection.page.handle": "Permanent URI for this collection", + "collection.page.handle": "Бұл коллекция үшін тұрақты URI", // "collection.page.license": "License", "collection.page.license": "Лицензия", @@ -1567,15 +1550,14 @@ // "communityList.breadcrumbs": "Community List", - // TODO New key - Add a translation - "communityList.breadcrumbs": "Community List", + "communityList.breadcrumbs": "Қауымдастықтар тізімі", // "communityList.tabTitle": "Community List", // TODO Source message changed - Revise the translation - "communityList.tabTitle": "DSpace - Қауымдастық Тізімі", + "communityList.tabTitle": " Қауымдастық Тізімі", // "communityList.title": "List of Communities", - "communityList.title": "Қауымдастықтар Тізімі", + "communityList.title": "Қауымдастықтар тізімі", // "communityList.showMore": "Show More", "communityList.showMore": "Көбірек Көрсету", @@ -1601,8 +1583,7 @@ "community.delete.confirm": "Растау", // "community.delete.processing": "Deleting...", - // TODO New key - Add a translation - "community.delete.processing": "Deleting...", + "community.delete.processing": "Жоюда...", // "community.delete.head": "Delete Community", "community.delete.head": "Қауымдастықты Жою", @@ -1623,16 +1604,14 @@ "community.edit.head": "Қауымдастықты Өзгерту", // "community.edit.breadcrumbs": "Edit Community", - "community.edit.breadcrumbs": "Edit Community", + "community.edit.breadcrumbs": "Қауымдастықты өңдеу", // "community.edit.logo.delete.title": "Delete logo", - // TODO New key - Add a translation - "community.edit.logo.delete.title": "Delete logo", + "community.edit.logo.delete.title": "Логотипті жою", // "community.edit.logo.delete-undo.title": "Undo delete", - // TODO New key - Add a translation - "community.edit.logo.delete-undo.title": "Undo delete", + "community.edit.logo.delete-undo.title": "Өшірмеу", // "community.edit.logo.label": "Community logo", "community.edit.logo.label": "Қауымдастық логотипі", @@ -1956,8 +1935,7 @@ "curation.form.submit.error.content": "Тапсырманы іске қосу кезіндегі қателік.", // "curation.form.submit.error.invalid-handle": "Couldn't determine the handle for this object", - // TODO New key - Add a translation - "curation.form.submit.error.invalid-handle": "Couldn't determine the handle for this object", + "curation.form.submit.error.invalid-handle": "Бұл нысан үшін дескрипторды анықтау мүмкін емес", // "curation.form.handle.label": "Handle:", "curation.form.handle.label": "Қалам:", @@ -2302,12 +2280,10 @@ "form.clear-help": "Таңдалған мәнді жою үшін осы жерді басыңыз", // "form.discard": "Discard", - // TODO New key - Add a translation - "form.discard": "Discard", + "form.discard": "Тастау", // "form.drag": "Drag", - // TODO New key - Add a translation - "form.drag": "Drag", + "form.drag": "Сүйреп апарыңыз", // "form.edit": "Edit", "form.edit": "Өзгерту", @@ -2829,8 +2805,7 @@ "item.edit.item-mapper.notifications.remove.success.head": "Карталауды жою аяқталды", // "item.edit.item-mapper.search-form.placeholder": "Search collections...", - // TODO New key - Add a translation - "item.edit.item-mapper.search-form.placeholder": "Search collections...", + "item.edit.item-mapper.search-form.placeholder": "Жинақтар бойынша іздеу...", // "item.edit.item-mapper.tabs.browse": "Browse mapped collections", "item.edit.item-mapper.tabs.browse": "Карталанған жинақтарды шолу", @@ -2871,7 +2846,7 @@ "item.edit.metadata.headers.language": "Тіл", // "item.edit.metadata.headers.value": "Value", - "item.edit.metadata.headers.value": "құн", + "item.edit.metadata.headers.value": "Құны", // "item.edit.metadata.metadatafield.invalid": "Please choose a valid metadata field", "item.edit.metadata.metadatafield.invalid": "Жарамды метадеректер өрісін таңдаңыз", @@ -3140,8 +3115,7 @@ "item.edit.tabs.status.buttons.reinstate.label": "Сақтау ішіндегі элементті қалпына келтіру", // "item.edit.tabs.status.buttons.unauthorized": "You're not authorized to perform this action", - // TODO New key - Add a translation - "item.edit.tabs.status.buttons.unauthorized": "You're not authorized to perform this action", + "item.edit.tabs.status.buttons.unauthorized": "Сіз бұл әрекетті орындауға құқығыңыз жоқ", // "item.edit.tabs.status.buttons.withdraw.button": "Withdraw...", "item.edit.tabs.status.buttons.withdraw.button": "Шегінуге...", @@ -3515,102 +3489,78 @@ // "item.version.create.modal.header": "New version", - // TODO New key - Add a translation - "item.version.create.modal.header": "New version", + "item.version.create.modal.header": "Жаңа нұсқа", // "item.version.create.modal.text": "Create a new version for this item", - // TODO New key - Add a translation - "item.version.create.modal.text": "Create a new version for this item", + "item.version.create.modal.text": "Осы элементтің жаңа нұсқасын жасаңыз", // "item.version.create.modal.text.startingFrom": "starting from version {{version}}", - // TODO New key - Add a translation - "item.version.create.modal.text.startingFrom": "starting from version {{version}}", + "item.version.create.modal.text.startingFrom": "{{version}} нұсқасынан бастап", // "item.version.create.modal.button.confirm": "Create", - // TODO New key - Add a translation - "item.version.create.modal.button.confirm": "Create", + "item.version.create.modal.button.confirm": "Құру", // "item.version.create.modal.button.confirm.tooltip": "Create new version", - // TODO New key - Add a translation - "item.version.create.modal.button.confirm.tooltip": "Create new version", + "item.version.create.modal.button.confirm.tooltip": "Жаңа нұсқаны жасау", // "item.version.create.modal.button.cancel": "Cancel", - // TODO New key - Add a translation - "item.version.create.modal.button.cancel": "Cancel", + "item.version.create.modal.button.cancel": "Алып тастау", // "item.version.create.modal.button.cancel.tooltip": "Do not create new version", - // TODO New key - Add a translation - "item.version.create.modal.button.cancel.tooltip": "Do not create new version", + "item.version.create.modal.button.cancel.tooltip": "Жаңа нұсқаны жасамаңыз", // "item.version.create.modal.form.summary.label": "Summary", - // TODO New key - Add a translation - "item.version.create.modal.form.summary.label": "Summary", + "item.version.create.modal.form.summary.label": "Түйіндеме", // "item.version.create.modal.form.summary.placeholder": "Insert the summary for the new version", - // TODO New key - Add a translation - "item.version.create.modal.form.summary.placeholder": "Insert the summary for the new version", + "item.version.create.modal.form.summary.placeholder": "Жаңа нұсқаның қысқаша мазмұнын салыңыз", // "item.version.create.modal.submitted.header": "Creating new version...", - // TODO New key - Add a translation - "item.version.create.modal.submitted.header": "Creating new version...", + "item.version.create.modal.submitted.header": "Жаңа нұсқасын жасау...", // "item.version.create.modal.submitted.text": "The new version is being created. This may take some time if the item has a lot of relationships.", - // TODO New key - Add a translation - "item.version.create.modal.submitted.text": "The new version is being created. This may take some time if the item has a lot of relationships.", + "item.version.create.modal.submitted.text": "Жаңа нұсқа жасалуда. Егер элемент көптеген байланыстарға ие болса, біраз уақыт кетуі мүмкін.", // "item.version.create.notification.success" : "New version has been created with version number {{version}}", - // TODO New key - Add a translation - "item.version.create.notification.success" : "New version has been created with version number {{version}}", + "item.version.create.notification.success" : "Жаңа нұсқа {{version}} нұсқасының нөмірімен жасалды", // "item.version.create.notification.failure" : "New version has not been created", - // TODO New key - Add a translation - "item.version.create.notification.failure" : "New version has not been created", + "item.version.create.notification.failure" : "Жаңа нұсқа жасалмады", // "item.version.create.notification.inProgress" : "A new version cannot be created because there is an inprogress submission in the version history", - // TODO New key - Add a translation - "item.version.create.notification.inProgress" : "A new version cannot be created because there is an inprogress submission in the version history", + "item.version.create.notification.inProgress" : "Жаңа нұсқаны жасау мүмкін емес, өйткені нұсқа тарихында аяқталмаған жіберу бар", // "item.version.delete.modal.header": "Delete version", - // TODO New key - Add a translation - "item.version.delete.modal.header": "Delete version", + "item.version.delete.modal.header": "Нұсқаны өшіру", // "item.version.delete.modal.text": "Do you want to delete version {{version}}?", - // TODO New key - Add a translation - "item.version.delete.modal.text": "Do you want to delete version {{version}}?", + "item.version.delete.modal.text": "Сіз {{version}} нұсқасын жойғыңыз келе ме?", // "item.version.delete.modal.button.confirm": "Delete", - // TODO New key - Add a translation - "item.version.delete.modal.button.confirm": "Delete", + "item.version.delete.modal.button.confirm": "Жою", // "item.version.delete.modal.button.confirm.tooltip": "Delete this version", - // TODO New key - Add a translation - "item.version.delete.modal.button.confirm.tooltip": "Delete this version", + "item.version.delete.modal.button.confirm.tooltip": "Бұл нұсқаны жою", // "item.version.delete.modal.button.cancel": "Cancel", - // TODO New key - Add a translation - "item.version.delete.modal.button.cancel": "Cancel", + "item.version.delete.modal.button.cancel": "Алып тастау", // "item.version.delete.modal.button.cancel.tooltip": "Do not delete this version", - // TODO New key - Add a translation - "item.version.delete.modal.button.cancel.tooltip": "Do not delete this version", + "item.version.delete.modal.button.cancel.tooltip": "Бұл нұсқаны жоймаңыз", // "item.version.delete.notification.success" : "Version number {{version}} has been deleted", - // TODO New key - Add a translation - "item.version.delete.notification.success" : "Version number {{version}} has been deleted", + "item.version.delete.notification.success" : "Нұсқа нөмірі {{version}} жойылды", // "item.version.delete.notification.failure" : "Version number {{version}} has not been deleted", - // TODO New key - Add a translation - "item.version.delete.notification.failure" : "Version number {{version}} has not been deleted", + "item.version.delete.notification.failure" : "Нұсқа нөмірі {{version}} жойылған жоқ", // "item.version.edit.notification.success" : "The summary of version number {{version}} has been changed", - // TODO New key - Add a translation - "item.version.edit.notification.success" : "The summary of version number {{version}} has been changed", + "item.version.edit.notification.success" : "Нұсқа нөмірінің қысқаша сипаттамасы {{version}} өзгертілді", // "item.version.edit.notification.failure" : "The summary of version number {{version}} has not been changed", - // TODO New key - Add a translation - "item.version.edit.notification.failure" : "The summary of version number {{version}} has not been changed", + "item.version.edit.notification.failure" : "Нұсқа нөмірінің қысқаша сипаттамасы {{version}} өзгертілген жоқ", @@ -3633,18 +3583,16 @@ "journal.page.publisher": "Баспагер", // "journal.page.titleprefix": "Journal: ", - "journal.page.titleprefix": "Күнделік:", + "journal.page.titleprefix": "Журнал:", // "journal.search.results.head": "Journal Search Results", "journal.search.results.head":"Журналдағы іздеу нәтижелері", // "journal-relationships.search.results.head": "Journal Search Results", - // TODO New key - Add a translation - "journal-relationships.search.results.head": "Journal Search Results", + "journal-relationships.search.results.head": "Журналдағы іздеу нәтижелері", // "journal.search.title": "Journal Search", - // TODO Source message changed - Revise the translation - "journal.search.title": "DSpace Angular :: Журналдағы іздеу", + "journal.search.title": "Журналдағы іздеу", @@ -3690,55 +3638,44 @@ "journalvolume.page.issuedate": "Шығарылған күні", // "journalvolume.page.titleprefix": "Journal Volume: ", - "journalvolume.page.titleprefix":"Журнал көлемі:", + "journalvolume.page.titleprefix": "Журнал көлемі:", // "journalvolume.page.volume": "Volume", "journalvolume.page.volume": "Көлемі", // "iiifsearchable.listelement.badge": "Document Media", - // TODO New key - Add a translation - "iiifsearchable.listelement.badge": "Document Media", + "iiifsearchable.listelement.badge": "Құжаттың медиасы", // "iiifsearchable.page.titleprefix": "Document: ", - // TODO New key - Add a translation - "iiifsearchable.page.titleprefix": "Document: ", + "iiifsearchable.page.titleprefix": "Құжат: ", // "iiifsearchable.page.doi": "Permanent Link: ", - // TODO New key - Add a translation - "iiifsearchable.page.doi": "Permanent Link: ", + "iiifsearchable.page.doi": "Тұрақты сілтеме: ", // "iiifsearchable.page.issue": "Issue: ", - // TODO New key - Add a translation - "iiifsearchable.page.issue": "Issue: ", + "iiifsearchable.page.issue": "Шығарылымы: ", // "iiifsearchable.page.description": "Description: ", - // TODO New key - Add a translation - "iiifsearchable.page.description": "Description: ", + "iiifsearchable.page.description": "Сипаттамасы: ", // "iiifviewer.fullscreen.notice": "Use full screen for better viewing.", - // TODO New key - Add a translation - "iiifviewer.fullscreen.notice": "Use full screen for better viewing.", + "iiifviewer.fullscreen.notice": "Анық көру үшін толық экранды пайдаланыңыз.", // "iiif.listelement.badge": "Image Media", - // TODO New key - Add a translation - "iiif.listelement.badge": "Image Media", + "iiif.listelement.badge": "Белгіше", // "iiif.page.titleprefix": "Image: ", - // TODO New key - Add a translation - "iiif.page.titleprefix": "Image: ", + "iiif.page.titleprefix": "Суреті: ", // "iiif.page.doi": "Permanent Link: ", - // TODO New key - Add a translation - "iiif.page.doi": "Permanent Link: ", + "iiif.page.doi": "Тұрақты сілтеме: ", // "iiif.page.issue": "Issue: ", - // TODO New key - Add a translation - "iiif.page.issue": "Issue: ", + "iiif.page.issue": "Шығарылымы: ", // "iiif.page.description": "Description: ", - // TODO New key - Add a translation - "iiif.page.description": "Description: ", + "iiif.page.description": "Сипаттамасы: ", // "loading.bitstream": "Loading bitstream...", @@ -3813,12 +3750,10 @@ "login.form.or-divider": "немесе", // "login.form.oidc": "Log in with OIDC", - // TODO New key - Add a translation - "login.form.oidc": "Log in with OIDC", + "login.form.oidc": "OIDC"-пен кіру, // "login.form.orcid": "Log in with ORCID", - // TODO New key - Add a translation - "login.form.orcid": "Log in with ORCID", + "login.form.orcid": "ORCID-пен кіру", // "login.form.password": "Password", "login.form.password":"Пароль", @@ -3856,8 +3791,7 @@ "menu.header.image.logo": "Репозиторий логотипі", // "menu.header.admin.description": "Management menu", - // TODO New key - Add a translation - "menu.header.admin.description": "Management menu", + "menu.header.admin.description": "Басқару мәзірі", @@ -3961,8 +3895,7 @@ "menu.section.icon.control_panel": "Басқару тақтасының мәзір бөлімі", // "menu.section.icon.curation_tasks": "Curation Task menu section", - // TODO New key - Add a translation - "menu.section.icon.curation_tasks": "Curation Task menu section", + "menu.section.icon.curation_tasks": "Кураторлық тапсырмалар мәзірінің бөлімі", // "menu.section.icon.edit": "Edit menu section", "menu.section.icon.edit": "Өңдеу мәзір бөлімі", @@ -3974,8 +3907,7 @@ "menu.section.icon.find": "Мәзір бөлімін табу", // "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": "Жағдайды тексеру мәзірінің бөлімі", // "menu.section.icon.import": "Import menu section", "menu.section.icon.import": "Импорттау мәзірі бөлімі", @@ -3987,8 +3919,7 @@ "menu.section.icon.pin": "Бүйірлік тақтаны бекіту", // "menu.section.icon.processes": "Processes Health", - // TODO Source message changed - Revise the translation - "menu.section.icon.processes": "Процесстер мәзірі бөлімі", + "menu.section.icon.processes": "Процесстердің жағдайы", // "menu.section.icon.registries": "Registries menu section", "menu.section.icon.registries": "Тіркеулер мәзірі бөлімі", @@ -3997,8 +3928,7 @@ "menu.section.icon.statistics_task": "Статистика тапсырмалары мәзірі бөлімі", // "menu.section.icon.workflow": "Administer workflow menu section", - // TODO New key - Add a translation - "menu.section.icon.workflow": "Administer workflow menu section", + "menu.section.icon.workflow": "Мәзір бөлімі жұмыс процесін басқару", // "menu.section.icon.unpin": "Unpin sidebar", "menu.section.icon.unpin": "Бүйірлік тақтаны босату", @@ -4045,11 +3975,10 @@ // "menu.section.processes": "Processes", - "menu.section.processes": "процестер", + "menu.section.processes": "Процестер", // "menu.section.health": "Health", - // TODO New key - Add a translation - "menu.section.health": "Health", + "menu.section.health": "Жағдайы", @@ -4102,25 +4031,22 @@ // "menu.section.toggle.statistics_task": "Toggle Statistics Task section", "menu.section.toggle.statistics_task": "Статистика тапсырмасы бөлімін ауыстыру", - // "menu.section.workflow": "Administer Workflow", "menu.section.workflow": "Жұмыс процесін басқару", // "metadata-export-search.tooltip": "Export search results as CSV", - // TODO New key - Add a translation - "metadata-export-search.tooltip": "Export search results as CSV", + "metadata-export-search.tooltip": "Іздеу нәтижелерін CSV форматында экспорттау", + // "metadata-export-search.submit.success": "The export was started successfully", - // TODO New key - Add a translation - "metadata-export-search.submit.success": "The export was started successfully", + "metadata-export-search.submit.success": "Экспорт сәтті басталды", + // "metadata-export-search.submit.error": "Starting the export has failed", - // TODO New key - Add a translation - "metadata-export-search.submit.error": "Starting the export has failed", + "metadata-export-search.submit.error": "Экспортты іске қосу мүмкін емес", // "mydspace.breadcrumbs": "MyDSpace", - // TODO New key - Add a translation - "mydspace.breadcrumbs": "MyDSpace", + "mydspace.breadcrumbs": "Ортам", // "mydspace.description": "", "mydspace.description": "", @@ -4144,7 +4070,7 @@ "mydspace.messages.mark-as-unread": "Оқылмаған деп белгілеу", // "mydspace.messages.no-content": "No content.", - "mydspace.messages.no-content":"Мазмұн жоқ.", + "mydspace.messages.no-content":"Мазмұны жоқ.", // "mydspace.messages.no-messages": "No messages yet.", "mydspace.messages.no-messages": "Әлі хабарлар жоқ.", @@ -4204,12 +4130,10 @@ "mydspace.results.no-uri": "Uri жоқ", // "mydspace.search-form.placeholder": "Search in mydspace...", - // TODO New key - Add a translation - "mydspace.search-form.placeholder": "Search in mydspace...", + "mydspace.search-form.placeholder": "Өз ортадан іздеу...", // "mydspace.show.workflow": "Workflow tasks", - // TODO Source message changed - Revise the translation - "mydspace.show.workflow": "Барлық тапсырмалар", + "mydspace.show.workflow": "Жұмыс процесінің міндеттері", // "mydspace.show.workspace": "Your Submissions", "mydspace.show.workspace":"Сіздің өтініштеріңіз", @@ -4265,15 +4189,13 @@ "nav.login": "Кіру", // "nav.logout": "User profile menu and Log Out", - // TODO Source message changed - Revise the translation - "nav.logout": "Шығу", + "nav.logout": "Пайдаланушы профилі мәзірі және шығу", // "nav.main.description": "Main navigation bar", - // TODO New key - Add a translation - "nav.main.description": "Main navigation bar", + "nav.main.description": "Басты навигация жолағы", // "nav.mydspace": "MyDSpace", - "nav.mydspace": "MyDSpace", + "nav.mydspace": "Менім DSpace", // "nav.profile": "Profile", "nav.profile": "Профиль", @@ -4288,16 +4210,13 @@ "nav.stop-impersonating": "Eperson атын шығаруды тоқтатыңыз", // "nav.toggle" : "Toggle navigation", - // TODO New key - Add a translation - "nav.toggle" : "Toggle navigation", + "nav.toggle" : "Навигацияны ауыстыру", // "nav.user.description" : "User profile bar", - // TODO New key - Add a translation - "nav.user.description" : "User profile bar", + "nav.user.description" : "Пайдаланушы профилі тақтасы", // "none.listelement.badge": "Item", - // TODO New key - Add a translation - "none.listelement.badge": "Item", + "none.listelement.badge": "Элемент", // "orgunit.listelement.badge": "Organizational Unit", @@ -4327,8 +4246,7 @@ // "pagination.options.description": "Pagination options", - // TODO New key - Add a translation - "pagination.options.description": "Pagination options", + "pagination.options.description": "Бетті бөлу нұсқалары", // "pagination.results-per-page": "Results Per Page", "pagination.results-per-page": "Әр беттегі нәтижелер", @@ -4500,42 +4418,33 @@ "process.detail.create" : "Ұқсас процесті жасау", // "process.detail.actions": "Actions", - // TODO New key - Add a translation - "process.detail.actions": "Actions", + "process.detail.actions": "Әрекеттер", // "process.detail.delete.button": "Delete process", - // TODO New key - Add a translation - "process.detail.delete.button": "Delete process", + "process.detail.delete.button": "Процесс жою", // "process.detail.delete.header": "Delete process", - // TODO New key - Add a translation - "process.detail.delete.header": "Delete process", + "process.detail.delete.header": "Процесс жою", // "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": "Ағымдағы процесті жойғыңыз келетініне сенімдісіз бе?", // "process.detail.delete.cancel": "Cancel", - // TODO New key - Add a translation - "process.detail.delete.cancel": "Cancel", + "process.detail.delete.cancel": "Алып тастау", // "process.detail.delete.confirm": "Delete process", - // TODO New key - Add a translation - "process.detail.delete.confirm": "Delete process", + "process.detail.delete.confirm": "Процесс жою.", // "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": "Процесс сәтті жойылды.", // "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": "Процесс жойылған кезде бірдеңе дұрыс болмады", // "process.overview.table.finish" : "Finish time (UTC)", - // TODO Source message changed - Revise the translation - "process.overview.table.finish" :"Аяқтау уақыты", + "process.overview.table.finish" : "Аяқтау уақыты (UTC)", // "process.overview.table.id" : "Process ID", "process.overview.table.id" :"Процесс идентификаторы", @@ -4563,40 +4472,31 @@ "process.overview.new": "Жаңа", // "process.overview.table.actions": "Actions", - // TODO New key - Add a translation - "process.overview.table.actions": "Actions", + "process.overview.table.actions": "Әрекеттер", // "process.overview.delete": "Delete {{count}} processes", - // TODO New key - Add a translation - "process.overview.delete": "Delete {{count}} processes", + "process.overview.delete": "{{count}} процестерді жою", // "process.overview.delete.clear": "Clear delete selection", - // TODO New key - Add a translation - "process.overview.delete.clear": "Clear delete selection", + "process.overview.delete.clear": "Өшіруді өшіру", // "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}} процестер жойылады. Жоюдың толық аяқталғанын күтіңіз. Бұл біраз уақытты алуы мүмкін екенін ескеріңіз.", // "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": "Сіз {{count}} процесс(тер) жойғыңыз келетініне сенімдісіз бе?", // "process.overview.delete.header": "Delete processes", - // TODO New key - Add a translation - "process.overview.delete.header": "Delete processes", + "process.overview.delete.header": "Процесті жою", // "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": "Процесті жою қатесі", // "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": "{{processId}} идентификаторы бар Процесс жойылмады. Қалған процестер әлі де жойылады. ", // "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}} процестер сәтті жойылды", @@ -4604,21 +4504,19 @@ "profile.breadcrumbs": "Профильді жаңарту", // "profile.card.identify": "Identify", - "profile.card.identify":"Анықтау", + "profile.card.identify": "Анықтау", // "profile.card.security": "Security", "profile.card.security": "Қауіпсіздік", // "profile.form.submit": "Save", - // TODO Source message changed - Revise the translation "profile.form.submit": "Профильді жаңарту", // "profile.groups.head": "Authorization groups you belong to", "profile.groups.head": "Сіз тиесілі авторизация топтары", // "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": "Авторизация сіз тиесілі арнайы топтар", // "profile.head": "Update Profile", "profile.head": "Профильді жаңарту", @@ -4690,11 +4588,10 @@ "profile.title":"Профильді жаңарту", // "profile.card.researcher": "Researcher Profile", - // TODO New key - Add a translation - "profile.card.researcher": "Researcher Profile", + "profile.card.researcher": "Зерттеуші профилі", // "project.listelement.badge": "Research Project", - "project.listelement.badge":"Зерттеу жобасы", + "project.listelement.badge": "Зерттеу жобасы", // "project.page.contributor": "Contributors", "project.page.contributor": "Қалымдар", @@ -4766,16 +4663,13 @@ // "media-viewer.next": "Next", - // TODO New key - Add a translation - "media-viewer.next": "Next", + "media-viewer.next": "Келесісі", // "media-viewer.previous": "Previous", - // TODO New key - Add a translation - "media-viewer.previous": "Previous", + "media-viewer.previous": "Алдыңғы", // "media-viewer.playlist": "Playlist", - // TODO New key - Add a translation - "media-viewer.playlist": "Playlist", + "media-viewer.playlist": "Ойнату тізімі", // "register-email.title": "New user registration", @@ -4945,14 +4839,12 @@ // "repository.image.logo": "Repository logo", - "repository.image.logo": "Repository logo", + "repository.image.logo": "Репозиторий логотипі", // "repository.title.prefix": "DSpace Angular :: ", - // TODO New key - Add a translation "repository.title.prefix": "DSpace Angular :: ", // "repository.title.prefixDSpace": "DSpace Angular ::", - // TODO New key - Add a translation "repository.title.prefixDSpace": "DSpace Angular ::", @@ -5008,12 +4900,10 @@ "resource-policies.edit.page.failure.content":"Ресурстар саясатын өңдеуде қате пайда болды.", // "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": "Ресурстар саясатының мақсатты нысанын (тұлғаны немесе топты) өңдеу кезінде қате пайда болды.", // "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": "Ресурстар саясатын өңдеу кезінде қате пайда болды. Мақсат (адам немесе топ) сәтті жаңартылды.", // "resource-policies.edit.page.success.content": "Operation successful", "resource-policies.edit.page.success.content": "Операция сәтті өтті", @@ -5049,23 +4939,18 @@ "resource-policies.form.eperson-group-list.table.headers.name": "Аты", // "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": "Түрін өзгерту мүмкін емес", // "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": "Адамды топпен алмастыру мүмкін емес.", // "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": "Топты 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": "Ағымдағы ресурстар саясатын жойып, дұрыс түрімен жаңасын жасаңыз.", // "resource-policies.form.eperson-group-list.modal.close": "Ok", - // TODO New key - Add a translation "resource-policies.form.eperson-group-list.modal.close": "Ok", // "resource-policies.form.date.end.label": "End Date", @@ -5165,14 +5050,13 @@ "search.filters.applied.f.dateSubmitted": "Ұсыну күні", // "search.filters.applied.f.discoverable": "Non-discoverable", - // TODO Source message changed - Revise the translation "search.filters.applied.f.discoverable": "Жеке", // "search.filters.applied.f.entityType": "Item Type", "search.filters.applied.f.entityType": "Өнім түрі", // "search.filters.applied.f.has_content_in_original_bundle": "Has files", - "search.filters.applied.f.has_content_in_original_bundle":"Файлдар бар", + "search.filters.applied.f.has_content_in_original_bundle": "Файлдар бар", // "search.filters.applied.f.itemtype": "Type", "search.filters.applied.f.itemtype": "Түрі", @@ -5207,8 +5091,7 @@ "search.filters.filter.author.placeholder": "Автордың аты", // "search.filters.filter.author.label": "Search author name", - // TODO New key - Add a translation - "search.filters.filter.author.label": "Search author name", + "search.filters.filter.author.label": "Іздеу авторының аты", // "search.filters.filter.birthDate.head": "Birth Date", "search.filters.filter.birthDate.head": "Туған күні", @@ -5217,12 +5100,10 @@ "search.filters.filter.birthDate.placeholder":"Туған күні", // "search.filters.filter.birthDate.label": "Search birth date", - // TODO New key - Add a translation - "search.filters.filter.birthDate.label": "Search birth date", + "search.filters.filter.birthDate.label": "Туған күнді іздеу", // "search.filters.filter.collapse": "Collapse filter", - // TODO New key - Add a translation - "search.filters.filter.collapse": "Collapse filter", + "search.filters.filter.collapse": "Сүзгіні кішірейту", // "search.filters.filter.creativeDatePublished.head": "Date Published", "search.filters.filter.creativeDatePublished.head": "Жарияланған күні", @@ -5231,8 +5112,7 @@ "search.filters.filter.creativeDatePublished.placeholder": "Жарияланған күні", // "search.filters.filter.creativeDatePublished.label": "Search date published", - // TODO New key - Add a translation - "search.filters.filter.creativeDatePublished.label": "Search date published", + "search.filters.filter.creativeDatePublished.label": "Жарияланған күнімен іздеу", // "search.filters.filter.creativeWorkEditor.head": "Editor", "search.filters.filter.creativeWorkEditor.head": "Редактор", @@ -5241,8 +5121,7 @@ "search.filters.filter.creativeWorkEditor.placeholder": "Редактор", // "search.filters.filter.creativeWorkEditor.label": "Search editor", - // TODO New key - Add a translation - "search.filters.filter.creativeWorkEditor.label": "Search editor", + "search.filters.filter.creativeWorkEditor.label": "Редакторды іздеу", // "search.filters.filter.creativeWorkKeywords.head": "Subject", "search.filters.filter.creativeWorkKeywords.head": "Тақырып", @@ -5260,27 +5139,22 @@ "search.filters.filter.creativeWorkPublisher.placeholder":"Баспагер", // "search.filters.filter.creativeWorkPublisher.label": "Search publisher", - // TODO New key - Add a translation - "search.filters.filter.creativeWorkPublisher.label": "Search publisher", + "search.filters.filter.creativeWorkPublisher.label": "Баспагерді іздеу", // "search.filters.filter.dateIssued.head": "Date", - "search.filters.filter.dateIssued.head": "Кездесуге", + "search.filters.filter.dateIssued.head": "Күн", // "search.filters.filter.dateIssued.max.placeholder": "Maximum Date", - // TODO Source message changed - Revise the translation - "search.filters.filter.dateIssued.max.placeholder": "Ең төменгі күн", + "search.filters.filter.dateIssued.max.placeholder": "Ең жоғары күн", // "search.filters.filter.dateIssued.max.label": "End", - // TODO New key - Add a translation - "search.filters.filter.dateIssued.max.label": "End", + "search.filters.filter.dateIssued.max.label": "Соңы", // "search.filters.filter.dateIssued.min.placeholder": "Minimum Date", - // TODO Source message changed - Revise the translation - "search.filters.filter.dateIssued.min.placeholder":"Максималды күн", + "search.filters.filter.dateIssued.min.placeholder":"Минималды күн", // "search.filters.filter.dateIssued.min.label": "Start", - // TODO New key - Add a translation - "search.filters.filter.dateIssued.min.label": "Start", + "search.filters.filter.dateIssued.min.label": "Бастау", // "search.filters.filter.dateSubmitted.head": "Date submitted", "search.filters.filter.dateSubmitted.head": "Жіберілген күні", @@ -5289,14 +5163,13 @@ "search.filters.filter.dateSubmitted.placeholder": "Ұсыну күні", // "search.filters.filter.dateSubmitted.label": "Search date submitted", - // TODO New key - Add a translation - "search.filters.filter.dateSubmitted.label": "Search date submitted", + "search.filters.filter.dateSubmitted.label": "Жіберілген күнімен сұрау салу", // "search.filters.filter.discoverable.head": "Non-discoverable", "search.filters.filter.discoverable.head": "Жеке", // "search.filters.filter.withdrawn.head": "Withdrawn", - "search.filters.filter.withdrawn.head": "Отозванный", + "search.filters.filter.withdrawn.head": "Алынған", // "search.filters.filter.entityType.head": "Item Type", "search.filters.filter.entityType.head": "Өнім түрі", @@ -5305,12 +5178,10 @@ "search.filters.filter.entityType.placeholder": "Өнім түрі", // "search.filters.filter.entityType.label": "Search item type", - // TODO New key - Add a translation - "search.filters.filter.entityType.label": "Search item type", + "search.filters.filter.entityType.label": "Іздеу элементінің түрі", // "search.filters.filter.expand": "Expand filter", - // TODO New key - Add a translation - "search.filters.filter.expand": "Expand filter", + "search.filters.filter.expand": "Сүзгіні жаю", // "search.filters.filter.has_content_in_original_bundle.head": "Has files", "search.filters.filter.has_content_in_original_bundle.head": "Файлдар бар", @@ -5322,8 +5193,7 @@ "search.filters.filter.itemtype.placeholder":"Түрі", // "search.filters.filter.itemtype.label": "Search type", - // TODO New key - Add a translation - "search.filters.filter.itemtype.label": "Search type", + "search.filters.filter.itemtype.label": "Түрі бойынша іздеу", // "search.filters.filter.jobTitle.head": "Job Title", "search.filters.filter.jobTitle.head": "Лауазымы", @@ -5332,8 +5202,7 @@ "search.filters.filter.jobTitle.placeholder":"Лауазымы", // "search.filters.filter.jobTitle.label": "Search job title", - // TODO New key - Add a translation - "search.filters.filter.jobTitle.label": "Search job title", + "search.filters.filter.jobTitle.label": "Лауазым атауын іздеу", // "search.filters.filter.knowsLanguage.head": "Known language", "search.filters.filter.knowsLanguage.head": "Белгілі тіл", @@ -5342,8 +5211,7 @@ "search.filters.filter.knowsLanguage.placeholder": "Белгілі тіл", // "search.filters.filter.knowsLanguage.label": "Search known language", - // TODO New key - Add a translation - "search.filters.filter.knowsLanguage.label": "Search known language", + "search.filters.filter.knowsLanguage.label": "Белгілі тілде іздеу", // "search.filters.filter.namedresourcetype.head": "Status", "search.filters.filter.namedresourcetype.head": "Мәртебе", @@ -5352,8 +5220,7 @@ "search.filters.filter.namedresourcetype.placeholder": "Мәртебе", // "search.filters.filter.namedresourcetype.label": "Search status", - // TODO New key - Add a translation - "search.filters.filter.namedresourcetype.label": "Search status", + "search.filters.filter.namedresourcetype.label": "Іздеу мәртебесі", // "search.filters.filter.objectpeople.head": "People", "search.filters.filter.objectpeople.head": "Адамдар", @@ -5362,8 +5229,7 @@ "search.filters.filter.objectpeople.placeholder": "Адамдар", // "search.filters.filter.objectpeople.label": "Search people", - // TODO New key - Add a translation - "search.filters.filter.objectpeople.label": "Search people", + "search.filters.filter.objectpeople.label": "Тұлғаны іздеу", // "search.filters.filter.organizationAddressCountry.head": "Country", "search.filters.filter.organizationAddressCountry.head": "Ел", @@ -5372,8 +5238,7 @@ "search.filters.filter.organizationAddressCountry.placeholder": "Ел", // "search.filters.filter.organizationAddressCountry.label": "Search country", - // TODO New key - Add a translation - "search.filters.filter.organizationAddressCountry.label": "Search country", + "search.filters.filter.organizationAddressCountry.label": "Елді іздеу", // "search.filters.filter.organizationAddressLocality.head": "City", "search.filters.filter.organizationAddressLocality.head": "Қала", @@ -5382,8 +5247,7 @@ "search.filters.filter.organizationAddressLocality.placeholder": "Қаласы", // "search.filters.filter.organizationAddressLocality.label": "Search city", - // TODO New key - Add a translation - "search.filters.filter.organizationAddressLocality.label": "Search city", + "search.filters.filter.organizationAddressLocality.label": "Қаланы іздеу", // "search.filters.filter.organizationFoundingDate.head": "Date Founded", "search.filters.filter.organizationFoundingDate.head": "Құрылған күні", @@ -5392,8 +5256,7 @@ "search.filters.filter.organizationFoundingDate.placeholder": "Құрылған күні", // "search.filters.filter.organizationFoundingDate.label": "Search date founded", - // TODO New key - Add a translation - "search.filters.filter.organizationFoundingDate.label": "Search date founded", + "search.filters.filter.organizationFoundingDate.label": "Құрылған күні бойынша іздеу", // "search.filters.filter.scope.head": "Scope", "search.filters.filter.scope.head": "Қолдану саласы", @@ -5402,8 +5265,7 @@ "search.filters.filter.scope.placeholder": "Ауқым сүзгісі", // "search.filters.filter.scope.label": "Search scope filter", - // TODO New key - Add a translation - "search.filters.filter.scope.label": "Search scope filter", + "search.filters.filter.scope.label": "Іздеу аумағының сүзгісі", // "search.filters.filter.show-less": "Collapse", "search.filters.filter.show-less": "Коллапс", @@ -5418,8 +5280,7 @@ "search.filters.filter.subject.placeholder": "Тақырып", // "search.filters.filter.subject.label": "Search subject", - // TODO New key - Add a translation - "search.filters.filter.subject.label": "Search subject", + "search.filters.filter.subject.label": "Іздеу тақырыбы", // "search.filters.filter.submitter.head": "Submitter", "search.filters.filter.submitter.head": "Жіберуші", @@ -5428,8 +5289,7 @@ "search.filters.filter.submitter.placeholder":"Жіберуші", // "search.filters.filter.submitter.label": "Search submitter", - // TODO New key - Add a translation - "search.filters.filter.submitter.label": "Search submitter", + "search.filters.filter.submitter.label": "Іздеу жіберушіні", @@ -5496,20 +5356,16 @@ "search.results.empty": "Сіздің іздеуіңіз нәтиже бермеді.", // "search.results.view-result": "View", - // TODO New key - Add a translation - "search.results.view-result": "View", + "search.results.view-result": "Көрінісі", // "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": "Сұрауды орындау кезінде қате пайда болды, кейінірек қайталап көріңіз", // "default.search.results.head": "Search Results", - // TODO New key - Add a translation - "default.search.results.head": "Search Results", + "default.search.results.head": "Іздеу нәтижелері", // "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": "Іздеу нәтижелері", // "search.sidebar.close": "Back to results", @@ -5559,36 +5415,28 @@ "sorting.dc.title.DESC": "Кему тақырыбы", // "sorting.score.ASC": "Least Relevant", - // TODO New key - Add a translation - "sorting.score.ASC": "Least Relevant", + "sorting.score.ASC": "Ең Аз Өзекті", // "sorting.score.DESC": "Most Relevant", - // TODO Source message changed - Revise the translation - "sorting.score.DESC": "Өзектілігі", + "sorting.score.DESC": "Өзектілігі көбірек", // "sorting.dc.date.issued.ASC": "Date Issued Ascending", - // TODO New key - Add a translation - "sorting.dc.date.issued.ASC": "Date Issued Ascending", + "sorting.dc.date.issued.ASC": "Өсу бойынша шығарылған күні", // "sorting.dc.date.issued.DESC": "Date Issued Descending", - // TODO New key - Add a translation - "sorting.dc.date.issued.DESC": "Date Issued Descending", + "sorting.dc.date.issued.DESC": "Кему бойынша шығарылған күні", // "sorting.dc.date.accessioned.ASC": "Accessioned Date Ascending", - // TODO New key - Add a translation - "sorting.dc.date.accessioned.ASC": "Accessioned Date Ascending", + "sorting.dc.date.accessioned.ASC": "Өсу бойынша қол жеткізу күні", // "sorting.dc.date.accessioned.DESC": "Accessioned Date Descending", - // TODO New key - Add a translation - "sorting.dc.date.accessioned.DESC": "Accessioned Date Descending", + "sorting.dc.date.accessioned.DESC": "Кему бойынша қол жеткізу күні", // "sorting.lastModified.ASC": "Last modified Ascending", - // TODO New key - Add a translation - "sorting.lastModified.ASC": "Last modified Ascending", + "sorting.lastModified.ASC": "Өсу бойынша соңғы өзгеріс", // "sorting.lastModified.DESC": "Last modified Descending", - // TODO New key - Add a translation - "sorting.lastModified.DESC": "Last modified Descending", + "sorting.lastModified.DESC": "Кему бойынша соңғы өзгеріс", // "statistics.title": "Statistics", @@ -5627,15 +5475,13 @@ // "submission.edit.breadcrumbs": "Edit Submission", - // TODO New key - Add a translation - "submission.edit.breadcrumbs": "Edit Submission", + "submission.edit.breadcrumbs": "Жіберуді өңдеу", // "submission.edit.title": "Edit Submission", "submission.edit.title": "Жіберуді өңдеу", // "submission.general.cancel": "Cancel", - // TODO New key - Add a translation - "submission.general.cancel": "Cancel", + "submission.general.cancel": "Алып тастау", // "submission.general.cannot_submit": "You have not the privilege to make a new submission.", "submission.general.cannot_submit": "Сізде жаңа қойылым жасау артықшылығы жоқ.", @@ -5659,12 +5505,10 @@ "submission.general.discard.submit": "Тастау", // "submission.general.info.saved": "Saved", - // TODO New key - Add a translation - "submission.general.info.saved": "Saved", + "submission.general.info.saved": "Сақталған", // "submission.general.info.pending-changes": "Unsaved changes", - // TODO New key - Add a translation - "submission.general.info.pending-changes": "Unsaved changes", + "submission.general.info.pending-changes": "Сақталмаған өзгерістер", // "submission.general.save": "Save", "submission.general.save": "Сақтау", @@ -5680,36 +5524,28 @@ "submission.import-external.title":"Метадеректерді сыртқы көзден импорттау", // "submission.import-external.title.Journal": "Import a journal from an external source", - // TODO New key - Add a translation - "submission.import-external.title.Journal": "Import a journal from an external source", + "submission.import-external.title.Journal": "Журналды сыртқы көзден импорттау", // "submission.import-external.title.JournalIssue": "Import a journal issue from an external source", - // TODO New key - Add a translation - "submission.import-external.title.JournalIssue": "Import a journal issue from an external source", + "submission.import-external.title.JournalIssue": "Журнал шығарылымын сыртқы көзден импорттаңыз", // "submission.import-external.title.JournalVolume": "Import a journal volume from an external source", - // TODO New key - Add a translation - "submission.import-external.title.JournalVolume": "Import a journal volume from an external source", + "submission.import-external.title.JournalVolume": "Журнал көлемін сыртқы көзден импорттаңыз", // "submission.import-external.title.OrgUnit": "Import a publisher from an external source", - // TODO New key - Add a translation - "submission.import-external.title.OrgUnit": "Import a publisher from an external source", + "submission.import-external.title.OrgUnit": "Баспаны сыртқы көзден импорттаңыз", // "submission.import-external.title.Person": "Import a person from an external source", - // TODO New key - Add a translation - "submission.import-external.title.Person": "Import a person from an external source", + "submission.import-external.title.Person": "Тұлғаны сыртқы көзден импорттаңыз", // "submission.import-external.title.Project": "Import a project from an external source", - // TODO New key - Add a translation - "submission.import-external.title.Project": "Import a project from an external source", + "submission.import-external.title.Project": "Жобаны сыртқы көзден импорттаңыз", // "submission.import-external.title.Publication": "Import a publication from an external source", - // TODO New key - Add a translation - "submission.import-external.title.Publication": "Import a publication from an external source", + "submission.import-external.title.Publication": "Жарияланымды сыртқы көзден импорттау", // "submission.import-external.title.none": "Import metadata from an external source", - // TODO New key - Add a translation - "submission.import-external.title.none": "Import metadata from an external source", + "submission.import-external.title.none": "Метадеректерді сыртқы көзден импорттау", // "submission.import-external.page.hint": "Enter a query above to find items from the web to import in to DSpace.", "submission.import-external.page.hint": "Ғарышқа импорттау үшін интернеттен элементтерді табу үшін жоғарыдағы сұрауды енгізіңіз.", @@ -5733,67 +5569,55 @@ "submission.import-external.source.arxiv": "arXiv", // "submission.import-external.source.ads": "NASA/ADS", - // TODO New key - Add a translation "submission.import-external.source.ads": "NASA/ADS", // "submission.import-external.source.cinii": "CiNii", - // TODO New key - Add a translation "submission.import-external.source.cinii": "CiNii", // "submission.import-external.source.crossref": "CrossRef", - // TODO New key - Add a translation "submission.import-external.source.crossref": "CrossRef", // "submission.import-external.source.scielo": "SciELO", - // TODO New key - Add a translation "submission.import-external.source.scielo": "SciELO", // "submission.import-external.source.scopus": "Scopus", - // TODO New key - Add a translation "submission.import-external.source.scopus": "Scopus", // "submission.import-external.source.vufind": "VuFind", - // TODO New key - Add a translation "submission.import-external.source.vufind": "VuFind", // "submission.import-external.source.wos": "Web Of Science", - // TODO New key - Add a translation - "submission.import-external.source.wos": "Web Of Science", + "submission.import-external.source.wos": "Ғылым Желісі", // "submission.import-external.source.orcidWorks": "ORCID", - // TODO New key - Add a translation "submission.import-external.source.orcidWorks": "ORCID", // "submission.import-external.source.epo": "European Patent Office (EPO)", - // TODO New key - Add a translation - "submission.import-external.source.epo": "European Patent Office (EPO)", + "submission.import-external.source.epo": "Еуропалық патенттік ведомство (ЕПВ)", // "submission.import-external.source.loading": "Loading ...", "submission.import-external.source.loading": "Жүктеу...", // "submission.import-external.source.sherpaJournal": "SHERPA Journals", - "submission.import-external.source.sherpaJournal": "ШЕРПА күнделіктері", + "submission.import-external.source.sherpaJournal": "ШЕРПА журналдары", // "submission.import-external.source.sherpaJournalIssn": "SHERPA Journals by ISSN", - // TODO New key - Add a translation - "submission.import-external.source.sherpaJournalIssn": "SHERPA Journals by ISSN", + "submission.import-external.source.sherpaJournalIssn": "SHERPA журналдағы ISSN", // "submission.import-external.source.sherpaPublisher": "SHERPA Publishers", "submission.import-external.source.sherpaPublisher": "ШЕРПА баспасы", // "submission.import-external.source.openAIREFunding": "Funding OpenAIRE API", - // TODO New key - Add a translation - "submission.import-external.source.openAIREFunding": "Funding OpenAIRE API", + "submission.import-external.source.openAIREFunding": "OpenAIRE API қаржыландыру", // "submission.import-external.source.orcid": "ORCID", "submission.import-external.source.orcid": "ОРКИД", // "submission.import-external.source.pubmed": "Pubmed", - "submission.import-external.source.pubmed": "Pubmed", + "submission.import-external.source.pubmed": "Жарияланған", // "submission.import-external.source.pubmedeu": "Pubmed Europe", - // TODO New key - Add a translation - "submission.import-external.source.pubmedeu": "Pubmed Europe", + "submission.import-external.source.pubmedeu": "Жарияланған Еуропа", // "submission.import-external.source.lcname": "Library of Congress Names", "submission.import-external.source.lcname": "Конгресс Кітапханасының Атаулары", @@ -5802,24 +5626,19 @@ "submission.import-external.preview.title": "Элементті алдын-ала қарау", // "submission.import-external.preview.title.Publication": "Publication Preview", - // TODO New key - Add a translation - "submission.import-external.preview.title.Publication": "Publication Preview", + "submission.import-external.preview.title.Publication": "Жарияланымдарды алдын ала қарау", // "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": "Элементті алдын ала қарау", // "submission.import-external.preview.title.Journal": "Journal Preview", - // TODO New key - Add a translation - "submission.import-external.preview.title.Journal": "Journal Preview", + "submission.import-external.preview.title.Journal": "Журналды алдын ала қарау", // "submission.import-external.preview.title.OrgUnit": "Organizational Unit Preview", - // TODO New key - Add a translation - "submission.import-external.preview.title.OrgUnit": "Organizational Unit Preview", + "submission.import-external.preview.title.OrgUnit": "Мекемені алдын ала қарау", // "submission.import-external.preview.title.Person": "Person Preview", - // TODO New key - Add a translation - "submission.import-external.preview.title.Person": "Person Preview", + "submission.import-external.preview.title.Person": "Жобаны алдын ала қарау", // "submission.import-external.preview.title.Project": "Project Preview", "submission.import-external.preview.title.Project": "Жобаны алдын ала қарау", @@ -6543,11 +6362,9 @@ "submission.sections.sherpa.publication.information.title": "Атауы", // "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.url": "URL", - // TODO New key - Add a translation "submission.sections.sherpa.publication.information.url": "URL", // "submission.sections.sherpa.publication.information.publishers": "Publisher", @@ -7256,7 +7073,5 @@ // "home.recent-submissions.head": "Recent Submissions", "home.recent-submissions.head": "Соңғы материалдар", - - - + } From 4489b3417c633386b82ee5de1126465bf5ba4234 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 28 Sep 2022 18:09:30 +0200 Subject: [PATCH 034/758] 94391: intermittent commit --- ...expandable-admin-sidebar-section.component.html | 2 +- .../shared/dso-page/dso-edit-menu.resolver.spec.ts | 8 +++++--- src/app/shared/dso-page/dso-edit-menu.resolver.ts | 9 +++++---- ...dso-edit-menu-expandable-section.component.html | 14 ++++++++------ src/styles/_global-styles.scss | 5 +++++ 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html index 259dbd10600..1f4666bbd08 100644 --- a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html +++ b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html @@ -7,7 +7,7 @@ [attr.aria-labelledby]="'sidebarName-' + section.id" [attr.aria-expanded]="expanded | async" [title]="('menu.section.icon.' + section.id) | translate" - [class.disabled]="section.model.disabled" + [class.disabled]="section.model?.disabled" (click)="toggleSection($event)" (keyup.space)="toggleSection($event)" (keyup.enter)="toggleSection($event)" diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts index 3f1e34b0e0d..ac4c4dae174 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts @@ -15,6 +15,8 @@ import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-ve import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { Item } from '../../core/shared/item.model'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { TextMenuItemModel } from '../menu/menu-item/models/text.model'; +import { LinkMenuItemModel } from '../menu/menu-item/models/link.model'; describe('DSOEditMenuResolver', () => { @@ -164,7 +166,7 @@ describe('DSOEditMenuResolver', () => { expect(menuList[0].active).toEqual(false); expect(menuList[0].visible).toEqual(true); expect(menuList[0].model.type).toEqual(MenuItemType.ONCLICK); - expect(menuList[0].model.text).toEqual('message'); + expect((menuList[0].model as TextMenuItemModel).text).toEqual('message'); expect(menuList[0].model.disabled).toEqual(false); expect(menuList[0].icon).toEqual('code-branch'); done(); @@ -179,8 +181,8 @@ describe('DSOEditMenuResolver', () => { expect(menuList[0].active).toEqual(false); expect(menuList[0].visible).toEqual(true); expect(menuList[0].model.type).toEqual(MenuItemType.LINK); - expect(menuList[0].model.text).toEqual('item.page.edit'); - expect(menuList[0].model.link).toEqual('test-url/edit/metadata'); + expect((menuList[0].model as LinkMenuItemModel).text).toEqual('item.page.edit'); + expect((menuList[0].model as LinkMenuItemModel).link).toEqual('test-url/edit/metadata'); expect(menuList[0].icon).toEqual('pencil-alt'); done(); }); 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 d4615843585..ceedda7b600 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -16,6 +16,7 @@ import { URLCombiner } from '../../core/url-combiner/url-combiner'; import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service'; import { hasValue } from '../empty.util'; import { MenuSection } from '../menu/menu.reducer'; +import { TextMenuItemModel } from '../menu/menu-item/models/text.model'; /** * Creates the menus for the dspace object pages @@ -65,17 +66,17 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection /** * Return all the menus for a dso based on the route and state */ - getDsoMenus(dso, route, state) { + getDsoMenus(dso, route, state): Observable[] { return [ this.getItemMenu(dso), - this.getCommonMenu(dso, state) + this.getCommonMenu(dso, state), ]; } /** * Get the common menus between all dspace objects */ - protected getCommonMenu(dso, state): Observable { + protected getCommonMenu(dso, state): Observable { return combineLatest([ this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, dso.self), ]).pipe( @@ -102,7 +103,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection /** * Get item sepcific menus */ - protected getItemMenu(dso): Observable { + protected getItemMenu(dso): Observable { if (dso instanceof Item) { return combineLatest([ this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, dso.self), diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html index b330b1e87d5..d44238eb6e4 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html @@ -1,14 +1,16 @@ -
+
- -
    - +
      +
diff --git a/src/styles/_global-styles.scss b/src/styles/_global-styles.scss index e337539c157..c71a9695f94 100644 --- a/src/styles/_global-styles.scss +++ b/src/styles/_global-styles.scss @@ -92,3 +92,8 @@ ngb-modal-backdrop { hyphens: auto; } + +ul.dso-edit-dropdown-menu li.nav-item ng-deep a.nav-link { + padding: 0; + display: inline; +} From 785e4085fcb4432d26997e51fa7287037cf3e75e Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Wed, 28 Sep 2022 18:28:41 +0200 Subject: [PATCH 035/758] 94389: Fix icon alignment & dropdown styles --- src/app/shared/dso-page/dso-edit-menu.resolver.ts | 2 -- .../dso-edit-menu-expandable-section.component.html | 9 ++++++--- .../dso-edit-menu-expandable-section.component.scss | 4 ++++ .../dso-edit-menu-expandable-section.component.ts | 12 +++++++++++- src/styles/_global-styles.scss | 3 ++- 5 files changed, 23 insertions(+), 7 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 ceedda7b600..2bf1aa08b5e 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -16,7 +16,6 @@ import { URLCombiner } from '../../core/url-combiner/url-combiner'; import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service'; import { hasValue } from '../empty.util'; import { MenuSection } from '../menu/menu.reducer'; -import { TextMenuItemModel } from '../menu/menu-item/models/text.model'; /** * Creates the menus for the dspace object pages @@ -95,7 +94,6 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection index: 1 }, ]; - }) ); } diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html index d44238eb6e4..a119cf03272 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.html @@ -4,9 +4,12 @@ -
    -
From 603c60f36865ecf258e654e01dc2590a1f61c6e4 Mon Sep 17 00:00:00 2001 From: Lucas Zinato Carraro Date: Wed, 12 Oct 2022 11:38:18 -0300 Subject: [PATCH 046/758] Add missing translations in version 7.4 pt-BR Add missing keywords, and correct translations in pt-BR --- src/assets/i18n/pt-BR.json5 | 53 ++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/assets/i18n/pt-BR.json5 b/src/assets/i18n/pt-BR.json5 index dae60803f57..7d4c49fa2d8 100644 --- a/src/assets/i18n/pt-BR.json5 +++ b/src/assets/i18n/pt-BR.json5 @@ -745,6 +745,33 @@ // "admin.access-control.groups.form.return": "Back", "admin.access-control.groups.form.return": "Voltar", + //"admin.batch-import.breadcrumbs": "Import Batch", + "admin.batch-import.breadcrumbs": "Importar um Lote", + + //"admin.batch-import.title": "Import Batch", + "admin.batch-import.title": "Importar um Lote", + + //"admin.batch-import.page.header": "Import Batch", + "admin.batch-import.page.header": "Importar um Lote", + + //"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": "Selecione a Coleção para o qual deseja importar. Arraste ou selecione um arquivo zip no formato Simple Archive Format (SAF) que inclua os Items para importar", + + //"admin.batch-import.page.dropMsg": "Drop a batch ZIP to import", + "admin.batch-import.page.dropMsg": "Arraste e solte um lote ZIP para importar", + + //"admin.batch-import.page.dropMsgReplace": "Drop to replace the batch ZIP to import", + "admin.batch-import.page.dropMsgReplace": "Arraste e solte um lote ZIP para substituir o lote para importar", + + //"admin.batch-import.page.error.addFile": "Select Zip file first!", + "admin.batch-import.page.error.addFile": "Selecione um arquivo ZIP primeiro!", + + //"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": "Quando selecionado , o ZIP enviado sera validado. Você receberá um relatório das alterações detectadas, mas nenhuma alteração será salva.", + + //"admin.batch-import.page.remove": "remove", + "admin.batch-import.page.remove": "remover", + // "admin.search.breadcrumbs": "Administrative Search", "admin.search.breadcrumbs": "Pesquisa Administrativa", @@ -820,6 +847,9 @@ // "admin.metadata-import.page.button.proceed": "Proceed", "admin.metadata-import.page.button.proceed": "Continuar", + //"admin.metadata-import.page.button.select-collection": "Select Collection", + "admin.metadata-import.page.button.select-collection": "Selecione a Coleção", + // "admin.metadata-import.page.error.addFile": "Select file first!", "admin.metadata-import.page.error.addFile": "Selecione o arquivo primeiro!", @@ -1176,7 +1206,7 @@ "collection.edit.item-mapper.confirm": "Mapear itens selecionados", // "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": "Esta é a ferramenta de mapeação de itens que permite administradores de coleções a mapear itens de outras coleções nesta. VoCẽ pode busca-los em outras coleções para mapeá-los, ou navegar na lista dos itens atualmente mapeados.", + "collection.edit.item-mapper.description": "Esta é a ferramenta de mapeação de itens que permite administradores de coleções a mapear itens de outras coleções nesta. Você pode buscar em outras coleções para mapear, ou navegar na lista dos itens atualmente mapeados.", // "collection.edit.item-mapper.head": "Item Mapper - Map Items from Other Collections", "collection.edit.item-mapper.head": "Mapeador de Itens - Mapear itens em Outras Coleções", @@ -1912,6 +1942,12 @@ // "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", "dso-selector.export-metadata.dspaceobject.head": "Exportar metadados de", + //"dso-selector.export-batch.dspaceobject.head": "Export Batch (ZIP) from", + "dso-selector.export-batch.dspaceobject.head": "Exportar Lote (ZIP) de", + + //"dso-selector.import-batch.dspaceobject.head": "Import batch from", + "dso-selector.import-batch.dspaceobject.head": "Importar lote de", + // "dso-selector.no-results": "No {{ type }} found", "dso-selector.no-results": "Nenhum(a) {{ type }} encontrado(a)", @@ -1954,6 +1990,18 @@ // "confirmation-modal.export-metadata.confirm": "Export", "confirmation-modal.export-metadata.confirm": "Exportar", + //"confirmation-modal.export-batch.header": "Export batch (ZIP) for {{ dsoName }}", + "confirmation-modal.export-batch.header": "Exportar lote (ZIP) para {{ dsoName }}", + + //"confirmation-modal.export-batch.info": "Are you sure you want to export batch (ZIP) for {{ dsoName }}", + "confirmation-modal.export-batch.info": "Você tem certeza que deseja exportar o lote (ZIP) para {{ dsoName }}", + + //"confirmation-modal.export-batch.cancel": "Cancel", + "confirmation-modal.export-batch.cancel": "Cancelar", + + //"confirmation-modal.export-batch.confirm": "Export", + "confirmation-modal.export-batch.confirm": "Exportar", + // "confirmation-modal.delete-eperson.header": "Delete EPerson \"{{ dsoName }}\"", "confirmation-modal.delete-eperson.header": "Apagar EPerson \"{{ dsoName }}\"", @@ -3748,6 +3796,9 @@ // "menu.section.import_batch": "Batch Import (ZIP)", "menu.section.import_batch": "Importação em Lote (ZIP)", + + // "menu.section.export_batch": "Batch Export (ZIP)", + "menu.section.export_batch": "Exportação em Lote (ZIP)", // "menu.section.import_metadata": "Metadata", "menu.section.import_metadata": "Metadados", From 519e031e6a77124edec2ba6f838df504328b831b Mon Sep 17 00:00:00 2001 From: Lucas Zinato Carraro Date: Wed, 12 Oct 2022 12:06:03 -0300 Subject: [PATCH 047/758] Fix missing { --- .../home-page/recent-item-list/recent-item-list.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.html b/src/app/home-page/recent-item-list/recent-item-list.component.html index 7f9ca2646bb..cd14891b3ff 100644 --- a/src/app/home-page/recent-item-list/recent-item-list.component.html +++ b/src/app/home-page/recent-item-list/recent-item-list.component.html @@ -6,7 +6,7 @@

{{'home.recent-submissions.head' | translate}}

- +
From ff1ea804c9408fb7b7a1a2361eb34c386dddf260 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 13 Oct 2022 14:02:40 +0200 Subject: [PATCH 048/758] 95648: Issue 1900 - URl encode/escape prefix value in facets endpoint --- src/app/core/shared/search/search.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 75723366bc2..d3cca3dc908 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -376,7 +376,7 @@ export class SearchService implements OnDestroy { let href; const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`]; if (hasValue(filterQuery)) { - args.push(`prefix=${filterQuery}`); + args.push(`prefix=${encodeURIComponent(filterQuery)}`); } if (hasValue(searchOptions)) { href = searchOptions.toRestUrl(filterConfig._links.self.href, args); From f5cbe54348ce6e685633142ac02f8fa9f5951bc1 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 14 Oct 2022 11:09:54 -0500 Subject: [PATCH 049/758] Add data.util.spec.ts which all pass when using moment.js --- src/app/shared/date.util.spec.ts | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/app/shared/date.util.spec.ts diff --git a/src/app/shared/date.util.spec.ts b/src/app/shared/date.util.spec.ts new file mode 100644 index 00000000000..abdfc9a88fa --- /dev/null +++ b/src/app/shared/date.util.spec.ts @@ -0,0 +1,89 @@ +import { dateToString, dateToNgbDateStruct, dateToISOFormat, isValidDate } from './date.util'; + +fdescribe('Date Utils', () => { + + describe('dateToISOFormat', () => { + it('should convert Date to YYYY-MM-DDThh:mm:ssZ string', () => { + // NOTE: month is zero indexed which is why it increases by one + expect(dateToISOFormat(new Date(2022, 5, 3))).toEqual('2022-06-03T00:00:00Z'); + }); + it('should convert Date string to YYYY-MM-DDThh:mm:ssZ string', () => { + expect(dateToISOFormat('2022-06-03')).toEqual('2022-06-03T00:00:00Z'); + }); + it('should convert Month string to YYYY-MM-DDThh:mm:ssZ string', () => { + expect(dateToISOFormat('2022-06')).toEqual('2022-06-01T00:00:00Z'); + }); + it('should convert Year string to YYYY-MM-DDThh:mm:ssZ string', () => { + expect(dateToISOFormat('2022')).toEqual('2022-01-01T00:00:00Z'); + }); + it('should convert ISO Date string to YYYY-MM-DDThh:mm:ssZ string', () => { + // NOTE: Time is always zeroed out as proven by this test. + expect(dateToISOFormat('2022-06-03T03:24:04Z')).toEqual('2022-06-03T00:00:00Z'); + }); + it('should convert NgbDateStruct to YYYY-MM-DDThh:mm:ssZ string', () => { + // NOTE: month is zero indexed which is why it increases by one + const date = new Date(2022, 5, 3); + expect(dateToISOFormat(dateToNgbDateStruct(date))).toEqual('2022-06-03T00:00:00Z'); + }); + }); + + describe('dateToString', () => { + it('should convert Date to YYYY-MM-DD string', () => { + // NOTE: month is zero indexed which is why it increases by one + expect(dateToString(new Date(2022, 5, 3))).toEqual('2022-06-03'); + }); + it('should convert Date with time to YYYY-MM-DD string', () => { + // NOTE: month is zero indexed which is why it increases by one + expect(dateToString(new Date(2022, 5, 3, 3, 24, 0))).toEqual('2022-06-03'); + }); + it('should convert Month only to YYYY-MM-DD string', () => { + // NOTE: month is zero indexed which is why it increases by one + expect(dateToString(new Date(2022, 5))).toEqual('2022-06-01'); + }); + it('should convert ISO Date to YYYY-MM-DD string', () => { + expect(dateToString(new Date('2022-06-03T03:24:00Z'))).toEqual('2022-06-03'); + }); + it('should convert NgbDateStruct to YYYY-MM-DD string', () => { + // NOTE: month is zero indexed which is why it increases by one + const date = new Date(2022, 5, 3); + expect(dateToString(dateToNgbDateStruct(date))).toEqual('2022-06-03'); + }); + }); + + + describe('isValidDate', () => { + it('should return false for null', () => { + expect(isValidDate(null)).toBe(false); + }); + it('should return false for empty string', () => { + expect(isValidDate('')).toBe(false); + }); + it('should return false for text', () => { + expect(isValidDate('test')).toBe(false); + }); + it('should return true for YYYY', () => { + expect(isValidDate('2022')).toBe(true); + }); + it('should return true for YYYY-MM', () => { + expect(isValidDate('2022-12')).toBe(true); + }); + it('should return true for YYYY-MM-DD', () => { + expect(isValidDate('2022-06-03')).toBe(true); + }); + it('should return true for YYYY-MM-DDTHH:MM:SS', () => { + expect(isValidDate('2022-06-03T10:20:30')).toBe(true); + }); + it('should return true for YYYY-MM-DDTHH:MM:SSZ', () => { + expect(isValidDate('2022-06-03T10:20:30Z')).toBe(true); + }); + it('should return false for a month that does not exist', () => { + expect(isValidDate('2022-13')).toBe(false); + }); + it('should return false for a day that does not exist', () => { + expect(isValidDate('2022-02-60')).toBe(false); + }); + it('should return false for a time that does not exist', () => { + expect(isValidDate('2022-02-60T10:60:20')).toBe(false); + }); + }); +}); \ No newline at end of file From 3eca2b323e127c13fda1dac93919acc97facea12 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 14 Oct 2022 11:18:11 -0500 Subject: [PATCH 050/758] Bug fix to initial test --- src/app/shared/date.util.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/date.util.spec.ts b/src/app/shared/date.util.spec.ts index abdfc9a88fa..fbc8d60e3fc 100644 --- a/src/app/shared/date.util.spec.ts +++ b/src/app/shared/date.util.spec.ts @@ -1,11 +1,11 @@ import { dateToString, dateToNgbDateStruct, dateToISOFormat, isValidDate } from './date.util'; -fdescribe('Date Utils', () => { +describe('Date Utils', () => { describe('dateToISOFormat', () => { it('should convert Date to YYYY-MM-DDThh:mm:ssZ string', () => { // NOTE: month is zero indexed which is why it increases by one - expect(dateToISOFormat(new Date(2022, 5, 3))).toEqual('2022-06-03T00:00:00Z'); + expect(dateToISOFormat(new Date(Date.UTC(2022, 5, 3)))).toEqual('2022-06-03T00:00:00Z'); }); it('should convert Date string to YYYY-MM-DDThh:mm:ssZ string', () => { expect(dateToISOFormat('2022-06-03')).toEqual('2022-06-03T00:00:00Z'); From c75f5f7c8123fc5f861c9b14eb74597783710148 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Fri, 7 Oct 2022 19:23:16 +0530 Subject: [PATCH 051/758] [CST-7119] User menu fixes and improvements --- .../expandable-navbar-section.component.html | 2 +- .../expandable-navbar-section.component.scss | 7 ++++++ .../navbar-section.component.html | 4 +-- .../navbar-section.component.scss | 5 ++++ src/app/navbar/navbar.component.html | 5 +++- src/app/navbar/navbar.component.scss | 2 +- src/app/navbar/navbar.component.spec.ts | 25 ++++++++++++++++++- src/app/navbar/navbar.component.ts | 17 ++++++++++++- src/app/root/root.component.html | 6 ++--- .../auth-nav-menu.component.html | 14 +++++------ .../user-menu/user-menu.component.html | 11 +++++--- .../user-menu/user-menu.component.spec.ts | 18 +++++++++++-- .../user-menu/user-menu.component.ts | 7 +++++- src/assets/i18n/en.json5 | 4 ++- .../dspace/app/navbar/navbar.component.html | 7 ++++-- .../dspace/app/navbar/navbar.component.scss | 2 +- src/themes/dspace/styles/_global-styles.scss | 9 ------- 17 files changed, 108 insertions(+), 37 deletions(-) diff --git a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html index d8a517dc554..d04c22e60af 100644 --- a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html +++ b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html @@ -1,4 +1,4 @@ - diff --git a/src/app/navbar/navbar-section/navbar-section.component.scss b/src/app/navbar/navbar-section/navbar-section.component.scss index e69de29bb2d..7af4ada8e63 100644 --- a/src/app/navbar/navbar-section/navbar-section.component.scss +++ b/src/app/navbar/navbar-section/navbar-section.component.scss @@ -0,0 +1,5 @@ +.navbar-section { + display: flex; + align-items: center; + height: 100%; +} diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index b41f2ea7067..bc1e04f5130 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -1,10 +1,13 @@
diff --git a/src/themes/custom/app/shared/collection-dropdown/collection-dropdown.component.html b/src/themes/custom/app/shared/collection-dropdown/collection-dropdown.component.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/themes/custom/app/shared/collection-dropdown/collection-dropdown.component.scss b/src/themes/custom/app/shared/collection-dropdown/collection-dropdown.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/themes/custom/app/shared/collection-dropdown/collection-dropdown.component.ts b/src/themes/custom/app/shared/collection-dropdown/collection-dropdown.component.ts new file mode 100644 index 00000000000..4fdbd9125b2 --- /dev/null +++ b/src/themes/custom/app/shared/collection-dropdown/collection-dropdown.component.ts @@ -0,0 +1,15 @@ +import { + CollectionDropdownComponent as BaseComponent +} from '../../../../../app/shared/collection-dropdown/collection-dropdown.component'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-collection-dropdown', + templateUrl: '../../../../../app/shared/collection-dropdown/collection-dropdown.component.html', + // templateUrl: './collection-dropdown.component.html', + styleUrls: ['../../../../../app/shared/collection-dropdown/collection-dropdown.component.scss'] + // styleUrls: ['./collection-dropdown.component.scss'] +}) +export class CollectionDropdownComponent extends BaseComponent { + +} diff --git a/src/themes/custom/eager-theme.module.ts b/src/themes/custom/eager-theme.module.ts index 4e3c6f8b464..6bca5180928 100644 --- a/src/themes/custom/eager-theme.module.ts +++ b/src/themes/custom/eager-theme.module.ts @@ -43,6 +43,7 @@ import { import { CommunityListElementComponent } from './app/shared/object-list/community-list-element/community-list-element.component'; import { CollectionListElementComponent} from './app/shared/object-list/collection-list-element/collection-list-element.component'; +import { CollectionDropdownComponent } from './app/shared/collection-dropdown/collection-dropdown.component'; /** @@ -58,6 +59,7 @@ const ENTRY_COMPONENTS = [ CommunityListElementComponent, CollectionListElementComponent, + CollectionDropdownComponent, ]; const DECLARATIONS = [ From 02499f8019f3d47849df7d3662d2899e772444d2 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 31 Oct 2022 19:42:07 +0100 Subject: [PATCH 098/758] 96062: Theme the ExternalSourceEntryImportModalComponent --- ...-relation-external-source-tab.component.ts | 2 +- ...nal-source-entry-import-modal.component.ts | 22 +++++++++++++++++++ src/app/shared/form/form.module.ts | 4 ++++ ...l-source-entry-import-modal.component.html | 0 ...l-source-entry-import-modal.component.scss | 0 ...nal-source-entry-import-modal.component.ts | 15 +++++++++++++ src/themes/custom/lazy-theme.module.ts | 4 ++++ 7 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts create mode 100644 src/themes/custom/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.html create mode 100644 src/themes/custom/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.scss create mode 100644 src/themes/custom/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 diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts index e5ea98e5374..0c41586eabc 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts @@ -15,7 +15,7 @@ import { fadeIn, fadeInOut } from '../../../../../animations/fade'; import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; import { RelationshipOptions } from '../../../models/relationship-options.model'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/external-source-entry-import-modal.component'; +import { ThemedExternalSourceEntryImportModalComponent as ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/themed-external-source-entry-import-modal.component'; import { hasValue } from '../../../../../empty.util'; import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service'; import { Item } from '../../../../../../core/shared/item.model'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-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/themed-external-source-entry-import-modal.component.ts new file mode 100644 index 00000000000..26e6097c2d7 --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts @@ -0,0 +1,22 @@ +import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal.component'; +import { ThemedComponent } from '../../../../../../theme-support/themed.component'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-themed-external-source-entry-import-modal', + styleUrls: [], + templateUrl: '../../../../../../../shared/theme-support/themed.component.html', +}) +export class ThemedExternalSourceEntryImportModalComponent extends ThemedComponent { + protected getComponentName(): string { + return 'ExternalSourceEntryImportModalComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../../../../../themes/${themeName}/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`); + } + + protected importUnthemedComponent(): Promise { + return import(`./external-source-entry-import-modal.component`); + } +} diff --git a/src/app/shared/form/form.module.ts b/src/app/shared/form/form.module.ts index 62ab5bd6476..741d51b6160 100644 --- a/src/app/shared/form/form.module.ts +++ b/src/app/shared/form/form.module.ts @@ -30,6 +30,9 @@ import { ExistingRelationListElementComponent } from './builder/ds-dynamic-form- import { ExternalSourceEntryImportModalComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component'; import { CustomSwitchComponent } from './builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component'; import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; +import { + ThemedExternalSourceEntryImportModalComponent +} from './builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component'; const COMPONENTS = [ CustomSwitchComponent, @@ -53,6 +56,7 @@ const COMPONENTS = [ ExistingMetadataListElementComponent, ExistingRelationListElementComponent, ExternalSourceEntryImportModalComponent, + ThemedExternalSourceEntryImportModalComponent, FormComponent ]; diff --git a/src/themes/custom/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.html b/src/themes/custom/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.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/themes/custom/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.scss b/src/themes/custom/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.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/themes/custom/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/themes/custom/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 new file mode 100644 index 00000000000..cd1de5d159d --- /dev/null +++ b/src/themes/custom/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 @@ -0,0 +1,15 @@ +import { + ExternalSourceEntryImportModalComponent as BaseComponent +} from '../../../../../../../../../../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'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-external-source-entry-import-modal', + styleUrls: ['../../../../../../../../../../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.scss'], + // styleUrls: ['./external-source-entry-import-modal.component.scss'], + templateUrl: '../../../../../../../../../../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.html', + // templateUrl: './external-source-entry-import-modal.component.html' +}) +export class ExternalSourceEntryImportModalComponent extends BaseComponent { + +} diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index d2ac0ae7877..2ff4af79466 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -114,6 +114,9 @@ import { ObjectListComponent } from './app/shared/object-list/object-list.compon import { BrowseByMetadataPageComponent } from './app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component'; import { BrowseByDatePageComponent } from './app/browse-by/browse-by-date-page/browse-by-date-page.component'; import { BrowseByTitlePageComponent } from './app/browse-by/browse-by-title-page/browse-by-title-page.component'; +import { + ExternalSourceEntryImportModalComponent +} from './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'; const DECLARATIONS = [ FileSectionComponent, @@ -168,6 +171,7 @@ const DECLARATIONS = [ BrowseByMetadataPageComponent, BrowseByDatePageComponent, BrowseByTitlePageComponent, + ExternalSourceEntryImportModalComponent, ]; From 43df05fe35026a46bf19677aeb49092e600a20ad Mon Sep 17 00:00:00 2001 From: Nikunj Sharma Date: Tue, 1 Nov 2022 15:04:03 +0530 Subject: [PATCH 099/758] [CST-7217]applied css for facet break --- .../search-facet-option/search-facet-option.component.html | 2 +- .../search-facet-option/search-facet-option.component.scss | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html index 3c15eff1270..bf165274d50 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html @@ -4,7 +4,7 @@ [queryParams]="addQueryParams" queryParamsHandling="merge"> diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.scss b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.scss index 74cede54a86..45e32f21e7a 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.scss +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.scss @@ -7,3 +7,6 @@ a { vertical-align: text-top; } } +.break-facet { + word-break: break-all; +} From 46f108072059f4a5ec8dc5868a229ac06ac10fe6 Mon Sep 17 00:00:00 2001 From: AndrukhivAndriy <79985930+AndrukhivAndriy@users.noreply.github.com> Date: Tue, 1 Nov 2022 03:00:45 -0700 Subject: [PATCH 100/758] Add: Ukrainian translation --- src/assets/i18n/uk.json5 | 5475 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 5475 insertions(+) create mode 100644 src/assets/i18n/uk.json5 diff --git a/src/assets/i18n/uk.json5 b/src/assets/i18n/uk.json5 new file mode 100644 index 00000000000..b0413d9353a --- /dev/null +++ b/src/assets/i18n/uk.json5 @@ -0,0 +1,5475 @@ +{ + + // "401.help": "You're not authorized to access this page. You can use the button below to get back to the home page.", + + "401.help": "Ви не авторизовані для доступу до цієї сторінки. Ви можете скористатися кнопкою нижче, щоб повернутися на головну сторінку.", + + // "401.link.home-page": "Take me to the home page", + + "401.link.home-page": "Перейти на головну сторінку", + + // "401.unauthorized": "unauthorized", + + "401.unauthorized": "Ви не авторизовані", + + + + // "403.help": "You don't have permission to access this page. You can use the button below to get back to the home page.", + + "403.help": "Ви не маєте дозволу на доступ до цієї сторінки. Ви можете скористатися кнопкою нижче, щоб повернутися на головну сторінку.", + + // "403.link.home-page": "Take me to the home page", + + "403.link.home-page": "Перейти на головну сторінку", + + // "403.forbidden": "forbidden", + + "403.forbidden": "заборонено", + + + + // "404.help": "We can't find the page you're looking for. The page may have been moved or deleted. You can use the button below to get back to the home page. ", + "404.help": "Ми не можемо знайти сторінку, яку ви шукаєте. Можливо, сторінку було переміщено або видалено. Ви можете скористатися кнопкою нижче, щоб повернутися на головну сторінку. ", + + // "404.link.home-page": "Take me to the home page", + "404.link.home-page": "Перейти на головну сторінку", + + // "404.page-not-found": "page not found", + "404.page-not-found": "сторінка не знайдена", + + // "admin.curation-tasks.breadcrumbs": "System curation tasks", + + "admin.curation-tasks.breadcrumbs": "Управління системою", + + // "admin.curation-tasks.title": "System curation tasks", + + "admin.curation-tasks.title": "Управління системою", + + // "admin.curation-tasks.header": "System curation tasks", + + "admin.curation-tasks.header": "Управління системою", + + // "admin.registries.bitstream-formats.breadcrumbs": "Format registry", + + "admin.registries.bitstream-formats.breadcrumbs": "Реєстр форматів", + + // "admin.registries.bitstream-formats.create.breadcrumbs": "Bitstream format", + + "admin.registries.bitstream-formats.create.breadcrumbs": "Формат файлів", + + // "admin.registries.bitstream-formats.create.failure.content": "An error occurred while creating the new bitstream format.", + "admin.registries.bitstream-formats.create.failure.content": "Під час створення нового файлу сталася помилка.", + + // "admin.registries.bitstream-formats.create.failure.head": "Failure", + "admin.registries.bitstream-formats.create.failure.head": "Крах", + + // "admin.registries.bitstream-formats.create.head": "Create Bitstream format", + "admin.registries.bitstream-formats.create.head": "Створити формат файлу", + + // "admin.registries.bitstream-formats.create.new": "Add a new bitstream format", + "admin.registries.bitstream-formats.create.new": "Додати новий формат файлу", + + // "admin.registries.bitstream-formats.create.success.content": "The new bitstream format was successfully created.", + "admin.registries.bitstream-formats.create.success.content": "Новий формат файлу був успішно створений.", + + // "admin.registries.bitstream-formats.create.success.head": "Success", + "admin.registries.bitstream-formats.create.success.head": "Успішно", + + // "admin.registries.bitstream-formats.delete.failure.amount": "Failed to remove {{ amount }} format(s)", + "admin.registries.bitstream-formats.delete.failure.amount": "Неможливо видалити {{ amount }} формат(и)", + + // "admin.registries.bitstream-formats.delete.failure.head": "Failure", + "admin.registries.bitstream-formats.delete.failure.head": "Крах", + + // "admin.registries.bitstream-formats.delete.success.amount": "Successfully removed {{ amount }} format(s)", + "admin.registries.bitstream-formats.delete.success.amount": "Успішно видалено {{ amount }} формат(и)", + + // "admin.registries.bitstream-formats.delete.success.head": "Success", + "admin.registries.bitstream-formats.delete.success.head": "Успіх", + + // "admin.registries.bitstream-formats.description": "This list of bitstream formats provides information about known formats and their support level.", + "admin.registries.bitstream-formats.description": "Цей список форматів файлів містить інформацію про відомі формати та рівень їх підтримки.", + + // "admin.registries.bitstream-formats.edit.breadcrumbs": "Bitstream format", + + "admin.registries.bitstream-formats.edit.breadcrumbs": "Формат файлу", + + // "admin.registries.bitstream-formats.edit.description.hint": "", + "admin.registries.bitstream-formats.edit.description.hint": "", + + // "admin.registries.bitstream-formats.edit.description.label": "Description", + "admin.registries.bitstream-formats.edit.description.label": "Опис", + + // "admin.registries.bitstream-formats.edit.extensions.hint": "Extensions are file extensions that are used to automatically identify the format of uploaded files. You can enter several extensions for each format.", + "admin.registries.bitstream-formats.edit.extensions.hint": "Розширення — це розширення файлів, які використовуються для автоматичного визначення формату завантажених файлів. Ви можете ввести кілька розширень для кожного формату.", + + // "admin.registries.bitstream-formats.edit.extensions.label": "File extensions", + "admin.registries.bitstream-formats.edit.extensions.label": "Розширення файлу", + + // "admin.registries.bitstream-formats.edit.extensions.placeholder": "Enter a file extension without the dot", + "admin.registries.bitstream-formats.edit.extensions.placeholder": "Ввведіть розширення файлу без крапки", + + // "admin.registries.bitstream-formats.edit.failure.content": "An error occurred while editing the bitstream format.", + "admin.registries.bitstream-formats.edit.failure.content": "Виникла помилка при редагуванні формату файлу.", + + // "admin.registries.bitstream-formats.edit.failure.head": "Failure", + "admin.registries.bitstream-formats.edit.failure.head": "Крах", + + // "admin.registries.bitstream-formats.edit.head": "Bitstream format: {{ format }}", + "admin.registries.bitstream-formats.edit.head": "Формат файлу: {{ 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": "Формати, позначені як внутрішні, приховані від користувача та використовуються в адміністративних цілях.", + + // "admin.registries.bitstream-formats.edit.internal.label": "Internal", + "admin.registries.bitstream-formats.edit.internal.label": "Внутрішні", + + // "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": "Тип MIME, пов’язаний із цим форматом, не обов’язково має бути унікальним.", + + // "admin.registries.bitstream-formats.edit.mimetype.label": "MIME Type", + "admin.registries.bitstream-formats.edit.mimetype.label": "Тип MIME ", + + // "admin.registries.bitstream-formats.edit.shortDescription.hint": "A unique name for this format, (e.g. Microsoft Word XP or Microsoft Word 2000)", + "admin.registries.bitstream-formats.edit.shortDescription.hint": "Унікальна назва формату, (наприклад Microsoft Word XP or Microsoft Word 2000)", + + // "admin.registries.bitstream-formats.edit.shortDescription.label": "Name", + "admin.registries.bitstream-formats.edit.shortDescription.label": "Ім'я", + + // "admin.registries.bitstream-formats.edit.success.content": "The bitstream format was successfully edited.", + "admin.registries.bitstream-formats.edit.success.content": "Формат файлу успішно відредаговано.", + + // "admin.registries.bitstream-formats.edit.success.head": "Success", + "admin.registries.bitstream-formats.edit.success.head": "Успішно", + + // "admin.registries.bitstream-formats.edit.supportLevel.hint": "The level of support your institution pledges for this format.", + "admin.registries.bitstream-formats.edit.supportLevel.hint": "Рівень підтримки, який ваша установа обіцяє для цього формату.", + + // "admin.registries.bitstream-formats.edit.supportLevel.label": "Support level", + "admin.registries.bitstream-formats.edit.supportLevel.label": "Рівень підтримки", + + // "admin.registries.bitstream-formats.head": "Bitstream Format Registry", + "admin.registries.bitstream-formats.head": "Реєстр формату файлу", + + // "admin.registries.bitstream-formats.no-items": "No bitstream formats to show.", + "admin.registries.bitstream-formats.no-items": "Немає форматів файлів.", + + // "admin.registries.bitstream-formats.table.delete": "Delete selected", + "admin.registries.bitstream-formats.table.delete": "Видалити видалене", + + // "admin.registries.bitstream-formats.table.deselect-all": "Deselect all", + "admin.registries.bitstream-formats.table.deselect-all": "Зняти вибір із усіх", + + // "admin.registries.bitstream-formats.table.internal": "internal", + "admin.registries.bitstream-formats.table.internal": "внутрішні", + + // "admin.registries.bitstream-formats.table.mimetype": "MIME Type", + "admin.registries.bitstream-formats.table.mimetype": "Тип MIME", + + // "admin.registries.bitstream-formats.table.name": "Name", + "admin.registries.bitstream-formats.table.name": "Ім'я", + + // "admin.registries.bitstream-formats.table.return": "Return", + "admin.registries.bitstream-formats.table.return": "Повернутись", + + // "admin.registries.bitstream-formats.table.supportLevel.KNOWN": "Known", + "admin.registries.bitstream-formats.table.supportLevel.KNOWN": "Відомо", + + // "admin.registries.bitstream-formats.table.supportLevel.SUPPORTED": "Supported", + "admin.registries.bitstream-formats.table.supportLevel.SUPPORTED": "Підтримується", + + // "admin.registries.bitstream-formats.table.supportLevel.UNKNOWN": "Unknown", + "admin.registries.bitstream-formats.table.supportLevel.UNKNOWN": "Невідомо", + + // "admin.registries.bitstream-formats.table.supportLevel.head": "Support Level", + "admin.registries.bitstream-formats.table.supportLevel.head": "Рівні підтримки", + + // "admin.registries.bitstream-formats.title": "DSpace Angular :: Bitstream Format Registry", + "admin.registries.bitstream-formats.title": "Репозитарій :: реєстр формату файлу", + + + + // "admin.registries.metadata.breadcrumbs": "Metadata registry", + // TODO New key - Add a translation + "admin.registries.metadata.breadcrumbs": "Реєстр метаданих", + + // "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": "Реєстр метаданих підтримує список усіх полів метаданих, доступних у репозитарії. Ці поля можна розділити на кілька схем. Однак для ПЗ DSpace потрібна схема Dublin Core.", + + // "admin.registries.metadata.form.create": "Create metadata schema", + "admin.registries.metadata.form.create": "Створити схему метаданих", + + // "admin.registries.metadata.form.edit": "Edit metadata schema", + "admin.registries.metadata.form.edit": "Редагувати схему метаданих", + + // "admin.registries.metadata.form.name": "Name", + "admin.registries.metadata.form.name": "Ім'я", + + // "admin.registries.metadata.form.namespace": "Namespace", + "admin.registries.metadata.form.namespace": "Простір", + + // "admin.registries.metadata.head": "Metadata Registry", + "admin.registries.metadata.head": "Реєстр метаданих", + + // "admin.registries.metadata.schemas.no-items": "No metadata schemas to show.", + "admin.registries.metadata.schemas.no-items": "Немає схеми метаданих.", + + // "admin.registries.metadata.schemas.table.delete": "Delete selected", + "admin.registries.metadata.schemas.table.delete": "Видалити виділене", + + // "admin.registries.metadata.schemas.table.id": "ID", + "admin.registries.metadata.schemas.table.id": "Ідентифікатор", + + // "admin.registries.metadata.schemas.table.name": "Name", + "admin.registries.metadata.schemas.table.name": "Ім'я", + + // "admin.registries.metadata.schemas.table.namespace": "Namespace", + "admin.registries.metadata.schemas.table.namespace": "Простір", + + // "admin.registries.metadata.title": "DSpace Angular :: Metadata Registry", + "admin.registries.metadata.title": "Репозитарій :: Реєстр метаданих", + + + + // "admin.registries.schema.breadcrumbs": "Metadata schema", + + "admin.registries.schema.breadcrumbs": "Схема метаданих", + + // "admin.registries.schema.description": "This is the metadata schema for \"{{namespace}}\".", + "admin.registries.schema.description": "Це схема метаданих для \"{{namespace}}\".", + + // "admin.registries.schema.fields.head": "Schema metadata fields", + "admin.registries.schema.fields.head": "Поля схеми метаданих", + + // "admin.registries.schema.fields.no-items": "No metadata fields to show.", + "admin.registries.schema.fields.no-items": "Немає полів метаданих.", + + // "admin.registries.schema.fields.table.delete": "Delete selected", + "admin.registries.schema.fields.table.delete": "Видалити виділене", + + // "admin.registries.schema.fields.table.field": "Field", + "admin.registries.schema.fields.table.field": "Поле", + + // "admin.registries.schema.fields.table.scopenote": "Scope Note", + "admin.registries.schema.fields.table.scopenote": "Примітка щодо сфери застосування", + + // "admin.registries.schema.form.create": "Create metadata field", + "admin.registries.schema.form.create": "Створити поле метаданих", + + // "admin.registries.schema.form.edit": "Edit metadata field", + "admin.registries.schema.form.edit": "Редагувати поле метаданих", + + // "admin.registries.schema.form.element": "Element", + "admin.registries.schema.form.element": "Елементи", + + // "admin.registries.schema.form.qualifier": "Qualifier", + "admin.registries.schema.form.qualifier": "Кваліфікатор", + + // "admin.registries.schema.form.scopenote": "Scope Note", + "admin.registries.schema.form.scopenote": "Примітка щодо сфери застосування", + + // "admin.registries.schema.head": "Metadata Schema", + "admin.registries.schema.head": "Схема метаданих", + + // "admin.registries.schema.notification.created": "Successfully created metadata schema \"{{prefix}}\"", + "admin.registries.schema.notification.created": "Схема метаданих \"{{prefix}}\" успішно створена", + + // "admin.registries.schema.notification.deleted.failure": "Failed to delete {{amount}} metadata schemas", + "admin.registries.schema.notification.deleted.failure": "Неможливо видалити {{amount}} схему метаданих", + + // "admin.registries.schema.notification.deleted.success": "Successfully deleted {{amount}} metadata schemas", + "admin.registries.schema.notification.deleted.success": "{{amount}} схема метаданих успішно видалена", + + // "admin.registries.schema.notification.edited": "Successfully edited metadata schema \"{{prefix}}\"", + "admin.registries.schema.notification.edited": "Схема метаданих \"{{prefix}}\" успішно відредагована", + + // "admin.registries.schema.notification.failure": "Error", + "admin.registries.schema.notification.failure": "Помилка", + + // "admin.registries.schema.notification.field.created": "Successfully created metadata field \"{{field}}\"", + "admin.registries.schema.notification.field.created": "Поле метаданих \"{{field}}\" успішно створено", + + // "admin.registries.schema.notification.field.deleted.failure": "Failed to delete {{amount}} metadata fields", + "admin.registries.schema.notification.field.deleted.failure": "Неможливо видалити {{amount}} поле(я) метаданих", + + // "admin.registries.schema.notification.field.deleted.success": "Successfully deleted {{amount}} metadata fields", + "admin.registries.schema.notification.field.deleted.success": "{{amount}} поля метаданих успішно видалено", + + // "admin.registries.schema.notification.field.edited": "Successfully edited metadata field \"{{field}}\"", + "admin.registries.schema.notification.field.edited": "\"{{field}}\" поле метаданих успішно відредаговано", + + // "admin.registries.schema.notification.success": "Success", + "admin.registries.schema.notification.success": "Успіх", + + // "admin.registries.schema.return": "Return", + "admin.registries.schema.return": "Повернутись", + + // "admin.registries.schema.title": "DSpace Angular :: Metadata Schema Registry", + "admin.registries.schema.title": "Репозитарій :: реєстр схеми метаданих", + + + + // "admin.access-control.epeople.actions.delete": "Delete EPerson", + + "admin.access-control.epeople.actions.delete": "Видалити користувача", + + // "admin.access-control.epeople.actions.impersonate": "Impersonate EPerson", + + "admin.access-control.epeople.actions.impersonate": "Видати себе за користувача", + + // "admin.access-control.epeople.actions.reset": "Скинути пароль", + + "admin.access-control.epeople.actions.reset": "Скинути пароль", + + // "admin.access-control.epeople.actions.stop-impersonating": "Stop impersonating EPerson", + // TODO New key - Add a translation + "admin.access-control.epeople.actions.stop-impersonating": "Зупинити видавати себе за користувача", + + // "admin.access-control.epeople.title": "Репозитарій :: EPeople", + "admin.access-control.epeople.title": "Репозитарій :: Користувачі", + + // "admin.access-control.epeople.head": "EPeople", + "admin.access-control.epeople.head": "Користувачі", + + // "admin.access-control.epeople.search.head": "Search", + "admin.access-control.epeople.search.head": "Шукати", + + // "admin.access-control.epeople.button.see-all": "Browse All", + "admin.access-control.epeople.button.see-all": "Переглянути всі", + + // "admin.access-control.epeople.search.scope.metadata": "Metadata", + "admin.access-control.epeople.search.scope.metadata": "Метадані", + + // "admin.access-control.epeople.search.scope.email": "E-mail (exact)", + "admin.access-control.epeople.search.scope.email": "E-mail", + + // "admin.access-control.epeople.search.button": "Шукати", + "admin.access-control.epeople.search.button": "Шукати", + + // "admin.access-control.epeople.button.add": "Add EPerson", + "admin.access-control.epeople.button.add": "Додати користувача", + + // "admin.access-control.epeople.table.id": "ID", + "admin.access-control.epeople.table.id": "ID", + + // "admin.access-control.epeople.table.name": "Name", + "admin.access-control.epeople.table.name": "Ім'я", + + // "admin.access-control.epeople.table.email": "E-mail (exact)", + "admin.access-control.epeople.table.email": "E-mail", + + // "admin.access-control.epeople.table.edit": "Edit", + "admin.access-control.epeople.table.edit": "Редагувати", + + // "admin.access-control.epeople.table.edit.buttons.edit": "Edit \"{{name}}\"", + "admin.access-control.epeople.table.edit.buttons.edit": "Редагувати \"{{name}}\"", + + // "admin.access-control.epeople.table.edit.buttons.remove": "Delete \"{{name}}\"", + "admin.access-control.epeople.table.edit.buttons.remove": "Видалити \"{{name}}\"", + + // "admin.access-control.epeople.no-items": "No EPeople to show.", + "admin.access-control.epeople.no-items": "Користувачі відсутні.", + + // "admin.access-control.epeople.form.create": "Create EPerson", + "admin.access-control.epeople.form.create": "Створити користувача", + + // "admin.access-control.epeople.form.edit": "Edit EPerson", + "admin.access-control.epeople.form.edit": "Редагувати користувача", + + // "admin.access-control.epeople.form.firstName": "First name", + "admin.access-control.epeople.form.firstName": "Ім'я", + + // "admin.access-control.epeople.form.lastName": "Last name", + "admin.access-control.epeople.form.lastName": "Прізвище", + + // "admin.access-control.epeople.form.email": "E-mail", + "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": "E-mail повинен бути правильний", + + // "admin.access-control.epeople.form.canLogIn": "Can log in", + "admin.access-control.epeople.form.canLogIn": "Може увійти", + + // "admin.access-control.epeople.form.requireCertificate": "Requires certificate", + "admin.access-control.epeople.form.requireCertificate": "Вимагати сертифікат", + + // "admin.access-control.epeople.form.notification.created.success": "Successfully created EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.created.success": "Користувач \"{{name}}\" успішно створений", + + // "admin.access-control.epeople.form.notification.created.failure": "Failed to create EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.created.failure": "Неможливо створити користувача \"{{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": "Неможливо створити користувача \"{{name}}\", e-pasts \"{{email}}\" вже використовується.", + + // "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": "Неможливо редагувати користувача \"{{name}}\", e-pasts \"{{email}}\" вже використовується.", + + // "admin.access-control.epeople.form.notification.edited.success": "Successfully edited EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.edited.success": "Користувач \"{{name}}\" успішно відредагований", + + // "admin.access-control.epeople.form.notification.edited.failure": "Failed to edit EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.edited.failure": "Неможливо відредагувати користувача \"{{name}}\"", + + // "admin.access-control.epeople.form.notification.deleted.success": "Successfully deleted EPerson \"{{name}}\"", + + "admin.access-control.epeople.form.notification.deleted.success": "Користувач \"{{name}}\" успішно видалений", + + // "admin.access-control.epeople.form.notification.deleted.failure": "Failed to delete EPerson \"{{name}}\"", + + "admin.access-control.epeople.form.notification.deleted.failure": "Неможливо видалити користувача \"{{name}}\"", + + // "admin.access-control.epeople.form.groupsEPersonIsMemberOf": "Member of these groups:", + "admin.access-control.epeople.form.groupsEPersonIsMemberOf": "Член груп:", + + // "admin.access-control.epeople.form.table.id": "ID", + "admin.access-control.epeople.form.table.id": "ID", + + // "admin.access-control.epeople.form.table.name": "Name", + "admin.access-control.epeople.form.table.name": "Ім'я", + + // "admin.access-control.epeople.form.memberOfNoGroups": "This EPerson is not a member of any groups", + "admin.access-control.epeople.form.memberOfNoGroups": "Користувач не є членом жодної групи", + + // "admin.access-control.epeople.form.goToGroups": "Add to groups", + "admin.access-control.epeople.form.goToGroups": "Додати до груп", + + // "admin.access-control.epeople.notification.deleted.failure": "Failed to delete EPerson: \"{{name}}\"", + "admin.access-control.epeople.notification.deleted.failure": "Неможливо видалити користувача: \"{{name}}\"", + + // "admin.access-control.epeople.notification.deleted.success": "Successfully deleted EPerson: \"{{name}}\"", + "admin.access-control.epeople.notification.deleted.success": "Користувача \"{{name}}\" успішно видалено", + + + + // "admin.access-control.groups.title": "Репозитарій :: Групи", + "admin.access-control.groups.title": "Репозитарій :: Групи", + + // "admin.access-control.groups.title.singleGroup": "DSpace Angular :: Edit Group", + + "admin.access-control.groups.title.singleGroup": "Репозитарій :: Редагувати групу", + + // "admin.access-control.groups.title.addGroup": "DSpace Angular :: New Group", + + "admin.access-control.groups.title.addGroup": "Репозитарій :: Створити нову групу", + + // "admin.access-control.groups.head": "Groups", + "admin.access-control.groups.head": "Групи", + + // "admin.access-control.groups.button.add": "Add group", + "admin.access-control.groups.button.add": "Створити нову групу", + + // "admin.access-control.groups.search.head": "Search groups", + "admin.access-control.groups.search.head": "Шукати групу", + + // "admin.access-control.groups.button.see-all": "Browse all", + "admin.access-control.groups.button.see-all": "Переглянути всі", + + // "admin.access-control.groups.search.button": "Search", + "admin.access-control.groups.search.button": "Шукати", + + // "admin.access-control.groups.table.id": "ID", + "admin.access-control.groups.table.id": "ID", + + // "admin.access-control.groups.table.name": "Name", + "admin.access-control.groups.table.name": "Ім'я", + + // "admin.access-control.groups.table.members": "Members", + "admin.access-control.groups.table.members": "Члени", + + // "admin.access-control.groups.table.edit": "Edit", + "admin.access-control.groups.table.edit": "Редагувати", + + // "admin.access-control.groups.table.edit.buttons.edit": "Edit \"{{name}}\"", + "admin.access-control.groups.table.edit.buttons.edit": "Редагувати \"{{name}}\"", + + // "admin.access-control.groups.table.edit.buttons.remove": "Delete \"{{name}}\"", + "admin.access-control.groups.table.edit.buttons.remove": "Видалити \"{{name}}\"", + + // "admin.access-control.groups.no-items": "No groups found with this in their name or this as UUID", + "admin.access-control.groups.no-items": "Групи не знайдено за Вашими критеріями ", + + // "admin.access-control.groups.notification.deleted.success": "Successfully deleted group \"{{name}}\"", + "admin.access-control.groups.notification.deleted.success": "Група \"{{name}}\" успішно видалена", + + // "admin.access-control.groups.notification.deleted.failure.title": "Failed to delete group \"{{name}}\"", + + "admin.access-control.groups.notification.deleted.failure.title": "Групу \"{{name}}\" видалити неможливо", + + // "admin.access-control.groups.notification.deleted.failure.content": "Cause: \"{{cause}}\"", + + "admin.access-control.groups.notification.deleted.failure.content": "Причина: \"{{cause}}\"", + + + + // "admin.access-control.groups.form.alert.permanent": "This group is permanent, so it can't be edited or deleted. You can still add and remove group members using this page.", + + "admin.access-control.groups.form.alert.permanent": "Ця група є постійна, тому її не можна редагувати чи видаляти. Ви все ще можете так це додавати та видаляти учасників групи за допомогою цієї сторінки.", + + // "admin.access-control.groups.form.alert.workflowGroup": "This group can’t be modified or deleted because it corresponds to a role in the submission and workflow process in the \"{{name}}\" {{comcol}}. You can delete it from the \"assign roles\" tab on the edit {{comcol}} page. You can still add and remove group members using this page.", + + "admin.access-control.groups.form.alert.workflowGroup": "Цю групу не можна змінити або видалити, оскільки вона відповідає за ролі в процесі подання чи опрацювання документів в \"{{name}}\" {{comcol}}. Ви можете видалити її \"assign roles\" на сторінці {{comcol}} вкладки Редагування. Ви все ще можете додавати та видаляти учасників групи за допомогою цієї сторінки.", + + // "admin.access-control.groups.form.head.create": "Create group", + "admin.access-control.groups.form.head.create": "Створити групу", + + // "admin.access-control.groups.form.head.edit": "Edit group", + "admin.access-control.groups.form.head.edit": "Редагувати групу", + + // "admin.access-control.groups.form.groupName": "Group name", + "admin.access-control.groups.form.groupName": "Ім'я групи", + + // "admin.access-control.groups.form.groupDescription": "Description", + "admin.access-control.groups.form.groupDescription": "Опис", + + // "admin.access-control.groups.form.notification.created.success": "Successfully created Group \"{{name}}\"", + "admin.access-control.groups.form.notification.created.success": "Група \"{{name}}\" успішно створена", + + // "admin.access-control.groups.form.notification.created.failure": "Failed to create Group \"{{name}}\"", + "admin.access-control.groups.form.notification.created.failure": "Неможливо створити групу \"{{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": "Неможливо створити групу: \"{{name}}\". Це ім'я вже зайняте .", + + // "admin.access-control.groups.form.notification.edited.failure": "Failed to edit Group \"{{name}}\"", + + "admin.access-control.groups.form.notification.edited.failure": "Неможливо редагувати групу \"{{name}}\"", + + // "admin.access-control.groups.form.notification.edited.failure.groupNameInUse": "Ім'я \"{{name}}\" вже використовується!", + + "admin.access-control.groups.form.notification.edited.failure.groupNameInUse": "Ім'я \"{{name}}\" вже використовується!", + + // "admin.access-control.groups.form.notification.edited.success": "Група \"{{name}}\" успішно відредагована", + + "admin.access-control.groups.form.notification.edited.success": "Група \"{{name}}\" успішно відредагована", + + // "admin.access-control.groups.form.actions.delete": "Delete Group", + + "admin.access-control.groups.form.actions.delete": "Видалити групу", + + // "admin.access-control.groups.form.delete-group.modal.header": "Delete Group \"{{ dsoName }}\"", + + "admin.access-control.groups.form.delete-group.modal.header": "Видалити групу \"{{ dsoName }}\"", + + // "admin.access-control.groups.form.delete-group.modal.info": "Are you sure you want to delete Group \"{{ dsoName }}\"", + + "admin.access-control.groups.form.delete-group.modal.info": "Ви впевнені, що хочете видалити групу \"{{ dsoName }}\"?", + + // "admin.access-control.groups.form.delete-group.modal.cancel": "Cancel", + + "admin.access-control.groups.form.delete-group.modal.cancel": "Відмінити", + + // "admin.access-control.groups.form.delete-group.modal.confirm": "Delete", + + "admin.access-control.groups.form.delete-group.modal.confirm": "Видалити", + + // "admin.access-control.groups.form.notification.deleted.success": "Successfully deleted group \"{{ name }}\"", + + "admin.access-control.groups.form.notification.deleted.success": "Група \"{{ name }}\" успішно видалена", + + // "admin.access-control.groups.form.notification.deleted.failure.title": "Failed to delete group \"{{ name }}\"", + + "admin.access-control.groups.form.notification.deleted.failure.title": "Неможливо видалити групу \"{{ name }}\"", + + // "admin.access-control.groups.form.notification.deleted.failure.content": "Cause: \"{{ cause }}\"", + + "admin.access-control.groups.form.notification.deleted.failure.content": "Причина: \"{{ cause }}\"", + + // "admin.access-control.groups.form.members-list.head": "EPeople", + "admin.access-control.groups.form.members-list.head": "Користувачі", + + // "admin.access-control.groups.form.members-list.search.head": "Add EPeople", + "admin.access-control.groups.form.members-list.search.head": "Додати користувача", + + // "admin.access-control.groups.form.members-list.button.see-all": "Browse All", + "admin.access-control.groups.form.members-list.button.see-all": "Переглянути всіх", + + // "admin.access-control.groups.form.members-list.headMembers": "Current Members", + "admin.access-control.groups.form.members-list.headMembers": "Поточні учасники групи", + + // "admin.access-control.groups.form.members-list.search.scope.metadata": "Metadata", + "admin.access-control.groups.form.members-list.search.scope.metadata": "Метадані", + + // "admin.access-control.groups.form.members-list.search.scope.email": "E-mail (exact)", + "admin.access-control.groups.form.members-list.search.scope.email": "E-mail", + + // "admin.access-control.groups.form.members-list.search.button": "Search", + "admin.access-control.groups.form.members-list.search.button": "Шукати", + + // "admin.access-control.groups.form.members-list.table.id": "ID", + "admin.access-control.groups.form.members-list.table.id": "ID", + + // "admin.access-control.groups.form.members-list.table.name": "Name", + "admin.access-control.groups.form.members-list.table.name": "Ім'я", + + // "admin.access-control.groups.form.members-list.table.edit": "Remove / Add", + "admin.access-control.groups.form.members-list.table.edit": "Видалити / Додати", + + // "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": "Видалити учасника групи з ім'ям \"{{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": "Учасника \"{{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": "Неможливо додати \"{{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": "Учасника \"{{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": "Неможливо видалити учасника \"{{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": "Додати учасника з ім'ям \"{{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": "Відсутня активна/поточна група. Виберіть спочатку її ім'я", + + // "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": "У групі відсутні учасники. Проведіть пошук та додайте", + + // "admin.access-control.groups.form.members-list.no-items": "No EPeople found in that search", + "admin.access-control.groups.form.members-list.no-items": "Не знайдено жодного учасника групи", + + // "admin.access-control.groups.form.subgroups-list.notification.failure": "Something went wrong: \"{{cause}}\"", + + "admin.access-control.groups.form.subgroups-list.notification.failure": "Щось пішло не так: \"{{cause}}\"", + + // "admin.access-control.groups.form.subgroups-list.head": "Groups", + "admin.access-control.groups.form.subgroups-list.head": "Групи", + + // "admin.access-control.groups.form.subgroups-list.search.head": "Add Subgroup", + "admin.access-control.groups.form.subgroups-list.search.head": "Додати групу", + + // "admin.access-control.groups.form.subgroups-list.button.see-all": "Browse All", + "admin.access-control.groups.form.subgroups-list.button.see-all": "Переглянути всі", + + // "admin.access-control.groups.form.subgroups-list.headSubgroups": "Current Subgroups", + "admin.access-control.groups.form.subgroups-list.headSubgroups": "Поточні підгрупи", + + // "admin.access-control.groups.form.subgroups-list.search.button": "Search", + "admin.access-control.groups.form.subgroups-list.search.button": "Шукати", + + // "admin.access-control.groups.form.subgroups-list.table.id": "ID", + "admin.access-control.groups.form.subgroups-list.table.id": "ID", + + // "admin.access-control.groups.form.subgroups-list.table.name": "Name", + "admin.access-control.groups.form.subgroups-list.table.name": "Ім'я", + + // "admin.access-control.groups.form.subgroups-list.table.edit": "Remove / Add", + "admin.access-control.groups.form.subgroups-list.table.edit": "Видалити / Додати", + + // "admin.access-control.groups.form.subgroups-list.table.edit.buttons.remove": "Remove subgroup with name \"{{name}}\"", + "admin.access-control.groups.form.subgroups-list.table.edit.buttons.remove": "Видалити підгрупу \"{{name}}\"", + + // "admin.access-control.groups.form.subgroups-list.table.edit.buttons.add": "Add subgroup with name \"{{name}}\"", + "admin.access-control.groups.form.subgroups-list.table.edit.buttons.add": "Додати підгрупу \"{{name}}\"", + + // "admin.access-control.groups.form.subgroups-list.table.edit.currentGroup": "Current group", + "admin.access-control.groups.form.subgroups-list.table.edit.currentGroup": "Поточна група", + + // "admin.access-control.groups.form.subgroups-list.notification.success.addSubgroup": "Successfully added subgroup: \"{{name}}\"", + "admin.access-control.groups.form.subgroups-list.notification.success.addSubgroup": "Підгрупа \"{{name}}\" успішно додана", + + // "admin.access-control.groups.form.subgroups-list.notification.failure.addSubgroup": "Failed to add subgroup: \"{{name}}\"", + "admin.access-control.groups.form.subgroups-list.notification.failure.addSubgroup": "Неможливо додати підгрупу \"{{name}}\"", + + // "admin.access-control.groups.form.subgroups-list.notification.success.deleteSubgroup": "Successfully deleted subgroup: \"{{name}}\"", + "admin.access-control.groups.form.subgroups-list.notification.success.deleteSubgroup": "Підгрупа \"{{name}}\" успішно видалена", + + // "admin.access-control.groups.form.subgroups-list.notification.failure.deleteSubgroup": "Failed to delete subgroup: \"{{name}}\"", + "admin.access-control.groups.form.subgroups-list.notification.failure.deleteSubgroup": "Неможливо видалити підгрупу \"{{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": "Відсутня активна група. Спочатку вкажіть ім'я", + + // "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": "Це поточна група. Вона не може бути додана", + + // "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": "Група не знайдена за критерієм імені чи 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": "Жодної підгрупи у групі немає.", + + // "admin.access-control.groups.form.return": "Return to groups", + "admin.access-control.groups.form.return": "Повернутись до груп", + + + + // "admin.search.breadcrumbs": "Administrative Search", + "admin.search.breadcrumbs": "Пошук адміністратора", + + // "admin.search.collection.edit": "Edit", + "admin.search.collection.edit": "Редагувати", + + // "admin.search.community.edit": "Edit", + "admin.search.community.edit": "Редагувати", + + // "admin.search.item.delete": "Delete", + "admin.search.item.delete": "Видалити", + + // "admin.search.item.edit": "Edit", + "admin.search.item.edit": "Редагувати", + + // "admin.search.item.make-private": "Make Private", + "admin.search.item.make-private": "Зробити приватним", + + // "admin.search.item.make-public": "Make Public", + "admin.search.item.make-public": "Зробити публічним", + + // "admin.search.item.move": "Move", + "admin.search.item.move": "Перемістити", + + // "admin.search.item.reinstate": "Reinstate", + "admin.search.item.reinstate": "Відновити", + + // "admin.search.item.withdraw": "Withdraw", + "admin.search.item.withdraw": "Вилучити", + + // "admin.search.title": "Administrative Search", + "admin.search.title": "Пошук адміністратора", + + // "administrativeView.search.results.head": "Administrative Search", + "administrativeView.search.results.head": "Пошук адміністратора", + + + + + // "admin.workflow.breadcrumbs": "Administer Workflow", + "admin.workflow.breadcrumbs": "Адміністрування робочого процесу", + + // "admin.workflow.title": "Administer Workflow", + "admin.workflow.title": "Адміністрування робочого процесу", + + // "admin.workflow.item.workflow": "Workflow", + "admin.workflow.item.workflow": "Робочий процес", + + // "admin.workflow.item.delete": "Delete", + "admin.workflow.item.delete": "Видалити", + + // "admin.workflow.item.send-back": "Send back", + "admin.workflow.item.send-back": "Повернути назад", + + + + // "admin.metadata-import.breadcrumbs": "Import Metadata", + + "admin.metadata-import.breadcrumbs": "Імпортувати метадані", + + // "admin.metadata-import.title": "Import Metadata", + + "admin.metadata-import.title": "Імпортувати метадані", + + // "admin.metadata-import.page.header": "Import Metadata", + + "admin.metadata-import.page.header": "Імпортувати метадані", + + // "admin.metadata-import.page.help": "You can drop or browse CSV files that contain batch metadata operations on files here", + // TODO New key - Add a translation + "admin.metadata-import.page.help": "Тут можна скинути або переглянути файли CSV, які містять пакетні операції з метаданими", + + // "admin.metadata-import.page.dropMsg": "Drop a metadata CSV to import", + + "admin.metadata-import.page.dropMsg": "Скинути метадані CSV для імпорту", + + // "admin.metadata-import.page.dropMsgReplace": "Drop to replace the metadata CSV to import", + + "admin.metadata-import.page.dropMsgReplace": "Скинути для заміни метаданих CSV для імпорту", + + // "admin.metadata-import.page.button.return": "Return", + + "admin.metadata-import.page.button.return": "Повернутись", + + // "admin.metadata-import.page.button.proceed": "Proceed", + + "admin.metadata-import.page.button.proceed": "Продовжити", + + // "admin.metadata-import.page.error.addFile": "Select file first!", + + "admin.metadata-import.page.error.addFile": "Спочатку виберіть файл", + + + + + // "auth.errors.invalid-user": "Invalid email address or password.", + "auth.errors.invalid-user": "Неправильний емейл чи пароль.", + + // "auth.messages.expired": "Your session has expired. Please log in again.", + "auth.messages.expired": "Час сесії вичерпано. Увійдіть знову.", + + + + // "bitstream.edit.bitstream": "Bitstream: ", + "bitstream.edit.bitstream": "Файл: ", + + // "bitstream.edit.form.description.hint": "Optionally, provide a brief description of the file, for example \"Main article\" or \"Experiment data readings\".", + "bitstream.edit.form.description.hint": "За бажанням надайте, наприклад, короткий опис файлу, \"Galvanais raksts\" або \"експериментальні читання даних\".", + + // "bitstream.edit.form.description.label": "Description", + "bitstream.edit.form.description.label": "Опис", + + // "bitstream.edit.form.embargo.hint": "The first day from which access is allowed. This date cannot be modified on this form. To set an embargo date for a bitstream, go to the Item Status tab, click Authorizations..., create or edit the bitstream's READ policy, and set the Start Date as desired.", + "bitstream.edit.form.embargo.hint": "Перший день, з якого доступ дозволено. Ця дата може бути змінена у цій формі. Для ембарго на цей файл перейдіть у вкладку Статус документа(item), та клацніть Авторизація..., створіть чи відредагуйте політику доступу Читання (READ) та задайте Дата старту.", + + // "bitstream.edit.form.embargo.label": "Embargo until specific date", + "bitstream.edit.form.embargo.label": "Ембарго до дати", + + // "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": "Змініть назву файла. Зверніть увагу, що це вплине на посилання. Проте мали б прцювати і старе посилання доки не зміниться ID документу.", + + // "bitstream.edit.form.fileName.label": "Filename", + "bitstream.edit.form.fileName.label": "Ім'я файлу", + + // "bitstream.edit.form.newFormat.label": "Describe new format", + "bitstream.edit.form.newFormat.label": "Опишіть новий формат", + + // "bitstream.edit.form.newFormat.hint": "The application you used to create the file, and the version number (for example, \"ACMESoft SuperApp version 1.5\").", + "bitstream.edit.form.newFormat.hint": "Програмне забезпечення та його версія, яке Ви використали при створенні файлу (наприклад, \" ACMESoft SuperApp versija 1.5 \").", + + // "bitstream.edit.form.primaryBitstream.label": "Primary bitstream", + "bitstream.edit.form.primaryBitstream.label": "Головний файл", + + // "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": "Якщо формату немає у переліку, будь ласка опишіть його.", + + // "bitstream.edit.form.selectedFormat.label": "Selected Format", + "bitstream.edit.form.selectedFormat.label": "Вибрані формати", + + // "bitstream.edit.form.selectedFormat.unknown": "Format not in list", + "bitstream.edit.form.selectedFormat.unknown": "Формату немає у переліку", + + // "bitstream.edit.notifications.error.format.title": "An error occurred saving the bitstream's format", + "bitstream.edit.notifications.error.format.title": "Сталась помилка при збереженні формату файла.", + + // "bitstream.edit.notifications.saved.content": "Your changes to this bitstream were saved.", + "bitstream.edit.notifications.saved.content": "Зміни до цього файлу були збережені.", + + // "bitstream.edit.notifications.saved.title": "Bitstream saved", + "bitstream.edit.notifications.saved.title": "Файл збережено", + + // "bitstream.edit.title": "Edit bitstream", + "bitstream.edit.title": "Редагувати файл", + + + + // "browse.comcol.by.author": "By Author", + "browse.comcol.by.author": "За автором", + + // "browse.comcol.by.dateissued": "За датою", + "browse.comcol.by.dateissued": "За датою", + + // "browse.comcol.by.subject": "By Subject", + "browse.comcol.by.subject": "За темою", + + // "browse.comcol.by.title": "By Title", + "browse.comcol.by.title": "За назвою", + + // "browse.comcol.head": "Browse", + "browse.comcol.head": "Переглянути", + + // "browse.empty": "No items to show.", + "browse.empty": "Немає документів.", + + // "browse.metadata.author": "Author", + "browse.metadata.author": "Автор", + + // "browse.metadata.dateissued": "Issue Date", + "browse.metadata.dateissued": "Дата публікації", + + // "browse.metadata.subject": "Subject", + "browse.metadata.subject": "Ключові слова", + + // "browse.metadata.title": "Title", + "browse.metadata.title": "Назва", + + // "browse.metadata.author.breadcrumbs": "Browse by Author", + "browse.metadata.author.breadcrumbs": "Переглянути за автором", + + // "browse.metadata.dateissued.breadcrumbs": "Browse by Date", + "browse.metadata.dateissued.breadcrumbs": "Переглянути за датою", + + // "browse.metadata.subject.breadcrumbs": "Browse by Subject", + "browse.metadata.subject.breadcrumbs": "Переглянути за ключовими словами", + + // "browse.metadata.title.breadcrumbs": "Browse by Title", + "browse.metadata.title.breadcrumbs": "Переглянути за назвою", + + // "browse.startsWith.choose_start": "(Choose start)", + "browse.startsWith.choose_start": "(виберіть початок)", + + // "browse.startsWith.choose_year": "(Choose year)", + "browse.startsWith.choose_year": "(виберіть рік)", + + // "browse.startsWith.jump": "Jump to a point in the index:", + "browse.startsWith.jump": "Перейти на індекс:", + + // "browse.startsWith.months.april": "April", + "browse.startsWith.months.april": "Квітень", + + // "browse.startsWith.months.august": "August", + "browse.startsWith.months.august": "Серпень", + + // "browse.startsWith.months.december": "December", + "browse.startsWith.months.december": "Грудень", + + // "browse.startsWith.months.february": "February", + "browse.startsWith.months.february": "Лютий", + + // "browse.startsWith.months.january": "January", + "browse.startsWith.months.january": "Січень", + + // "browse.startsWith.months.july": "July", + "browse.startsWith.months.july": "Липень", + + // "browse.startsWith.months.june": "June", + "browse.startsWith.months.june": "Червень", + + // "browse.startsWith.months.march": "March", + "browse.startsWith.months.march": "Березень", + + // "browse.startsWith.months.may": "May", + "browse.startsWith.months.may": "Травень", + + // "browse.startsWith.months.none": "(Choose month)", + "browse.startsWith.months.none": "(Виберіть місяць)", + + // "browse.startsWith.months.november": "November", + "browse.startsWith.months.november": "Листопад", + + // "browse.startsWith.months.october": "October", + "browse.startsWith.months.october": "Жовтень", + + // "browse.startsWith.months.september": "September", + "browse.startsWith.months.september": "Вересень", + + // "browse.startsWith.submit": "Go", + "browse.startsWith.submit": "Перейти", + + // "browse.startsWith.type_date": "Or type in a date (year-month):", + "browse.startsWith.type_date": "Або введіть дату (рік-місяць):", + + // "browse.startsWith.type_text": "Or enter first few letters:", + "browse.startsWith.type_text": "Або введіть перші символи:", + + // "browse.title": "Browsing {{ collection }} by {{ field }} {{ value }}", + "browse.title": "Перегляд {{ collection }} за {{ field }} {{ value }}", + + + // "chips.remove": "Remove chip", + // TODO "chips.remove": "Remove chip", + "chips.remove": "Remove chip", + + + + // "collection.create.head": "Create a Collection", + "collection.create.head": "Створити зібрання", + + // "collection.create.notifications.success": "Successfully created the Collection", + "collection.create.notifications.success": "Зібрання успішно створено", + + // "collection.create.sub-head": "Create a Collection for Community {{ parent }}", + "collection.create.sub-head": "Створити зібрання у колекції {{ parent }}", + + // "collection.curate.header": "Curate Collection: {{collection}}", + // TODO New key - Add a translation + "collection.curate.header": "Curate Collection: {{collection}}", + + // "collection.delete.cancel": "Cancel", + "collection.delete.cancel": "Відмінити", + + // "collection.delete.confirm": "Confirm", + "collection.delete.confirm": "Підтвердити", + + // "collection.delete.head": "Delete Collection", + "collection.delete.head": "Видалити зібрання", + + // "collection.delete.notification.fail": "Collection could not be deleted", + "collection.delete.notification.fail": "Зібрання не може бути видалене", + + // "collection.delete.notification.success": "Successfully deleted collection", + "collection.delete.notification.success": "Зібрання успішно видалено", + + // "collection.delete.text": "Are you sure you want to delete collection \"{{ dso }}\"", + "collection.delete.text": "Ви впевнені, що хочете видалити зібрання \"{{ dso }}\"?", + + + + // "collection.edit.delete": "Delete this collection", + "collection.edit.delete": "Видалити це зібрання", + + // "collection.edit.head": "Edit Collection", + "collection.edit.head": "Редагувати зібрання", + + // "collection.edit.breadcrumbs": "Edit Collection", + "collection.edit.breadcrumbs": "Редагувати зібрання", + + + + // "collection.edit.tabs.mapper.head": "Item Mapper", + + "collection.edit.tabs.mapper.head": "Карта документа", + + // "collection.edit.tabs.item-mapper.title": "Collection Edit - Item Mapper", + + "collection.edit.tabs.item-mapper.title": "Редагування зібрання - Карта документа", + + // "collection.edit.item-mapper.cancel": "Cancel", + "collection.edit.item-mapper.cancel": "Відмінити", + + // "collection.edit.item-mapper.collection": "Collection: \"{{name}}\"", + "collection.edit.item-mapper.collection": "Зібрання: \"{{name}}\"", + + // "collection.edit.item-mapper.confirm": "Map selected items", + "collection.edit.item-mapper.confirm": "Карта вибраних документів", + + // "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": "Це інструмент відображення елементів документа, який дозволяє адміністраторам колекції відображати елементи з інших колекцій у цю колекцію. Ви можете шукати елементи з інших колекцій і відображати їх, або переглядати список відображених елементів.", + + // "collection.edit.item-mapper.head": " - Map Items from Other Collections", + "collection.edit.item-mapper.head": "Карта документа - карта документів з іншого зібрання", + + // "collection.edit.item-mapper.no-search": "Please enter a query to search", + "collection.edit.item-mapper.no-search": "Введіть запит для пошуку", + + // "collection.edit.item-mapper.notifications.map.error.content": "Errors occurred for mapping of {{amount}} items.", + "collection.edit.item-mapper.notifications.map.error.content": "Виникла помилка {{amount}} при відображені елементів документа.", + + // "collection.edit.item-mapper.notifications.map.error.head": "Mapping errors", + "collection.edit.item-mapper.notifications.map.error.head": "Помилка відображення елементів документа", + + // "collection.edit.item-mapper.notifications.map.success.content": "Successfully mapped {{amount}} items.", + "collection.edit.item-mapper.notifications.map.success.content": "Документ {{amount}} успішно mapped.", + + // "collection.edit.item-mapper.notifications.map.success.head": "Mapping completed", + // TODO "collection.edit.item-mapper.notifications.map.success.head": "Mapping completed" + "collection.edit.item-mapper.notifications.map.success.head": "Mapping completed", + + // "collection.edit.item-mapper.notifications.unmap.error.content": "Errors occurred for removing the mappings of {{amount}} items.", + "collection.edit.item-mapper.notifications.unmap.error.content": "Виникла помилка mappings документа {{amount}}.", + + // "collection.edit.item-mapper.notifications.unmap.error.head": "Remove mapping errors", + "collection.edit.item-mapper.notifications.unmap.error.head": "Видалити помилки mapping", + + // "collection.edit.item-mapper.notifications.unmap.success.content": "Successfully removed the mappings of {{amount}} items.", + "collection.edit.item-mapper.notifications.unmap.success.content": "Успішно видалено mappings документа {{amount}}.", + + // "collection.edit.item-mapper.notifications.unmap.success.head": "Remove mapping completed", + // TODO "collection.edit.item-mapper.notifications.unmap.success.head": "Remove mapping completed", + "collection.edit.item-mapper.notifications.unmap.success.head": "Remove mapping completed", + + // "collection.edit.item-mapper.remove": "Remove selected item mappings", + "collection.edit.item-mapper.remove": "Видалити mapping документа", + + // "collection.edit.item-mapper.tabs.browse": "Browse mapped items", + "collection.edit.item-mapper.tabs.browse": "Переглянути елементи документа", + + // "collection.edit.item-mapper.tabs.map": "Map new items", + "collection.edit.item-mapper.tabs.map": "mapping нових документів", + + + + // "collection.edit.logo.label": "Collection logo", + "collection.edit.logo.label": "Логотип зібрання", + + // "collection.edit.logo.notifications.add.error": "Uploading Collection logo failed. Please verify the content before retrying.", + "collection.edit.logo.notifications.add.error": "Завантаження логотипу зібрання не вдалось.", + + // "collection.edit.logo.notifications.add.success": "Upload Collection logo successful.", + "collection.edit.logo.notifications.add.success": "Логотип зібрання успішно завантажено.", + + // "collection.edit.logo.notifications.delete.success.title": "Logo deleted", + "collection.edit.logo.notifications.delete.success.title": "Логотип видалено", + + // "collection.edit.logo.notifications.delete.success.content": "Successfully deleted the collection's logo", + "collection.edit.logo.notifications.delete.success.content": "Логотип зібрання успішно видалено.", + + // "collection.edit.logo.notifications.delete.error.title": "Error deleting logo", + "collection.edit.logo.notifications.delete.error.title": "Виникла помилка при видаленні логотипа", + + // "collection.edit.logo.upload": "Drop a Collection Logo to upload", + "collection.edit.logo.upload": "Виберіть логотип зібрання для завантаження", + + + + // "collection.edit.notifications.success": "Successfully edited the Collection", + "collection.edit.notifications.success": "Зібрання успішно відредаговано", + + // "collection.edit.return": "Return", + "collection.edit.return": "Повернутись", + + + + // "collection.edit.tabs.curate.head": "Curate", + // TODO "collection.edit.tabs.curate.head": "Curate", + "collection.edit.tabs.curate.head": "Curate", + + // "collection.edit.tabs.curate.title": "Collection Edit - Curate", + // TODO "collection.edit.tabs.curate.title": "Collection Edit - Curate", + "collection.edit.tabs.curate.title": "Collection Edit - Curate", + + // "collection.edit.tabs.authorizations.head": "Authorizations", + + "collection.edit.tabs.authorizations.head": "Авторизація", + + // "collection.edit.tabs.authorizations.title": "Collection Edit - Authorizations", + + "collection.edit.tabs.authorizations.title": "Редагування зібрання - авторизація ", + + // "collection.edit.tabs.metadata.head": "Edit Metadata", + "collection.edit.tabs.metadata.head": "Редагувати метадані", + + // "collection.edit.tabs.metadata.title": "Collection Edit - Metadata", + "collection.edit.tabs.metadata.title": "Редагування зібрання - метадані", + + // "collection.edit.tabs.roles.head": "Assign Roles", + "collection.edit.tabs.roles.head": "Призначити ролі", + + // "collection.edit.tabs.roles.title": "Collection Edit - Roles", + "collection.edit.tabs.roles.title": "Редагування зібрання - ролі", + + // "collection.edit.tabs.source.external": "This collection harvests its content from an external source", + "collection.edit.tabs.source.external": "Вміст зібрання взято із зовнішнього джерела інформації", + + // "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": "Ви повинні надати ідентифікатор цільового зібрання", + + // "collection.edit.tabs.source.form.harvestType": "Content being harvested", + "collection.edit.tabs.source.form.harvestType": "Вміст отримано", + + // "collection.edit.tabs.source.form.head": "Configure an external source", + "collection.edit.tabs.source.form.head": "Налаштувати зовнішні джерела інформації", + + // "collection.edit.tabs.source.form.metadataConfigId": "Metadata Format", + "collection.edit.tabs.source.form.metadataConfigId": "Формат метаданих", + + // "collection.edit.tabs.source.form.oaiSetId": "OAI specific set id", + // TODO "collection.edit.tabs.source.form.oaiSetId": "OAI specific set id" + "collection.edit.tabs.source.form.oaiSetId": "OAI specific set id", + + // "collection.edit.tabs.source.form.oaiSource": "OAI Provider", + "collection.edit.tabs.source.form.oaiSource": "OAI Провайдер", + + // "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": "Завантажити метадані та файли (потрібна 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": "Завантажити метадані та посилання на файли (потрібна ORE підтримка)", + + // "collection.edit.tabs.source.form.options.harvestType.METADATA_ONLY": "Harvest metadata only", + "collection.edit.tabs.source.form.options.harvestType.METADATA_ONLY": "Завантажити тільки метадані", + + // "collection.edit.tabs.source.head": "Content Source", + "collection.edit.tabs.source.head": "Джерело вмісту", + + // "collection.edit.tabs.source.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", + "collection.edit.tabs.source.notifications.discarded.content": "Ваші зміни скасовано. Щоб відновити зміни, натисніть кнопку -Скасувати-", + + // "collection.edit.tabs.source.notifications.discarded.title": "Changed discarded", + "collection.edit.tabs.source.notifications.discarded.title": "Ваші зміни скасовано", + + // "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": "Ваші зміни не збережено. Перед збереженням переконайтеся, що всі поля правильно заповнені.", + + // "collection.edit.tabs.source.notifications.invalid.title": "Metadata invalid", + "collection.edit.tabs.source.notifications.invalid.title": "Помилки у метаданих", + + // "collection.edit.tabs.source.notifications.saved.content": "Your changes to this collection's content source were saved.", + "collection.edit.tabs.source.notifications.saved.content": "Ваші зміни до вмісту зібрання збережено.", + + // "collection.edit.tabs.source.notifications.saved.title": "Content Source saved", + "collection.edit.tabs.source.notifications.saved.title": "Вміст джерела даних збереено", + + // "collection.edit.tabs.source.title": " - Content Source", + "collection.edit.tabs.source.title": "Редагування зібрання - джерела даних", + + + + // "collection.edit.template.add-button": "Add", + + "collection.edit.template.add-button": "Додати", + + // "collection.edit.template.breadcrumbs": "Item template", + + "collection.edit.template.breadcrumbs": "Шаблон документа", + + // "collection.edit.template.cancel": "Cancel", + + "collection.edit.template.cancel": "Відмінити", + + // "collection.edit.template.delete-button": "Delete", + + "collection.edit.template.delete-button": "Видалити", + + // "collection.edit.template.edit-button": "Edit", + + "collection.edit.template.edit-button": "Редагувати", + + // "collection.edit.template.head": "Edit Template Item for Collection \"{{ collection }}\"", + + "collection.edit.template.head": "Редагувати шаблон документа зібрання \"{{ collection }}\"", + + // "collection.edit.template.label": "Template item", + + "collection.edit.template.label": "Шаблон документа", + + // "collection.edit.template.notifications.delete.error": "Failed to delete the item template", + + "collection.edit.template.notifications.delete.error": "Не вдалося видалити шаблон елемента", + + // "collection.edit.template.notifications.delete.success": "Шаблон документа успішно видалено", + + "collection.edit.template.notifications.delete.success": "Шаблон документа успішно видалено", + + // "collection.edit.template.title": "Edit Template Item", + + "collection.edit.template.title": "Редагувати шаблон документа", + + + + // "collection.form.abstract": "Short Description", + "collection.form.abstract": "Короткий опис", + + // "collection.form.description": "Introductory text (HTML)", + "collection.form.description": "Вступний текст (HTML)", + + // "collection.form.errors.title.required": "Please enter a collection name", + "collection.form.errors.title.required": "Введіть назву зібрання", + + // "collection.form.license": "License", + "collection.form.license": "Ліцензійна угода", + + // "collection.form.provenance": "Provenance", + "collection.form.provenance": "Походження", + + // "collection.form.rights": "Copyright text (HTML)", + "collection.form.rights": "Копірайт (HTML)", + + // "collection.form.tableofcontents": "News (HTML)", + "collection.form.tableofcontents": "Новини (HTML)", + + // "collection.form.title": "Name", + "collection.form.title": "Ім'я", + + + + // "collection.listelement.badge": "Collection", + + "collection.listelement.badge": "Зібрання", + + +//"home.recent-submissions.head": "Recent Submissions", +"home.recent-submissions.head": "Нові надходження", + + +//"thumbnail.default.alt": "Thumbnail Image", +"thumbnail.default.alt": "Ескіз", + +// "thumbnail.default.placeholder": "No Thumbnail Available", +"thumbnail.default.placeholder": "Ескіз недоступний", + +// "thumbnail.project.alt": "Project Logo", + "thumbnail.project.alt": "Логотип проекту", + +// TODO "thumbnail.project.placeholder": "Project Placeholder Image", + "thumbnail.project.placeholder": "Project Placeholder Image", + +// "thumbnail.orgunit.alt": "OrgUnit Logo", +"thumbnail.orgunit.alt": "Логотип організації", + + "thumbnail.orgunit.placeholder": "OrgUnit Placeholder Image", + +// "thumbnail.person.alt": "Profile Picture", +"thumbnail.person.alt": "Зображення профілю", + + // "thumbnail.person.placeholder": "No Profile Picture Available", +"thumbnail.person.placeholder": "Зображення профілю відсутне", + + + + // "collection.page.browse.recent.head": "Recent Submissions", + "collection.page.browse.recent.head": "Нові надходження", + + // "collection.page.browse.recent.empty": "No items to show", + "collection.page.browse.recent.empty": "Немає документів", + + // "collection.page.edit": "Edit this collection", + + "collection.page.edit": "Редагувати зібрання", + + // "collection.page.handle": "Permanent URI for this collection", + "collection.page.handle": "Постійне посилання зібрання", + + // "collection.page.license": "License", + "collection.page.license": "Ліцензійна угода", + + // "collection.page.news": "News", + "collection.page.news": "Новини", + + + + // "collection.select.confirm": "Confirm selected", + "collection.select.confirm": "Підтвердити вибрані", + + // "collection.select.empty": "No collections to show", + "collection.select.empty": "Зібрання відсутні", + + // "collection.select.table.title": "Title", + "collection.select.table.title": "Назва", + + + + // "collection.source.update.notifications.error.content": "The provided settings have been tested and didn't work.", + "collection.source.update.notifications.error.content": "Введені налаштування перевірені та не працюють.", + + // "collection.source.update.notifications.error.title": "Server Error", + "collection.source.update.notifications.error.title": "Помилка роботи сервера, трясця його матері", + + + + // "communityList.tabTitle": "DSpace - Community List", + "communityList.tabTitle": "Репозитарій - Фонди", + + // "communityList.title": "List of Communities", + "communityList.title": "Перелік фондів", + + // "communityList.showMore": "Show More", + "communityList.showMore": "Детальніше", + + + + // "community.create.head": "Create a Community", + "community.create.head": "Створити фонд", + + // "community.create.notifications.success": "Successfully created the Community", + "community.create.notifications.success": "Фонд успішно створено", + + // "community.create.sub-head": "Create a Sub-Community for Community {{ parent }}", + "community.create.sub-head": "Створити підфонд фонду {{ parent }}", + + // "community.curate.header": "Curate Community: {{community}}", + + "community.curate.header": "Основний фонд: {{community}}", + + // "community.delete.cancel": "Cancel", + "community.delete.cancel": "Відмінити", + + // "community.delete.confirm": "Confirm", + "community.delete.confirm": "Підтвердити", + + // "community.delete.head": "Delete Community", + "community.delete.head": "Видалити фонд", + + // "community.delete.notification.fail": "Community could not be deleted", + "community.delete.notification.fail": "Фонд не може бути видалено", + + // "community.delete.notification.success": "Successfully deleted community", + "community.delete.notification.success": "Фонд успішно видалено", + + // "community.delete.text": "Are you sure you want to delete community \"{{ dso }}\"", + "community.delete.text": "Ви впевнені, що хочете видалити фонд \"{{ dso }}\"", + + // "community.edit.delete": "Delete this community", + "community.edit.delete": "Видалити цей фонд", + + // "community.edit.head": "Edit Community", + "community.edit.head": "Редагувати фонд", + + // "community.edit.breadcrumbs": "Edit Community", + "community.edit.breadcrumbs": "Редагувати фонд", + + + // "community.edit.logo.label": "Community logo", + "community.edit.logo.label": "Логотип фонду", + + // "community.edit.logo.notifications.add.error": "Uploading Community logo failed. Please verify the content before retrying.", + "community.edit.logo.notifications.add.error": "Помилка завантаження логотипу фонду. Перевірте все ще раз.", + + // "community.edit.logo.notifications.add.success": "Upload Community logo successful.", + "community.edit.logo.notifications.add.success": "Успішне завантаження логотипу фонду", + + // "community.edit.logo.notifications.delete.success.title": "Logo deleted", + "community.edit.logo.notifications.delete.success.title": "Логотип видалено", + + // "community.edit.logo.notifications.delete.success.content": "Successfully deleted the community's logo", + "community.edit.logo.notifications.delete.success.content": "Логотип фонду успішно видалено", + + // "community.edit.logo.notifications.delete.error.title": "Error deleting logo", + "community.edit.logo.notifications.delete.error.title": "Помилка видалення логотипу", + + // "community.edit.logo.upload": "Drop a Community Logo to upload", + "community.edit.logo.upload": "Виберіть логотип фонду для завантаження", + + + + // "community.edit.notifications.success": "Successfully edited the Community", + "community.edit.notifications.success": "Фонд успішно відредаговано", + + // "community.edit.notifications.unauthorized": "You do not have privileges to make this change", + + "community.edit.notifications.unauthorized": "У Вас немає повноважень для змін", + + // "community.edit.notifications.error": "An error occured while editing the Community", + + "community.edit.notifications.error": "Виникла помилка при редагуванні фонду", + + // "community.edit.return": "Return", + "community.edit.return": "Повернутись", + + + + // "community.edit.tabs.curate.head": "Curate", + "community.edit.tabs.curate.head": "Основне/Curate", + + // "community.edit.tabs.curate.title": "Community Edit - Curate", + "community.edit.tabs.curate.title": "Редагування фонду - основне", + + // "community.edit.tabs.metadata.head": "Edit Metadata", + "community.edit.tabs.metadata.head": "Реадгувати метадані", + + // "community.edit.tabs.metadata.title": "Community Edit - Metadata", + "community.edit.tabs.metadata.title": "Редагування фонду - метадані", + + // "community.edit.tabs.roles.head": "Assign Roles", + "community.edit.tabs.roles.head": "Призначити ролі", + + // "community.edit.tabs.roles.title": "Community Edit - Roles", + "community.edit.tabs.roles.title": "Редаувати фонд - ролі", + + // "community.edit.tabs.authorizations.head": "Authorizations", + + "community.edit.tabs.authorizations.head": "Авторизація", + + // "community.edit.tabs.authorizations.title": "Community Edit - Authorizations", + + "community.edit.tabs.authorizations.title": "Редагування фонду - авторизація", + + + + // "community.listelement.badge": "Community", + + "community.listelement.badge": "Фонд", + + + + // "comcol-role.edit.no-group": "None", + + "comcol-role.edit.no-group": "Жодної", + + // "comcol-role.edit.create": "Create", + + "comcol-role.edit.create": "Створити", + + // "comcol-role.edit.restrict": "Restrict", + + "comcol-role.edit.restrict": "Обмежити", + + // "comcol-role.edit.delete": "Delete", + + "comcol-role.edit.delete": "Видалити", + + + // "comcol-role.edit.community-admin.name": "Administrators", + + "comcol-role.edit.community-admin.name": "Адміністратори", + + // "comcol-role.edit.collection-admin.name": "Administrators", + + "comcol-role.edit.collection-admin.name": "Адміністратори", + + + // "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).", + // TODO New key - Add a translation + "comcol-role.edit.community-admin.description": "Адміністратори фонду можуть створювати підфонди або зібрання, а також керувати цими. Крім того, вони вирішують, хто може надсилати документи до будь-якого зібрання, редагувати метадані документа (після надсилання) і додавати (відображати) існуючі елементи з інших зібрань(за умови наданих повноважень).", + + // "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": "Адміністратори зібрання вирішують, хто може надсилати документи до зібрання, редагувати метадані документів (після надсилання) і додавати документи з інших зібрань (за умови наданих повноважень).", + + + // "comcol-role.edit.submitters.name": "Submitters", + + "comcol-role.edit.submitters.name": "Подавачі/Submitters", + + // "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": "Користувачі та групи у яких є повноваження вносити документи до цього зібрання.", + + + // "comcol-role.edit.item_read.name": "Default item read access", + + "comcol-role.edit.item_read.name": "Доступ на читання документа", + + // "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": "Користувачі та групи, які можуть читати нові документи, що надіслані до цього зібрання. Зміни цієї ролі не мають зворотної сили. Існуючі документи в системі все ще будуть доступні для перегляду тим, хто мав доступ для читання на момент їх додавання.", + + // "comcol-role.edit.item_read.anonymous-group": "Default read for incoming items is currently set to Anonymous.", + + "comcol-role.edit.item_read.anonymous-group": "Для групи Anonymous встановлено права документа на читання.", + + + // "comcol-role.edit.bitstream_read.name": "Доступ на читання для файла", + + "comcol-role.edit.bitstream_read.name": "Доступ на читання для файла", + + // "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": "Адміністратори фонду можуть створювати підфонди або зібрання, а також керувати ними. Крім того, вони вирішують, хто може надсилати документи до будь-якого зібрання, редагувати метадані документа (після надсилання) і додавати документи з інших колекцій (за умови наявності прав доступу)..", + + // "comcol-role.edit.bitstream_read.anonymous-group": "Default read for incoming bitstreams is currently set to Anonymous.", + + "comcol-role.edit.bitstream_read.anonymous-group": "Для групи Anonymous встановлено права файла на читання", + + + // "comcol-role.edit.editor.name": "Редактори", + + "comcol-role.edit.editor.name": "Редактори", + + // "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": "Редактори мають повноваження редагувати метадані, приймати та відхиляти їх, зокрема, для документів, що заносяться у репозитарій.", + + + // "comcol-role.edit.finaleditor.name": "Final editors", + + "comcol-role.edit.finaleditor.name": "Остаточні редактори", + + // "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": "Остаточні редактори мають повноваження редагувати метадані, але не мають права відхилити документ.", + + + // "comcol-role.edit.reviewer.name": "Reviewers", + + "comcol-role.edit.reviewer.name": "Рецензенти", + + // "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": "Рецензенти можуть приймати або відхиляти вхідні документи. Однак вони не можуть редагувати метадані.", + + + + // "community.form.abstract": "Short Description", + "community.form.abstract": "Короткий опис", + + // "community.form.description": "Introductory text (HTML)", + "community.form.description": "Вхідний текст (HTML)", + + // "community.form.errors.title.required": "Please enter a community name", + "community.form.errors.title.required": "Будь ласка, введіть назву фонду", + + // "community.form.rights": "Copyright text (HTML)", + "community.form.rights": "Копірайт (HTML)", + + // "community.form.tableofcontents": "News (HTML)", + "community.form.tableofcontents": "Новини (HTML)", + + // "community.form.title": "Name", + "community.form.title": "Ім'я", + + // "community.page.edit": "Edit this community", + + "community.page.edit": "Редагувати фонд", + + // "community.page.handle": "Permanent URI for this community", + "community.page.handle": "Постійне посилання на фонд", + + // "community.page.license": "License", + "community.page.license": "Ліцензійна угода", + + // "community.page.news": "News", + "community.page.news": "Новини", + + // "community.all-lists.head": "Subcommunities and Collections", + "community.all-lists.head": "Підфонди та зібрання", + + // "community.sub-collection-list.head": "Collections of this Community", + "community.sub-collection-list.head": "Зібрання у цьому фонді", + + // "community.sub-community-list.head": "Communities of this Community", + "community.sub-community-list.head": "Фонди, що знаходяться у цьому фонді", + + + // "cookies.consent.accept-all": "Accept all", + "cookies.consent.accept-all": "Прийняти все", + + // "cookies.consent.accept-selected": "Accept selected", + "cookies.consent.accept-selected": "Прийняти вибране", + + // "cookies.consent.app.opt-out.description": "This app is loaded by default (but you can opt out)", + "cookies.consent.app.opt-out.description": "Цей додаток був завантажений по замовчуванню. Але Ви можете відмовитись від нього", + + // "cookies.consent.app.opt-out.title": "(opt-out)", + "cookies.consent.app.opt-out.title": "(відмовитись)", + + // "cookies.consent.app.purpose": "purpose", + "cookies.consent.app.purpose": "ціль", + + // "cookies.consent.app.required.description": "This application is always required", + "cookies.consent.app.required.description": "Цей додаток завжди потрібний", + + // "cookies.consent.app.required.title": "(always required)", + "cookies.consent.app.required.title": "(завжди потрібний)", + + // "cookies.consent.update": "There were changes since your last visit, please update your consent.", + "cookies.consent.update": "З часу вашого останнього відвідування відбулися зміни, підтвердьте це.", + + // "cookies.consent.close": "Close", + "cookies.consent.close": "Закрити", + + // "cookies.consent.decline": "Decline", + "cookies.consent.decline": "Відхилити", + + // "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": "Ми збираємо та обробляємо вашу особисту інформацію для таких цілей: автентифікація, налаштування та статистика.
Щоб дізнатися більше, прочитайте нашу політику {privacyPolicy}.", + + // "cookies.consent.content-notice.learnMore": "Customize", + "cookies.consent.content-notice.learnMore": "Налаштувати", + + // "cookies.consent.content-modal.description": "Here you can see and customize the information that we collect about you.", + "cookies.consent.content-modal.description": "Тут ви можете переглянути та налаштувати збір інформаціїпро Вас.", + + // "cookies.consent.content-modal.privacy-policy.name": "privacy policy", + "cookies.consent.content-modal.privacy-policy.name": "політика приватності", + + // "cookies.consent.content-modal.privacy-policy.text": " {privacyPolicy}.", + "cookies.consent.content-modal.privacy-policy.text": "Детальніше - {privacyPolicy}.", + + // "cookies.consent.content-modal.title": "Information that we collect", + "cookies.consent.content-modal.title": "Інформація, яку ми збираємо", + + // "cookies.consent.app.title.authentication": "Authentication", + "cookies.consent.app.title.authentication": "Увійти", + + // "cookies.consent.app.description.authentication": "Required for signing you in", + "cookies.consent.app.description.authentication": "Необхідно для входу", + + // "cookies.consent.app.title.preferences": "Preferences", + "cookies.consent.app.title.preferences": "Налаштування", + + // "cookies.consent.app.description.preferences": "Required for saving your preferences", + "cookies.consent.app.description.preferences": "Необхідно для збереження ваших налаштувань", + + // "cookies.consent.app.title.acknowledgement": "Acknowledgement", + "cookies.consent.app.title.acknowledgement": "Підтвердження", + + // "cookies.consent.app.description.acknowledgement": "Required for saving your acknowledgements and consents", + "cookies.consent.app.description.acknowledgement": "Необхідний для збереження Ваших підтверджень і згод", + + // "cookies.consent.app.title.google-analytics": "Google Analytics", + "cookies.consent.app.title.google-analytics": "Google Analytics", + + // "cookies.consent.app.description.google-analytics": "Allows us to track statistical data", + "cookies.consent.app.description.google-analytics": "Дозволяє нам відстежувати статистичні дані", + + // "cookies.consent.purpose.functional": "Functional", + "cookies.consent.purpose.functional": "Функціональний", + + // "cookies.consent.purpose.statistical": "Statistical", + "cookies.consent.purpose.statistical": "Статичний", + + + // "curation-task.task.checklinks.label": "Check Links in Metadata", + "curation-task.task.checklinks.label": "Перевірте посилання у метаданих", + + // "curation-task.task.noop.label": "NOOP", + // TODO New key - Add a translation + "curation-task.task.noop.label": "NOOP", + + // "curation-task.task.profileformats.label": "Profile Bitstream Formats", + "curation-task.task.profileformats.label": "Профіль форматів файлів", + + // "curation-task.task.requiredmetadata.label": "Check for Required Metadata", + "curation-task.task.requiredmetadata.label": "Перевірте наявність необхідних метаданих", + + // "curation-task.task.translate.label": "Microsoft Translator", + "curation-task.task.translate.label": "Microsoft Translator", + + // "curation-task.task.vscan.label": "Virus Scan", + "curation-task.task.vscan.label": "Перевірка на віруси", + + + + // "curation.form.task-select.label": "Task:", + "curation.form.task-select.label": "Завдання:", + + // "curation.form.submit": "Start", + "curation.form.submit": "Почати", + + // "curation.form.submit.success.head": "The curation task has been started successfully", + "curation.form.submit.success.head": "Завдання успішно розпочате", + + // "curation.form.submit.success.content": "You will be redirected to the corresponding process page.", + "curation.form.submit.success.content": "Ви будете перенаправлені на сторінку відповідного процесу.", + + // "curation.form.submit.error.head": "Running the curation task failed", + "curation.form.submit.error.head": "Не вдалося виконати завдання", + + // "curation.form.submit.error.content": "An error occured when trying to start the curation task.", + "curation.form.submit.error.content": "Виникла помилка при спробі розпочати завдання.", + + // "curation.form.handle.label": "Handle:", + + "curation.form.handle.label": "Handle:", + + // "curation.form.handle.hint": "Hint: Enter [your-handle-prefix]/0 to run a task across entire site (not all tasks may support this capability)", + "curation.form.handle.hint": "Підказка: Введіть [your-handle-prefix]/0 для запуску завдання на весь сайт (не всі види завдань це підтримують)", + + + + // "dso-selector.create.collection.head": "New collection", + "dso-selector.create.collection.head": "Нове зібрання", + + // "dso-selector.create.collection.sub-level": "Create a new collection in", + "dso-selector.create.collection.sub-level": "Створити нове зібрання в", + + // "dso-selector.create.community.head": "New community", + "dso-selector.create.community.head": "Новий фонд", + + // "dso-selector.create.community.sub-level": "Create a new community in", + "dso-selector.create.community.sub-level": "Створити новий фонд в", + + // "dso-selector.create.community.top-level": "Create a new top-level community", + "dso-selector.create.community.top-level": "Створити фонд верхнього рівня", + + // "dso-selector.create.item.head": "New item", + "dso-selector.create.item.head": "Новий документ", + + // "dso-selector.create.item.sub-level": "Create a new item in", + "dso-selector.create.item.sub-level": "Створити новий документ в", + + // "dso-selector.create.submission.head": "New submission", + // TODO New key - Add a translation + "dso-selector.create.submission.head": "New submission", + + // "dso-selector.edit.collection.head": "Edit collection", + "dso-selector.edit.collection.head": "Редагувати зібрання", + + // "dso-selector.edit.community.head": "Edit community", + "dso-selector.edit.community.head": "Редагувати фонд", + + // "dso-selector.edit.item.head": "Edit item", + "dso-selector.edit.item.head": "Редагувати документ", + + // "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", + "dso-selector.export-metadata.dspaceobject.head": "Експорт метаданих з", + + // "dso-selector.no-results": "No {{ type }} found", + "dso-selector.no-results": "{{ type }} не знайдено", + + // "dso-selector.placeholder": "Search for a {{ type }}", + "dso-selector.placeholder": "Шукати {{ type }}", + + + + // "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}", + "confirmation-modal.export-metadata.header": "Експорт метаданих для {{ dsoName }}", + + // "confirmation-modal.export-metadata.info": "Are you sure you want to export metadata for {{ dsoName }}", + "confirmation-modal.export-metadata.info": "Ви впевнені, що хочете експортувати метадані для {{ dsoName }}?", + + // "confirmation-modal.export-metadata.cancel": "Cancel", + "confirmation-modal.export-metadata.cancel": "Скасувати", + + // "confirmation-modal.export-metadata.confirm": "Export", + "confirmation-modal.export-metadata.confirm": "Скасувати", + + // "confirmation-modal.delete-eperson.header": "Delete EPerson \"{{ dsoName }}\"", + "confirmation-modal.delete-eperson.header": "Видалити користувача \"{{ dsoName }}\"", + + // "confirmation-modal.delete-eperson.info": "Are you sure you want to delete EPerson \"{{ dsoName }}\"", + "confirmation-modal.delete-eperson.info": "Ви впевнені, що хочете видалити користувача \"{{ dsoName }}\"?", + + // "confirmation-modal.delete-eperson.cancel": "Cancel", + "confirmation-modal.delete-eperson.cancel": "Скасувати", + + // "confirmation-modal.delete-eperson.confirm": "Delete", + "confirmation-modal.delete-eperson.confirm": "Видалити", + + + // "error.bitstream": "Error fetching bitstream", + "error.bitstream": "Виникла помилка при отриманні файлу", + + // "error.browse-by": "Error fetching items", + "error.browse-by": "Виникла помилка при отриманні документа", + + // "error.collection": "Error fetching collection", + "error.collection": "Виникла помилка при отриманні зібрання", + + // "error.collections": "Error fetching collections", + "error.collections": "Виникла помилка при отриманні зібрань", + + // "error.community": "Error fetching community", + "error.community": "Виникла помилка при отриманні фонду", + + // "error.identifier": "No item found for the identifier", + "error.identifier": "Жодного документу не знайдено за вказаним ідентифікатором", + + // "error.default": "Error", + "error.default": "Виникла якась помилка, шляк трафить", + + // "error.item": "Error fetching item", + "error.item": "Виникла помилка при отриманні документа", + + // "error.items": "Error fetching items", + "error.items": "Виникла помилка при отриманні документів", + + // "error.objects": "Error fetching objects", + "error.objects": "Виникла помилка при отриманні об'єктів", + + // "error.recent-submissions": "Error fetching recent submissions", + "error.recent-submissions": "Виникла помилка при отриманні останніх submissions ", + + // "error.search-results": "Error fetching search results", + "error.search-results": "Виникла помилка при отриманні пошукових результатів", + + // "error.sub-collections": "Error fetching sub-collections", + "error.sub-collections": "Виникла помилка при отриманні підзібрання", + + // "error.sub-communities": "Error fetching sub-communities", + "error.sub-communities": "Виникла помилка при отриманні підфонду", + + // "error.submission.sections.init-form-error": "An error occurred during section initialize, please check your input-form configuration. Details are below :

", + "error.submission.sections.init-form-error": "Сталася помилка. Перевірте конфігурацію форми введення. Подробиці нижче:

", + + // "error.top-level-communities": "Error fetching top-level communities", + "error.top-level-communities": "Виникла помилка при отриманні фонду верхнього рівня", + + // "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": "Ви повинні дати згоду на умови ліцензії, щоб завершити submission. Якщо ви не можете погодитись на умови ліцензії на даний момент, ви можете зберегти свою роботу та повернутися пізніше або видалити submission.", + + // "error.validation.pattern": "This input is restricted by the current pattern: {{ pattern }}.", + "error.validation.pattern": "Вхідна інформація обмежена поточним шаблоном: {{ pattern }}.", + + // "error.validation.filerequired": "The file upload is mandatory", + "error.validation.filerequired": "Завантаження файлу є обов'язковим", + + + + // "file-section.error.header": "Error obtaining files for this item", + "file-section.error.header": "Помилка отримання файлів для цього документа", + + + + // "footer.copyright": "copyright © 2002-{{ year }}", + "footer.copyright": "copyright © 2002-{{ year }}", + + // "footer.link.dspace": "DSpace software", + //TODO Change your univer + "footer.link.dspace": "DSpace software and Lviv Polytechnic National University", + + // "footer.link.lyrasis": "LYRASIS", + "footer.link.lyrasis": "LYRASIS", + + // "footer.link.cookies": "Cookie settings", + "footer.link.cookies": "Налаштування куків", + + // "footer.link.privacy-policy": "Privacy policy", + "footer.link.privacy-policy": "Політика приватності", + + // "footer.link.end-user-agreement":"End User Agreement", + "footer.link.end-user-agreement":"Угода користувача", + + + + // "forgot-email.form.header": "Forgot Password", + "forgot-email.form.header": "Забули пароль?", + + // "forgot-email.form.info": "Enter Register an account to subscribe to collections for email updates, and submit new items to DSpace.", + "forgot-email.form.info": "Зареєструйтесь для отримання повідомлень про нові надходження та отримайте можливість вносити документи.", + + // "forgot-email.form.email": "Email Address *", + "forgot-email.form.email": "Email *", + + // "forgot-email.form.email.error.required": "Please fill in an email address", + "forgot-email.form.email.error.required": "Введіть email", + + // "forgot-email.form.email.error.pattern": "Please fill in a valid email address", + "forgot-email.form.email.error.pattern": "Ви повинні ввести правильний email", + + // "forgot-email.form.email.hint": "This address will be verified and used as your login name.", + "forgot-email.form.email.hint": "Ми провіримо цей email. Використовуйте його для входу у репозитарій", + + // "forgot-email.form.submit": "Submit", + "forgot-email.form.submit": "Надіслати", + + // "forgot-email.form.success.head": "Verification email sent", + "forgot-email.form.success.head": "На Ваш email було надіслане повідомлення", + + // "forgot-email.form.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", + "forgot-email.form.success.content": "На {{ email }} було надіслане повідомлення, що містить спеціальне посилання та подальші інструкції.", + + // "forgot-email.form.error.head": "Error when trying to register email", + "forgot-email.form.error.head": "Виникла помилка при реєстрації email", + + // "forgot-email.form.error.content": "An error occured when registering the following email address: {{ email }}", + "forgot-email.form.error.content": "Виникла помилка при реєстрації email: {{ email }}", + + + + // "forgot-password.title": "Forgot Password", + "forgot-password.title": "Забули пароль?", + + // "forgot-password.form.head": "Forgot Password", + "forgot-password.form.head": "Забули пароль?", + + // "forgot-password.form.info": "Enter a new password in the box below, and confirm it by typing it again into the second box. It should be at least six characters long.", + "forgot-password.form.info": "Введіть новий пароль у поле нижче та підтвердьте його, ввівши його ще раз у друге поле. Він має містити щонайменше 6 символів.", + + // "forgot-password.form.card.security": "Security", + "forgot-password.form.card.security": "Безпека", + + // "forgot-password.form.identification.header": "Identify", + "forgot-password.form.identification.header": "Ідентифікація", + + // "forgot-password.form.identification.email": "Email address: ", + "forgot-password.form.identification.email": "Email: ", + + // "forgot-password.form.label.password": "Password", + "forgot-password.form.label.password": "Пароль", + + // "forgot-password.form.label.passwordrepeat": "Retype to confirm", + "forgot-password.form.label.passwordrepeat": "Введіть ще раз для підтвердження", + + // "forgot-password.form.error.empty-password": "Please enter a password in the box below.", + "forgot-password.form.error.empty-password": "Введіть пароль у поле нижче.", + + // "forgot-password.form.error.matching-passwords": "The passwords do not match.", + "forgot-password.form.error.matching-passwords": "Паролі не співпадають.", + + // "forgot-password.form.error.password-length": "The password should be at least 6 characters long.", + "forgot-password.form.error.password-length": "Довжина пароля повинна становити не менше 6 символів.", + + // "forgot-password.form.notification.error.title": "Error when trying to submit new password", + "forgot-password.form.notification.error.title": "Виникла помилка при реєстрації нового пароля", + + // "forgot-password.form.notification.success.content": "The password reset was successful. You have been logged in as the created user.", + "forgot-password.form.notification.success.content": "Пароль успішно скинуто. Ви увійшли під щойно створеним користувачем.", + + // "forgot-password.form.notification.success.title": "Password reset completed", + "forgot-password.form.notification.success.title": "Пароль скинуто успішно", + + // "forgot-password.form.submit": "Submit password", + "forgot-password.form.submit": "Надіслати пароль", + + + + // "form.add": "Add", + "form.add": "Додати", + + // "form.add-help": "Click here to add the current entry and to add another one", + "form.add-help": "Натисніть тут, щоб додати поточний запис і додати інший", + + // "form.cancel": "Cancel", + "form.cancel": "Скасувати", + + // "form.clear": "Clear", + "form.clear": "Очистити", + + // "form.clear-help": "Click here to remove the selected value", + "form.clear-help": "Натисніть тут, щоб видалити вибране значення", + + // "form.edit": "Edit", + "form.edit": "Редагувати", + + // "form.edit-help": "Click here to edit the selected value", + "form.edit-help": "Натисніть тут, щоб змінити вибране значення", + + // "form.first-name": "First name", + "form.first-name": "Ім'я", + + // "form.group-collapse": "Collapse", + "form.group-collapse": "Згорнути", + + // "form.group-collapse-help": "Click here to collapse", + "form.group-collapse-help": "Натисніть тут щоб згорнути", + + // "form.group-expand": "Expand", + "form.group-expand": "Розгорнути", + + // "form.group-expand-help": "Click here to expand and add more elements", + "form.group-expand-help": "Натисніть тут, щоб розгорнути та додати більше елементів", + + // "form.last-name": "Last name", + "form.last-name": "Прізвище", + + // "form.loading": "Loading...", + "form.loading": "Вантажиться...", + + // "form.lookup": "Lookup", + "form.lookup": "Пошук", + + // "form.lookup-help": "Click here to look up an existing relation", + "form.lookup-help": "Клацніть тут, щоб знайти існуючий зв’язок", + + // "form.no-results": "No results found", + "form.no-results": "Нічого не знайдено", + + // "form.no-value": "No value entered", + "form.no-value": "Значення не введено", + + // "form.other-information": {}, + "form.other-information": {}, + + // "form.remove": "Remove", + "form.remove": "Видалити", + + // "form.save": "Save", + "form.save": "Зберегти", + + // "form.save-help": "Save changes", + "form.save-help": "Зберегти зміни", + + // "form.search": "Search", + "form.search": "Пошук", + + // "form.search-help": "Click here to look for an existing correspondence", + "form.search-help": "Клацніть тут, щоб знайти наявне листування", + + // "form.submit": "Submit", + "form.submit": "Надіслати", + + + + // "home.description": "", + "home.description": "", + + // "home.breadcrumbs": "Home", + "home.breadcrumbs": "Головна", + + // "home.title": "DSpace Angular :: Home", + // TODO Change univer name + "home.title": "Репозитарій Львівської політехніки :: Головна", + + // "home.top-level-communities.head": "Communities in DSpace", + "home.top-level-communities.head": "Фонди", + + // "home.top-level-communities.help": "Select a community to browse its collections.", + "home.top-level-communities.help": "Виберіть фонд, щоб переглянути його зібрання.", + + + + // "info.end-user-agreement.accept": "I have read and I agree to the End User Agreement", + "info.end-user-agreement.accept": "Я прочитав та погоджуюсь із користувацькою угодою", + + // "info.end-user-agreement.accept.error": "An error occurred accepting the End User Agreement", + "info.end-user-agreement.accept.error": "Виникла помилки при при фіксуванні Вашої згоди на користувацьку угоду", + + // "info.end-user-agreement.accept.success": "Successfully updated the End User Agreement", + "info.end-user-agreement.accept.success": "Користувацьку угоду успішно оновлено", + + // "info.end-user-agreement.breadcrumbs": "End User Agreement", + "info.end-user-agreement.breadcrumbs": "Користувацька угода", + + // "info.end-user-agreement.buttons.cancel": "Cancel", + "info.end-user-agreement.buttons.cancel": "Відмінити", + + // "info.end-user-agreement.buttons.save": "Save", + "info.end-user-agreement.buttons.save": "Зберегти", + + // "info.end-user-agreement.head": "End User Agreement", + "info.end-user-agreement.head": "Користувацька угода", + + // "info.end-user-agreement.title": "End User Agreement", + "info.end-user-agreement.title": "Користувацька угода", + + // "info.privacy.breadcrumbs": "Privacy Statement", + "info.privacy.breadcrumbs": "Заява про конфіденційність", + + // "info.privacy.head": "Privacy Statement", + "info.privacy.head": "Заява про конфіденційність", + + // "info.privacy.title": "Privacy Statement", + "info.privacy.title": "Заява про конфіденційність", + + + + // "item.alerts.private": "This item is private", + "item.alerts.private": "Доступ до документа закритий", + + // "item.alerts.withdrawn": "This item has been withdrawn", + "item.alerts.withdrawn": "Цей документ було вилучено", + + + + // "item.edit.authorizations.heading": "With this editor you can view and alter the policies of an item, plus alter policies of individual item components: bundles and bitstreams. Briefly, an item is a container of bundles, and bundles are containers of bitstreams. Containers usually have ADD/REMOVE/READ/WRITE policies, while bitstreams only have READ/WRITE policies.", + "item.edit.authorizations.heading": "За допомогою цього редактора ви можете переглядати та змінювати політики документа, а також змінювати політики окремих компонентів документа. Документ — це множина пакетів, а пакети — це множина файлів. Контейнери зазвичай мають політики ADD/REMOVE/READ/WRITE, тоді як файли мають лише політики READ/WRITE.", + + // "item.edit.authorizations.title": "Edit item's Policies", + "item.edit.authorizations.title": "Редагувати політики документа", + + + + // "item.badge.private": "Private", + "item.badge.private": "Приватне", + + // "item.badge.withdrawn": "Withdrawn", + "item.badge.withdrawn": "Вилучено", + + + + // "item.bitstreams.upload.bundle": "Bundle", + "item.bitstreams.upload.bundle": "контейнер файлів", + + // "item.bitstreams.upload.bundle.placeholder": "Select a bundle", + "item.bitstreams.upload.bundle.placeholder": "Виберіть контейнер файлів", + + // "item.bitstreams.upload.bundle.new": "Create bundle", + "item.bitstreams.upload.bundle.new": "Створити контейнер файлів", + + // "item.bitstreams.upload.bundles.empty": "This item doesn\'t contain any bundles to upload a bitstream to.", + "item.bitstreams.upload.bundles.empty": "Цей документ не містить контейнеру файлів, щоб завантажити файл.", + + // "item.bitstreams.upload.cancel": "Cancel", + "item.bitstreams.upload.cancel": "Відмінити", + + // "item.bitstreams.upload.drop-message": "Drop a file to upload", + "item.bitstreams.upload.drop-message": "Виберіть файл для завантаження", + + // "item.bitstreams.upload.item": "Item: ", + "item.bitstreams.upload.item": "Документ: ", + + // "item.bitstreams.upload.notifications.bundle.created.content": "Successfully created new bundle.", + "item.bitstreams.upload.notifications.bundle.created.content": "Контейнер файлів створено.", + + // "item.bitstreams.upload.notifications.bundle.created.title": "Created bundle", + "item.bitstreams.upload.notifications.bundle.created.title": "Контейнер файлів створено.", + + // "item.bitstreams.upload.notifications.upload.failed": "Upload failed. Please verify the content before retrying.", + "item.bitstreams.upload.notifications.upload.failed": "Не вдалось завантажити. Перевірте вміст даних", + + // "item.bitstreams.upload.title": "Upload bitstream", + "item.bitstreams.upload.title": "Завантажити файл", + + + + // "item.edit.bitstreams.bundle.edit.buttons.upload": "Upload", + "item.edit.bitstreams.bundle.edit.buttons.upload": "Завантажити", + + // "item.edit.bitstreams.bundle.displaying": "Currently displaying {{ amount }} bitstreams of {{ total }}.", + "item.edit.bitstreams.bundle.displaying": "Зараз відображаються {{ amount }}файли {{ total }}.", + + // "item.edit.bitstreams.bundle.load.all": "Load all ({{ total }})", + "item.edit.bitstreams.bundle.load.all": "Завантажити все ({{ total }})", + + // "item.edit.bitstreams.bundle.load.more": "Load more", + "item.edit.bitstreams.bundle.load.more": "Завантажити ще", + + // "item.edit.bitstreams.bundle.name": "BUNDLE: {{ name }}", + "item.edit.bitstreams.bundle.name": "Контейнер файлів: {{ name }}", + + // "item.edit.bitstreams.discard-button": "Discard", + "item.edit.bitstreams.discard-button": "Відхилити", + + // "item.edit.bitstreams.edit.buttons.download": "Download", + "item.edit.bitstreams.edit.buttons.download": "Завантажити", + + // "item.edit.bitstreams.edit.buttons.drag": "Drag", + "item.edit.bitstreams.edit.buttons.drag": "Перетягнути", + + // "item.edit.bitstreams.edit.buttons.edit": "Edit", + "item.edit.bitstreams.edit.buttons.edit": "Редагувати", + + // "item.edit.bitstreams.edit.buttons.remove": "Remove", + "item.edit.bitstreams.edit.buttons.remove": "Видалити", + + // "item.edit.bitstreams.edit.buttons.undo": "Undo changes", + "item.edit.bitstreams.edit.buttons.undo": "Відмінити зміни", + + // "item.edit.bitstreams.empty": "This item doesn't contain any bitstreams. Click the upload button to create one.", + "item.edit.bitstreams.empty": "Цей документ не містить жодного файлу. Клацніть для завантаження", + + // "item.edit.bitstreams.headers.actions": "Actions", + "item.edit.bitstreams.headers.actions": "Дії", + + // "item.edit.bitstreams.headers.bundle": "Bundle", + "item.edit.bitstreams.headers.bundle": "Контецнер файлів", + + // "item.edit.bitstreams.headers.description": "Description", + "item.edit.bitstreams.headers.description": "Опис", + + // "item.edit.bitstreams.headers.format": "Format", + "item.edit.bitstreams.headers.format": "Формат", + + // "item.edit.bitstreams.headers.name": "Name", + "item.edit.bitstreams.headers.name": "Назва", + + // "item.edit.bitstreams.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", + "item.edit.bitstreams.notifications.discarded.content": "Ваші зміни скасовано. Щоб відновити зміни, натисніть кнопку -Назад-.", + + // "item.edit.bitstreams.notifications.discarded.title": "Changes discarded", + "item.edit.bitstreams.notifications.discarded.title": "Зміни скасовано", + + // "item.edit.bitstreams.notifications.move.failed.title": "Error moving bitstreams", + "item.edit.bitstreams.notifications.move.failed.title": "Помилка переміщення файлу", + + // "item.edit.bitstreams.notifications.move.saved.content": "Your move changes to this item's bitstreams and bundles have been saved.", + "item.edit.bitstreams.notifications.move.saved.content": "Зміни до документа збережені.", + + // "item.edit.bitstreams.notifications.move.saved.title": "Move changes saved", + "item.edit.bitstreams.notifications.move.saved.title": "Зміни до документа збережені", + + // "item.edit.bitstreams.notifications.outdated.content": "The item you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts", + "item.edit.bitstreams.notifications.outdated.content": "Документ, над яким ви зараз працюєте, був змінений іншим користувачем. Ваші поточні зміни відхилено, щоб запобігти конфліктам", + + // "item.edit.bitstreams.notifications.outdated.title": "Changes outdated", + "item.edit.bitstreams.notifications.outdated.title": "Зміни застаріли", + + // "item.edit.bitstreams.notifications.remove.failed.title": "Error deleting bitstream", + "item.edit.bitstreams.notifications.remove.failed.title": "Помилка видалення файла", + + // "item.edit.bitstreams.notifications.remove.saved.content": "Your removal changes to this item's bitstreams have been saved.", + "item.edit.bitstreams.notifications.remove.saved.content": "Ваші зміни до документа збережено.", + + // "item.edit.bitstreams.notifications.remove.saved.title": "Removal changes saved", + "item.edit.bitstreams.notifications.remove.saved.title": "Ваші зміни до документа збережено", + + // "item.edit.bitstreams.reinstate-button": "Undo", + "item.edit.bitstreams.reinstate-button": "Назад", + + // "item.edit.bitstreams.save-button": "Save", + "item.edit.bitstreams.save-button": "Зберегти", + + // "item.edit.bitstreams.upload-button": "Upload", + "item.edit.bitstreams.upload-button": "Завантажити", + + + + // "item.edit.delete.cancel": "Cancel", + "item.edit.delete.cancel": "Відмінити", + + // "item.edit.delete.confirm": "Delete", + "item.edit.delete.confirm": "Видалити", + + // "item.edit.delete.description": "Are you sure this item should be completely deleted? Caution: At present, no tombstone would be left.", + "item.edit.delete.description": "Ви впевнені, що цей документ потрібно повністю видалити? Застереження: можливості відновити не буде.", + + // "item.edit.delete.error": "An error occurred while deleting the item", + "item.edit.delete.error": "Виникла помилка при видаленні документа", + + // "item.edit.delete.header": "Delete item: {{ id }}", + "item.edit.delete.header": "Видалити документ: {{ id }}", + + // "item.edit.delete.success": "The item has been deleted", + "item.edit.delete.success": "Документ видалено", + + // "item.edit.head": "Edit Item", + "item.edit.head": "Редагувати документ", + + // "item.edit.breadcrumbs": "Edit Item", + "item.edit.breadcrumbs": "Редагувати документ", + + + // "item.edit.tabs.mapper.head": "Collection Mapper", + "item.edit.tabs.mapper.head": "Карта зібрання", + + // "item.edit.tabs.item-mapper.title": "Item Edit - Collection Mapper", + "item.edit.tabs.item-mapper.title": "Редагування документа - карта зібрання", + + // "item.edit.item-mapper.buttons.add": "Map item to selected collections", + "item.edit.item-mapper.buttons.add": "Карта документа до вибраного зібрання", + + // "item.edit.item-mapper.buttons.remove": "Remove item's mapping for selected collections", + "item.edit.item-mapper.buttons.remove": "Видалити карту документа для вибраного зібрання", + + // "item.edit.item-mapper.cancel": "Cancel", + "item.edit.item-mapper.cancel": "Відмінити", + + // "item.edit.item-mapper.description": "This is the item mapper tool that allows administrators to map this item to other collections. You can search for collections and map them, or browse the list of collections the item is currently mapped to.", + "item.edit.item-mapper.description": "Це інструмент карти документа, який дозволяє адміністраторам прив'язувати цей документ до інших зібрань. Ви можете шукати зібрання та прив'язувати їх, або переглядати список зібрань на які посилається документ.", + + // "item.edit.item-mapper.head": "Item Mapper - Map Item to Collections", + "item.edit.item-mapper.head": "Карта документа - прив'язка документа до зібрання", + + // "item.edit.item-mapper.item": "Item: \"{{name}}\"", + "item.edit.item-mapper.item": "Документ: \"{{name}}\"", + + // "item.edit.item-mapper.no-search": "Please enter a query to search", + "item.edit.item-mapper.no-search": "Введіть запит для пошуку", + + // "item.edit.item-mapper.notifications.add.error.content": "Errors occurred for mapping of item to {{amount}} collections.", + "item.edit.item-mapper.notifications.add.error.content": "Виникла помилка прив'язування документа до{{amount}} зібрання.", + + // "item.edit.item-mapper.notifications.add.error.head": "Mapping errors", + "item.edit.item-mapper.notifications.add.error.head": "Помилка прив'язування", + + // "item.edit.item-mapper.notifications.add.success.content": "Successfully mapped item to {{amount}} collections.", + "item.edit.item-mapper.notifications.add.success.content": "Документ успішно прив'язано до {{amount}} зібрання.", + + // "item.edit.item-mapper.notifications.add.success.head": "Mapping completed", + "item.edit.item-mapper.notifications.add.success.head": "Прив'язування завершене", + + // "item.edit.item-mapper.notifications.remove.error.content": "Errors occurred for the removal of the mapping to {{amount}} collections.", + "item.edit.item-mapper.notifications.remove.error.content": "Виникла помилка прив'язування документа до {{amount}} зібрання.", + + // "item.edit.item-mapper.notifications.remove.error.head": "Removal of mapping errors", + "item.edit.item-mapper.notifications.remove.error.head": "Виникла помилка прив'язування", + + // "item.edit.item-mapper.notifications.remove.success.content": "Successfully removed mapping of item to {{amount}} collections.", + "item.edit.item-mapper.notifications.remove.success.content": "Прив'язування документа до {{amount}} зібрання успішно видалено.", + + // "item.edit.item-mapper.notifications.remove.success.head": "Removal of mapping completed", + "item.edit.item-mapper.notifications.remove.success.head": "Прив'язування видалено", + + // "item.edit.item-mapper.tabs.browse": "Browse mapped collections", + "item.edit.item-mapper.tabs.browse": "Переглянути прив'язки зібрання", + + // "item.edit.item-mapper.tabs.map": "Map new collections", + "item.edit.item-mapper.tabs.map": "Прив'язати нове зібрання", + + + + // "item.edit.metadata.add-button": "Add", + "item.edit.metadata.add-button": "Додати", + + // "item.edit.metadata.discard-button": "Discard", + "item.edit.metadata.discard-button": "Відмінити", + + // "item.edit.metadata.edit.buttons.edit": "Edit", + "item.edit.metadata.edit.buttons.edit": "Редагувати", + + // "item.edit.metadata.edit.buttons.remove": "Remove", + "item.edit.metadata.edit.buttons.remove": "Видалити", + + // "item.edit.metadata.edit.buttons.undo": "Undo changes", + "item.edit.metadata.edit.buttons.undo": "Відмінити зміни", + + // "item.edit.metadata.edit.buttons.unedit": "Stop editing", + "item.edit.metadata.edit.buttons.unedit": "Зупинити редагування", + + // "item.edit.metadata.empty": "The item currently doesn't contain any metadata. Click Add to start adding a metadata value.", + "item.edit.metadata.empty": "Документ не містить жодних метаданих. Клікніть Додати щоб почати додавати метадані.", + + // "item.edit.metadata.headers.edit": "Edit", + "item.edit.metadata.headers.edit": "Редагувати", + + // "item.edit.metadata.headers.field": "Field", + "item.edit.metadata.headers.field": "Поле", + + // "item.edit.metadata.headers.language": "Lang", + "item.edit.metadata.headers.language": "Мова", + + // "item.edit.metadata.headers.value": "Value", + "item.edit.metadata.headers.value": "Значення", + + // "item.edit.metadata.metadatafield.invalid": "Please choose a valid metadata field", + "item.edit.metadata.metadatafield.invalid": "Виберіть відповідне поле метаданих", + + // "item.edit.metadata.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", + "item.edit.metadata.notifications.discarded.content": "Ваші зміни скасовано. Щоб відновити зміни, натисніть кнопку -Повернутись-.", + + // "item.edit.metadata.notifications.discarded.title": "Changed discarded", + "item.edit.metadata.notifications.discarded.title": "Ваші зміни скасовано", + + // "item.edit.metadata.notifications.error.title": "An error occurred", + "item.edit.metadata.notifications.error.title": "Ваші зміни скасовано", + + // "item.edit.metadata.notifications.invalid.content": "Your changes were not saved. Please make sure all fields are valid before you save.", + "item.edit.metadata.notifications.invalid.content": "Ваші зміни не збережено. Перед збереженням переконайтеся, що всі поля вірно заповнені.", + + // "item.edit.metadata.notifications.invalid.title": "Metadata invalid", + "item.edit.metadata.notifications.invalid.title": "Помилкові метадані", + + // "item.edit.metadata.notifications.outdated.content": "The item you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts", + "item.edit.metadata.notifications.outdated.content": "Документ, над яким ви зараз працюєте, був змінений іншим користувачем. Ваші поточні зміни відхилено, щоб запобігти конфліктам", + + // "item.edit.metadata.notifications.outdated.title": "Changed outdated", + "item.edit.metadata.notifications.outdated.title": "Зміни застаріли", + + // "item.edit.metadata.notifications.saved.content": "Your changes to this item's metadata were saved.", + "item.edit.metadata.notifications.saved.content": "Ваші зміни метаданих документа збережено.", + + // "item.edit.metadata.notifications.saved.title": "Metadata saved", + "item.edit.metadata.notifications.saved.title": "Метадані збережено", + + // "item.edit.metadata.reinstate-button": "Undo", + "item.edit.metadata.reinstate-button": "Повернутись", + + // "item.edit.metadata.save-button": "Save", + "item.edit.metadata.save-button": "Зберегти", + + + + // "item.edit.modify.overview.field": "Field", + "item.edit.modify.overview.field": "Поле", + + // "item.edit.modify.overview.language": "Language", + "item.edit.modify.overview.language": "Мова", + + // "item.edit.modify.overview.value": "Value", + "item.edit.modify.overview.value": "Значення", + + + + // "item.edit.move.cancel": "Cancel", + "item.edit.move.cancel": "Відмінити", + + // "item.edit.move.description": "Select the collection you wish to move this item to. To narrow down the list of displayed collections, you can enter a search query in the box.", + "item.edit.move.description": "Виберіть зібрання, до якої потрібно перемістити цей документ. Щоб звузити список відображених зібрань, ви можете ввести пошуковий запит у поле.", + + // "item.edit.move.error": "An error occurred when attempting to move the item", + "item.edit.move.error": "Під час спроби перемістити документ - сталася помилка", + + // "item.edit.move.head": "Move item: {{id}}", + "item.edit.move.head": "Перемістити документ: {{id}}", + + // "item.edit.move.inheritpolicies.checkbox": "Inherit policies", + "item.edit.move.inheritpolicies.checkbox": "Наслідувати політики", + + // "item.edit.move.inheritpolicies.description": "Inherit the default policies of the destination collection", + "item.edit.move.inheritpolicies.description": "Наслідувати політики за замовчуванням цільового зібрання", + + // "item.edit.move.move": "Move", + "item.edit.move.move": "Перенести", + + // "item.edit.move.processing": "Moving...", + "item.edit.move.processing": "Переносимо...", + + // "item.edit.move.search.placeholder": "Enter a search query to look for collections", + "item.edit.move.search.placeholder": "Введіть пошуковий запит для пошуку зібрання", + + // "item.edit.move.success": "The item has been moved successfully", + "item.edit.move.success": "Документ успішно перенесено", + + // "item.edit.move.title": "Move item", + "item.edit.move.title": "Перенести документ", + + + + // "item.edit.private.cancel": "Cancel", + "item.edit.private.cancel": "Відмінити", + + // "item.edit.private.confirm": "Make it Private", + "item.edit.private.confirm": "Закрити доступ", + + // "item.edit.private.description": "Are you sure this item should be made private in the archive?", + "item.edit.private.description": "Ви впевнені, що хочете закрити доступ до документа?", + + // "item.edit.private.error": "An error occurred while making the item private", + "item.edit.private.error": "Виникла помилка при закритті доступу до документа", + + // "item.edit.private.header": "Make item private: {{ id }}", + "item.edit.private.header": "Закрити доступ документу: {{ id }}", + + // "item.edit.private.success": "The item is now private", + "item.edit.private.success": "Доступ до документа закритий", + + + + // "item.edit.public.cancel": "Cancel", + "item.edit.public.cancel": "ВІдмінити", + + // "item.edit.public.confirm": "Make it Public", + "item.edit.public.confirm": "Зробити загальнодоступним", + + // "item.edit.public.description": "Are you sure this item should be made public in the archive?", + "item.edit.public.description": "Ви впевнені, що хочете зробити вільним доступ до документа?", + + // "item.edit.public.error": "An error occurred while making the item public", + "item.edit.public.error": "Виникла помилка при відкритті доступу до документа", + + // "item.edit.public.header": "Make item public: {{ id }}", + "item.edit.public.header": "Зробити документ загальнодоступним: {{ id }}", + + // "item.edit.public.success": "The item is now public", + "item.edit.public.success": "Документ тепер загальнодоступний", + + + + // "item.edit.reinstate.cancel": "Cancel", + "item.edit.reinstate.cancel": "Відмінити", + + // "item.edit.reinstate.confirm": "Reinstate", + "item.edit.reinstate.confirm": "Відновити", + + // "item.edit.reinstate.description": "Are you sure this item should be reinstated to the archive?", + "item.edit.reinstate.description": "Ви дійсно хочете відновити документ?", + + // "item.edit.reinstate.error": "An error occurred while reinstating the item", + "item.edit.reinstate.error": "Виникла помилка при відновленні документа", + + // "item.edit.reinstate.header": "Reinstate item: {{ id }}", + "item.edit.reinstate.header": "Відновлення документа: {{ id }}", + + // "item.edit.reinstate.success": "The item was reinstated successfully", + "item.edit.reinstate.success": "Документ був успішно відновлений", + + + + // "item.edit.relationships.discard-button": "Discard", + "item.edit.relationships.discard-button": "Відмінити", + + // "item.edit.relationships.edit.buttons.add": "Add", + "item.edit.relationships.edit.buttons.add": "Додати", + + // "item.edit.relationships.edit.buttons.remove": "Remove", + "item.edit.relationships.edit.buttons.remove": "Видалити", + + // "item.edit.relationships.edit.buttons.undo": "Undo changes", + "item.edit.relationships.edit.buttons.undo": "Повернути зміни", + + // "item.edit.relationships.no-relationships": "No relationships", + "item.edit.relationships.no-relationships": "Відсутні зв'язки", + + // "item.edit.relationships.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", + "item.edit.relationships.notifications.discarded.content": "Ваші зміни скасовано. Щоб відновити зміни, натисніть кнопку -Повернутись-", + + // "item.edit.relationships.notifications.discarded.title": "Changes discarded", + "item.edit.relationships.notifications.discarded.title": "Зміни скасовано", + + // "item.edit.relationships.notifications.failed.title": "Error editing relationships", + "item.edit.relationships.notifications.failed.title": "Помилка редагування зв'яків", + + // "item.edit.relationships.notifications.outdated.content": "The item you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts", + "item.edit.relationships.notifications.outdated.content": "Документ, над яким ви зараз працюєте, був змінений іншим користувачем. Ваші поточні зміни відхилено, щоб запобігти конфліктам", + + // "item.edit.relationships.notifications.outdated.title": "Changes outdated", + "item.edit.relationships.notifications.outdated.title": "Зміни застаріли", + + // "item.edit.relationships.notifications.saved.content": "Your changes to this item's relationships were saved.", + "item.edit.relationships.notifications.saved.content": "Зміни до зв'язків документа успішно збережено.", + + // "item.edit.relationships.notifications.saved.title": "Relationships saved", + "item.edit.relationships.notifications.saved.title": "Зв'язки збережено", + + // "item.edit.relationships.reinstate-button": "Undo", + "item.edit.relationships.reinstate-button": "Повернутись", + + // "item.edit.relationships.save-button": "Save", + "item.edit.relationships.save-button": "Зберегти", + + // "item.edit.relationships.no-entity-type": "Add 'dspace.entity.type' metadata to enable relationships for this item", + "item.edit.relationships.no-entity-type": "Додайте 'dspace.entity.type' для створення зв'язків для документа", + + + + // "item.edit.tabs.bitstreams.head": "Bitstreams", + "item.edit.tabs.bitstreams.head": "Файли", + + // "item.edit.tabs.bitstreams.title": "Редагування докумена - Файли", + "item.edit.tabs.bitstreams.title": "Редагування докумена - Файли", + + // "item.edit.tabs.curate.head": "Curate", + // TODO translate + "item.edit.tabs.curate.head": "Curate", + + // "item.edit.tabs.curate.title": "Item Edit - Curate", + // TODO translate + "item.edit.tabs.curate.title": "Редагування документа - Curate", + + // "item.edit.tabs.metadata.head": "Metadata", + "item.edit.tabs.metadata.head": "Метадані", + + // "item.edit.tabs.metadata.title": "Item Edit - Metadata", + "item.edit.tabs.metadata.title": "Редагування документа - метадані", + + // "item.edit.tabs.relationships.head": "Relationships", + "item.edit.tabs.relationships.head": "Зв'язки", + + // "item.edit.tabs.relationships.title": "Редагування документа - зв'язки", + "item.edit.tabs.relationships.title": "Редагування документа - зв'язки", + + // "item.edit.tabs.status.buttons.authorizations.button": "Authorizations...", + "item.edit.tabs.status.buttons.authorizations.button": "Авторизація...", + + // "item.edit.tabs.status.buttons.authorizations.label": "Edit item's authorization policies", + "item.edit.tabs.status.buttons.authorizations.label": "Редагувати політики доступу документа", + + // "item.edit.tabs.status.buttons.delete.button": "Permanently delete", + "item.edit.tabs.status.buttons.delete.button": "Видалити назавжди", + + // "item.edit.tabs.status.buttons.delete.label": "Completely expunge item", + "item.edit.tabs.status.buttons.delete.label": "Повністю видалити документ", + + // "item.edit.tabs.status.buttons.mappedCollections.button": "Mapped collections", + "item.edit.tabs.status.buttons.mappedCollections.button": "Прив'язані зібрання", + + // "item.edit.tabs.status.buttons.mappedCollections.label": "Manage mapped collections", + "item.edit.tabs.status.buttons.mappedCollections.label": "Керувати прив'язаними зібраннями", + + // "item.edit.tabs.status.buttons.move.button": "Move...", + "item.edit.tabs.status.buttons.move.button": "Перемістити...", + + // "item.edit.tabs.status.buttons.move.label": "Move item to another collection", + "item.edit.tabs.status.buttons.move.label": "Перемістити документ до іншого зібрання", + + // "item.edit.tabs.status.buttons.private.button": "Make it private...", + "item.edit.tabs.status.buttons.private.button": "Закрити доступ...", + + // "item.edit.tabs.status.buttons.private.label": "Make item private", + "item.edit.tabs.status.buttons.private.label": "Закрити доступ до документа", + + // "item.edit.tabs.status.buttons.public.button": "Make it public...", + "item.edit.tabs.status.buttons.public.button": "Зробити загальнодоступним...", + + // "item.edit.tabs.status.buttons.public.label": "Make item public", + "item.edit.tabs.status.buttons.public.label": "Зробити документ загальнодоступним", + + // "item.edit.tabs.status.buttons.reinstate.button": "Reinstate...", + "item.edit.tabs.status.buttons.reinstate.button": "Відновити...", + + // "item.edit.tabs.status.buttons.reinstate.label": "Reinstate item into the repository", + "item.edit.tabs.status.buttons.reinstate.label": "Відновити документ в репозиторій", + + // "item.edit.tabs.status.buttons.withdraw.button": "Withdraw...", + "item.edit.tabs.status.buttons.withdraw.button": "Вилучити...", + + // "item.edit.tabs.status.buttons.withdraw.label": "Withdraw item from the repository", + "item.edit.tabs.status.buttons.withdraw.label": "Вилучити документ з репозиторію", + + // "item.edit.tabs.status.description": "Welcome to the item management page. From here you can withdraw, reinstate, move or delete the item. You may also update or add new metadata / bitstreams on the other tabs.", + "item.edit.tabs.status.description": "Вітаємо на сторінці керування документами. Тут ви можете вилучити, відновити, перемістити або видалити елемент. Ви також можете оновити або додати нові метадані чи файли на інших вкладках.", + + // "item.edit.tabs.status.head": "Status", + "item.edit.tabs.status.head": "Статус", + + // "item.edit.tabs.status.labels.handle": "Handle", + "item.edit.tabs.status.labels.handle": "Handle", + + // "item.edit.tabs.status.labels.id": "Item Internal ID", + "item.edit.tabs.status.labels.id": "Внутрішнє ID", + + // "item.edit.tabs.status.labels.itemPage": "Item Page", + "item.edit.tabs.status.labels.itemPage": "Сторінка документа", + + // "item.edit.tabs.status.labels.lastModified": "Last Modified", + "item.edit.tabs.status.labels.lastModified": "Останні редагування", + + // "item.edit.tabs.status.title": "Item Edit - Status", + "item.edit.tabs.status.title": "Редагування документа - статус", + + // "item.edit.tabs.versionhistory.head": "Version History", + "item.edit.tabs.versionhistory.head": "Історія версій", + + // "item.edit.tabs.versionhistory.title": "Item Edit - Version History", + "item.edit.tabs.versionhistory.title": "Редагування документа - історія версій", + + // "item.edit.tabs.versionhistory.under-construction": "Editing or adding new versions is not yet possible in this user interface.", + "item.edit.tabs.versionhistory.under-construction": "Редагування або додавання нових версій ще неможливе в цьому інтерфейсі користувача.", + + // "item.edit.tabs.view.head": "View Item", + "item.edit.tabs.view.head": "Перегляд документа", + + // "item.edit.tabs.view.title": "Item Edit - View", + "item.edit.tabs.view.title": "Редагування документа - перегляд", + + + + // "item.edit.withdraw.cancel": "Cancel", + "item.edit.withdraw.cancel": "Відмінити", + + // "item.edit.withdraw.confirm": "Withdraw", + "item.edit.withdraw.confirm": "Вилучити", + + // "item.edit.withdraw.description": "Are you sure this item should be withdrawn from the archive?", + "item.edit.withdraw.description": "Ви впевнені, що бажаєте вилучити документ?", + + // "item.edit.withdraw.error": "An error occurred while withdrawing the item", + "item.edit.withdraw.error": "Виникла помилка при вилученні документа", + + // "item.edit.withdraw.header": "Withdraw item: {{ id }}", + "item.edit.withdraw.header": "Вилучити документ: {{ id }}", + + // "item.edit.withdraw.success": "The item was withdrawn successfully", + "item.edit.withdraw.success": "Документ був успішно вилучений", + + + + // "item.listelement.badge": "Item", + "item.listelement.badge": "Документ", + + // "item.page.description": "Description", + "item.page.description": "Опис", + + // "item.page.edit": "Edit this item", + "item.page.edit": "Редагувати цей документ", + + // "item.page.journal-issn": "Journal ISSN", + "item.page.journal-issn": "Номер ISSN", + + // "item.page.journal-title": "Journal Title", + "item.page.journal-title": "Назва журналу", + + // "item.page.publisher": "Publisher", + "item.page.publisher": "Видавець", + + // "item.page.titleprefix": "Item: ", + "item.page.titleprefix": "Документ: ", + + // "item.page.volume-title": "Volume Title", + "item.page.volume-title": "Назва тому", + + // "item.search.results.head": "Результат пошуку за документами", + "item.search.results.head": "Результат пошуку за документами", + + // "item.search.title": "DSpace Angular :: Item Search", + "item.search.title": "Репозитарій :: Пошук документів", + + + + // "item.page.abstract": "Abstract", + "item.page.abstract": "Анотація", + + // "item.page.author": "Authors", + "item.page.author": "Автори", + + // "item.page.citation": "Citation", + "item.page.citation": "Бібліографічний опис", + + // "item.page.collections": "Collections", + "item.page.collections": "Зібрання", + + // "item.page.date": "Date", + "item.page.date": "Дата", + + // "item.page.edit": "Edit this item", + "item.page.edit": "Редагувати цей документ", + + // "item.page.files": "Files", + "item.page.files": "Файли", + + // "item.page.filesection.description": "Description:", + "item.page.filesection.description": "Опис:", + + // "item.page.filesection.download": "Download", + "item.page.filesection.download": "Завантажити", + + // "item.page.filesection.format": "Format:", + "item.page.filesection.format": "Формат:", + + // "item.page.filesection.name": "Name:", + "item.page.filesection.name": "Назва:", + + // "item.page.filesection.size": "Size:", + "item.page.filesection.size": "Розмір:", + + // "item.page.journal.search.title": "Articles in this journal", + "item.page.journal.search.title": "Публікації у цьому виданні", + + // "item.page.link.full": "Full item page", + "item.page.link.full": "Повна інформація про документ", + + // "item.page.link.simple": "Simple item page", + "item.page.link.simple": "Скорочена інформація про документ", + + // "item.page.person.search.title": "Articles by this author", + "item.page.person.search.title": "Публікації за автором", + + // "item.page.related-items.view-more": "Show {{ amount }} more", + "item.page.related-items.view-more": "Показати {{ amount }} більше", + + // "item.page.related-items.view-less": "Hide last {{ amount }}", + "item.page.related-items.view-less": "Приховати останні {{ amount }}", + + // "item.page.relationships.isAuthorOfPublication": "Publications", + "item.page.relationships.isAuthorOfPublication": "Публікації", + + // "item.page.relationships.isJournalOfPublication": "Publications", + "item.page.relationships.isJournalOfPublication": "Публікації", + + // "item.page.relationships.isOrgUnitOfPerson": "Authors", + "item.page.relationships.isOrgUnitOfPerson": "Автори", + + // "item.page.relationships.isOrgUnitOfProject": "Research Projects", + "item.page.relationships.isOrgUnitOfProject": "Дослідницькі проекти", + + // "item.page.subject": "Keywords", + "item.page.subject": "Ключові слова", + + // "item.page.uri": "URI", + "item.page.uri": "URI", + + // "item.page.bitstreams.view-more": "Show more", + "item.page.bitstreams.view-more": "Показати більше", + + // "item.page.bitstreams.collapse": "Collapse", + "item.page.bitstreams.collapse": "Згорнути", + + // "item.page.filesection.original.bundle" : "Original bundle", + "item.page.filesection.original.bundle" : "Контейнер файлів", + + // "item.page.filesection.license.bundle" : "License bundle", + "item.page.filesection.license.bundle" : "Ліцензійна угода", + + // "item.preview.dc.identifier.uri": "Identifier:", + "item.preview.dc.identifier.uri": "Ідентифікатор:", + + // "item.preview.dc.contributor.author": "Authors:", + "item.preview.dc.contributor.author": "Автори:", + + // "item.preview.dc.date.issued": "Published date:", + "item.preview.dc.date.issued": "Дата публікації:", + + // "item.preview.dc.description.abstract": "Abstract:", + "item.preview.dc.description.abstract": "Анотація:", + + // "item.preview.dc.identifier.other": "Other identifier:", + "item.preview.dc.identifier.other": "Інший ідентифікатор:", + + // "item.preview.dc.language.iso": "Language:", + "item.preview.dc.language.iso": "Мова:", + + // "item.preview.dc.subject": "Subjects:", + "item.preview.dc.subject": "Ключові слова:", + + // "item.preview.dc.title": "Title:", + "item.preview.dc.title": "Назва:", + + // "item.preview.person.familyName": "Surname:", + "item.preview.person.familyName": "Прізвище:", + + // "item.preview.person.givenName": "Name:", + "item.preview.person.givenName": "Ім'я:", + + // "item.preview.person.identifier.orcid": "ORCID:", + "item.preview.person.identifier.orcid": "ORCID:", + + + // "item.select.confirm": "Confirm selected", + "item.select.confirm": "Підтвердити вибрані", + + // "item.select.empty": "No items to show", + "item.select.empty": "Таких документів немає", + + // "item.select.table.author": "Author", + "item.select.table.author": "Автор", + + // "item.select.table.collection": "Collection", + "item.select.table.collection": "Зібрання", + + // "item.select.table.title": "Title", + "item.select.table.title": "Назва", + + + // "item.version.history.empty": "There are no other versions for this item yet.", + "item.version.history.empty": "Іншої версії цього документу немає.", + + // "item.version.history.head": "Version History", + "item.version.history.head": "Історія версій", + + // "item.version.history.return": "Return", + "item.version.history.return": "Повернутись", + + // "item.version.history.selected": "Selected version", + "item.version.history.selected": "Вибрана версія", + + // "item.version.history.table.version": "Version", + "item.version.history.table.version": "Версія", + + // "item.version.history.table.item": "Item", + "item.version.history.table.item": "Документ", + + // "item.version.history.table.editor": "Editor", + "item.version.history.table.editor": "Редактор", + + // "item.version.history.table.date": "Date", + "item.version.history.table.date": "Дата", + + // "item.version.history.table.summary": "Summary", + "item.version.history.table.summary": "Анотація/Summary", + + + + // "item.version.notice": "This is not the latest version of this item. The latest version can be found here.", + "item.version.notice": "Це не остання версія документа. Останню версію можна знайти за покликанням ТУТ.", + + + + // "journal.listelement.badge": "Journal", + "journal.listelement.badge": "Видання", + + // "journal.page.description": "Description", + "journal.page.description": "Опис", + + // "journal.page.edit": "Edit this item", + "journal.page.edit": "Редагувати цей документ", + + // "journal.page.editor": "Editor-in-Chief", + "journal.page.editor": "Головний редактор", + + // "journal.page.issn": "ISSN", + "journal.page.issn": "ISSN", + + // "journal.page.publisher": "Publisher", + "journal.page.publisher": "Видавець", + + // "journal.page.titleprefix": "Journal: ", + "journal.page.titleprefix": "Видання: ", + + // "journal.search.results.head": "Journal Search Results", + "journal.search.results.head": "Пошук у виданні", + + // "journal.search.title": "DSpace Angular :: Journal Search", + "journal.search.title": "Репозитарій :: пошук видання", + + + + // "journalissue.listelement.badge": "Journal Issue", + "journalissue.listelement.badge": "Випуск видання", + + // "journalissue.page.description": "Description", + "journalissue.page.description": "Опис", + + // "journalissue.page.edit": "Edit this item", + "journalissue.page.edit": "Редагувати цей документ", + + // "journalissue.page.issuedate": "Issue Date", + "journalissue.page.issuedate": "Дата випуску", + + // "journalissue.page.journal-issn": "Journal ISSN", + "journalissue.page.journal-issn": "ISSN", + + // "journalissue.page.journal-title": "Journal Title", + "journalissue.page.journal-title": "Назва видання", + + // "journalissue.page.keyword": "Keywords", + "journalissue.page.keyword": "Ключові слова", + + // "journalissue.page.number": "Number", + "journalissue.page.number": "Номер", + + // "journalissue.page.titleprefix": "Journal Issue: ", + "journalissue.page.titleprefix": "Випуск видання: ", + + + + // "journalvolume.listelement.badge": "Journal Volume", + "journalvolume.listelement.badge": "Том видання", + + // "journalvolume.page.description": "Description", + "journalvolume.page.description": "Опис", + + // "journalvolume.page.edit": "Edit this item", + + "journalvolume.page.edit": "Редагувати цей документ", + + // "journalvolume.page.issuedate": "Issue Date", + "journalvolume.page.issuedate": "Дата випуску", + + // "journalvolume.page.titleprefix": "Journal Volume: ", + "journalvolume.page.titleprefix": "Том видання: ", + + // "journalvolume.page.volume": "Volume", + "journalvolume.page.volume": "Том", + + + + // "loading.bitstream": "Loading bitstream...", + "loading.bitstream": "Файл вантажится...", + + // "loading.bitstreams": "Loading bitstreams...", + "loading.bitstreams": "Файли вантажаться...", + + // "loading.browse-by": "Loading items...", + "loading.browse-by": "Документи вантажаться...", + + // "loading.browse-by-page": "Loading page...", + "loading.browse-by-page": "Сторінка вантажится...", + + // "loading.collection": "Loading collection...", + "loading.collection": "Зібрання вантажиться...", + + // "loading.collections": "Loading collections...", + "loading.collections": "Зібрання вантажаться...", + + // "loading.content-source": "Loading content source...", + "loading.content-source": "Джерело даних вантажиться...", + + // "loading.community": "Loading community...", + "loading.community": "Фонд вантажиться...", + + // "loading.default": "Loading...", + "loading.default": "Вантажиться...", + + // "loading.item": "Loading item...", + "loading.item": "Документ вантажиться...", + + // "loading.items": "Loading items...", + "loading.items": "Документи вантажаться...", + + // "loading.mydspace-results": "Loading items...", + "loading.mydspace-results": "Документи вантажаться..", + + // "loading.objects": "Loading...", + "loading.objects": "Вантажиться...", + + // "loading.recent-submissions": "Loading recent submissions...", + "loading.recent-submissions": "Вантажаться останні підписки...", + + // "loading.search-results": "Loading search results...", + "loading.search-results": "Результати вантажаться...", + + // "loading.sub-collections": "Loading sub-collections...", + "loading.sub-collections": "Підзібрання вантажаться...", + + // "loading.sub-communities": "Loading sub-communities...", + "loading.sub-communities": "Підфонди вантажаться...", + + // "loading.top-level-communities": "Loading top-level communities...", + "loading.top-level-communities": "Фонди верхнього рівня вантажаться...", + + + + // "login.form.email": "Email address", + "login.form.email": "Email", + + // "login.form.forgot-password": "Have you forgotten your password?", + "login.form.forgot-password": "Забули пароль?", + + // "login.form.header": "Please log in to DSpace", + "login.form.header": "Увійдіть у репозитарій", + + // "login.form.new-user": "New user? Click here to register.", + "login.form.new-user": "Новий користувач? Зареєструйтесь.", + + // "login.form.or-divider": "or", + "login.form.or-divider": "або", + + // "login.form.password": "Password", + "login.form.password": "Пароль", + + // "login.form.shibboleth": "Log in with Shibboleth", + "login.form.shibboleth": "Увійти через Shibboleth/наразі не підримується/", + + // "login.form.submit": "Log in", + "login.form.submit": "Увійти", + + // "login.title": "Login", + "login.title": "Користувач", + + // "login.breadcrumbs": "Login", + "login.breadcrumbs": "Користувач", + + + + // "logout.form.header": "Log out from DSpace", + "logout.form.header": "Вийти з репозиторію", + + // "logout.form.submit": "Log out", + "logout.form.submit": "Вийти", + + // "logout.title": "Logout", + "logout.title": "Вийти", + + + + // "menu.header.admin": "Admin", + "menu.header.admin": "Адмін", + + // "menu.header.image.logo": "Repository logo", + "menu.header.image.logo": "Логотип репозиторію", + + + + // "menu.section.access_control": "Access Control", + "menu.section.access_control": "Контроль доступу", + + // "menu.section.access_control_authorizations": "Authorizations", + "menu.section.access_control_authorizations": "Авторизація", + + // "menu.section.access_control_groups": "Groups", + "menu.section.access_control_groups": "Групи", + + // "menu.section.access_control_people": "People", + "menu.section.access_control_people": "Користувачі", + + + + // "menu.section.admin_search": "Admin Search", + "menu.section.admin_search": "Пошук адміністратора", + + + + // "menu.section.browse_community": "This Community", + "menu.section.browse_community": "Цей фонд", + + // "menu.section.browse_community_by_author": "By Author", + "menu.section.browse_community_by_author": "За автором", + + // "menu.section.browse_community_by_issue_date": "By Issue Date", + "menu.section.browse_community_by_issue_date": "За датою видання", + + // "menu.section.browse_community_by_title": "By Title", + "menu.section.browse_community_by_title": "За назвою", + + // "menu.section.browse_global": "All of DSpace", + "menu.section.browse_global": "Пошук за критеріями", + + // "menu.section.browse_global_by_author": "By Author", + "menu.section.browse_global_by_author": "За автором", + + // "menu.section.browse_global_by_dateissued": "By Issue Date", + "menu.section.browse_global_by_dateissued": "За датою видання", + + // "menu.section.browse_global_by_subject": "By Subject", + "menu.section.browse_global_by_subject": "За ключовими словами", + + // "menu.section.browse_global_by_title": "By Title", + "menu.section.browse_global_by_title": "За назвою", + + // "menu.section.browse_global_communities_and_collections": "Communities & Collections", + "menu.section.browse_global_communities_and_collections": "Фонди та зібрання", + + + + // "menu.section.control_panel": "Control Panel", + "menu.section.control_panel": "Панель управління", + + // "menu.section.curation_task": "Curation Task", + "menu.section.curation_task": "Поточні завдання", + + + + // "menu.section.edit": "Edit", + "menu.section.edit": "Редагувати", + + // "menu.section.edit_collection": "Collection", + "menu.section.edit_collection": "Зібрання", + + // "menu.section.edit_community": "Community", + "menu.section.edit_community": "Фонд", + + // "menu.section.edit_item": "Item", + "menu.section.edit_item": "Документ", + + + + // "menu.section.export": "Export", + "menu.section.export": "Експорт", + + // "menu.section.export_collection": "Collection", + "menu.section.export_collection": "Зібрання", + + // "menu.section.export_community": "Community", + "menu.section.export_community": "Фонд", + + // "menu.section.export_item": "Item", + "menu.section.export_item": "Документ", + + // "menu.section.export_metadata": "Metadata", + "menu.section.export_metadata": "Метадані", + + + + // "menu.section.icon.access_control": "Access Control menu section", + "menu.section.icon.access_control": "Меню контролю доступу", + + // "menu.section.icon.admin_search": "Admin search menu section", + "menu.section.icon.admin_search": "Меню пошуку адміна", + + // "menu.section.icon.control_panel": "Control Panel menu section", + "menu.section.icon.control_panel": "Меню панелі управління", + + // "menu.section.icon.curation_task": "Curation Task menu section", + "menu.section.icon.curation_task": "Меню поточних завдань", + + // "menu.section.icon.edit": "Edit menu section", + "menu.section.icon.edit": "Редагувати розділ меню", + + // "menu.section.icon.export": "Export menu section", + "menu.section.icon.export": "Експорт розділу меню", + + // "menu.section.icon.find": "Find menu section", + "menu.section.icon.find": "Знайти розділ меню", + + // "menu.section.icon.import": "Import menu section", + "menu.section.icon.import": "Імпорт розділу меню", + + // "menu.section.icon.new": "New menu section", + "menu.section.icon.new": "Новий розділ меню", + + // "menu.section.icon.pin": "Pin sidebar", + "menu.section.icon.pin": "Закріпити бічну панель", + + // "menu.section.icon.processes": "Processes menu section", + + "menu.section.icon.processes": "Процеси розділу меню", + + // "menu.section.icon.registries": "Registries menu section", + "menu.section.icon.registries": "Реєстр розділів меню", + + // "menu.section.icon.statistics_task": "Statistics Task menu section", + "menu.section.icon.statistics_task": "Статистика розділу меню", + + // "menu.section.icon.unpin": "Unpin sidebar", + "menu.section.icon.unpin": "Відкріпити бічну панель", + + + + // "menu.section.import": "Import", + "menu.section.import": "Імпорт", + + // "menu.section.import_batch": "Batch Import (ZIP)", + "menu.section.import_batch": "Імпорт контейнера файлів (ZIP)", + + // "menu.section.import_metadata": "Metadata", + "menu.section.import_metadata": "Метадані", + + + + // "menu.section.new": "New", + "menu.section.new": "Новий", + + // "menu.section.new_collection": "Collection", + "menu.section.new_collection": "Зібрання", + + // "menu.section.new_community": "Community", + "menu.section.new_community": "Фонд", + + // "menu.section.new_item": "Item", + "menu.section.new_item": "Документ", + + // "menu.section.new_item_version": "Item Version", + "menu.section.new_item_version": "Версія документа", + + // "menu.section.new_process": "Process", + + "menu.section.new_process": "Процес", + + + + // "menu.section.pin": "Pin sidebar", + "menu.section.pin": "Закріпити бічне меню", + + // "menu.section.unpin": "Unpin sidebar", + "menu.section.unpin": "Відкріпити бічне меню", + + + + // "menu.section.processes": "Processes", + "menu.section.processes": "Процеси", + + + + // "menu.section.registries": "Registries", + "menu.section.registries": "Реєстри", + + // "menu.section.registries_format": "Format", + "menu.section.registries_format": "Формати", + + // "menu.section.registries_metadata": "Metadata", + "menu.section.registries_metadata": "Метадані", + + + + // "menu.section.statistics": "Statistics", + "menu.section.statistics": "Статистика", + + // "menu.section.statistics_task": "Statistics Task", + "menu.section.statistics_task": "Завдання статистики", + + + + // "menu.section.toggle.access_control": "Toggle Access Control section", + "menu.section.toggle.access_control": "Переключити розділ контролю доступу", + + // "menu.section.toggle.control_panel": "Toggle Control Panel section", + "menu.section.toggle.control_panel": "Переключити панель контролю доступу", + + // "menu.section.toggle.curation_task": "Toggle Curation Task section", + "menu.section.toggle.curation_task": "Переключити розділ завдань", + + // "menu.section.toggle.edit": "Toggle Edit section", + "menu.section.toggle.edit": "Переключити розділ редагування", + + // "menu.section.toggle.export": "Toggle Export section", + "menu.section.toggle.export": "Переключити розділ експорту", + + // "menu.section.toggle.find": "Toggle Find section", + "menu.section.toggle.find": "Переключити розділ пошуку", + + // "menu.section.toggle.import": "Toggle Import section", + "menu.section.toggle.import": "Переключити розділ імпорту", + + // "menu.section.toggle.new": "Toggle New section", + "menu.section.toggle.new": "Переключити новий розділ", + + // "menu.section.toggle.registries": "Toggle Registries section", + "menu.section.toggle.registries": "Переключити розділ реєстрів", + + // "menu.section.toggle.statistics_task": "Toggle Statistics Task section", + "menu.section.toggle.statistics_task": "Переключити розділ статистики", + + + // "menu.section.workflow": "Administer Workflow", + + "menu.section.workflow": "Керування процесу подачі документа", + + + // "mydspace.description": "", + "mydspace.description": "", + + // "mydspace.general.text-here": "here", + + "mydspace.general.text-here": "тут", + + // "mydspace.messages.controller-help": "Select this option to send a message to item's submitter.", + "mydspace.messages.controller-help": "Виберіть цей параметр, щоб надіслати повідомлення відправнику елемента.", + + // "mydspace.messages.description-placeholder": "Insert your message here...", + "mydspace.messages.description-placeholder": "Введіть своє повідомлення ...", + + // "mydspace.messages.hide-msg": "Hide message", + "mydspace.messages.hide-msg": "Приховати повідомлення", + + // "mydspace.messages.mark-as-read": "Mark as read", + "mydspace.messages.mark-as-read": "Позначити як прочитане", + + // "mydspace.messages.mark-as-unread": "Mark as unread", + "mydspace.messages.mark-as-unread": "Позначти як непрочитане", + + // "mydspace.messages.no-content": "No content.", + "mydspace.messages.no-content": "Нема змісту.", + + // "mydspace.messages.no-messages": "No messages yet.", + "mydspace.messages.no-messages": "Повідомлень немає.", + + // "mydspace.messages.send-btn": "Send", + "mydspace.messages.send-btn": "Надіслати", + + // "mydspace.messages.show-msg": "Show message", + "mydspace.messages.show-msg": "Показати повідомлення", + + // "mydspace.messages.subject-placeholder": "Subject...", + "mydspace.messages.subject-placeholder": "Тема...", + + // "mydspace.messages.submitter-help": "Select this option to send a message to controller.", + "mydspace.messages.submitter-help": "Виберіть цю опцію, щоб надіслати повідомлення на перевірку.", + + // "mydspace.messages.title": "Messages", + "mydspace.messages.title": "Повідомлення", + + // "mydspace.messages.to": "To", + "mydspace.messages.to": "До", + + // "mydspace.new-submission": "New submission", + "mydspace.new-submission": "Надійшли нові документи", + + // "mydspace.new-submission-external": "Import metadata from external source", + + "mydspace.new-submission-external": "Імпортувати метадані зі зовнішнього джерела", + + // "mydspace.new-submission-external-short": "Import metadata", + + "mydspace.new-submission-external-short": "Імпортувати метадані", + + // "mydspace.results.head": "Your submissions", + "mydspace.results.head": "Ваші відправлені документи", + + // "mydspace.results.no-abstract": "No Abstract", + "mydspace.results.no-abstract": "Анотація відсутня", + + // "mydspace.results.no-authors": "No Authors", + "mydspace.results.no-authors": "Автори відсутні", + + // "mydspace.results.no-collections": "No Collections", + "mydspace.results.no-collections": "Відсутні зібрання", + + // "mydspace.results.no-date": "No Date", + "mydspace.results.no-date": "Відсутня дата", + + // "mydspace.results.no-files": "No Files", + "mydspace.results.no-files": "Відсутні файли", + + // "mydspace.results.no-results": "There were no items to show", + "mydspace.results.no-results": "Документів не знайдено", + + // "mydspace.results.no-title": "No title", + "mydspace.results.no-title": "Назва відсутня", + + // "mydspace.results.no-uri": "No Uri", + "mydspace.results.no-uri": "Відсутнє покликання", + + // "mydspace.show.workflow": "All tasks", + "mydspace.show.workflow": "Всі завдання", + + // "mydspace.show.workspace": "Your Submissions", + "mydspace.show.workspace": "Ваші надіслані документи ", + + // "mydspace.status.archived": "Archived", + "mydspace.status.archived": "Заархівовані", + + // "mydspace.status.validation": "Validation", + "mydspace.status.validation": "Перевірка", + + // "mydspace.status.waiting-for-controller": "Waiting for controller", + "mydspace.status.waiting-for-controller": "Чекаємо контролера", + + // "mydspace.status.workflow": "Workflow", + "mydspace.status.workflow": "Робочий процес", + + // "mydspace.status.workspace": "Workspace", + "mydspace.status.workspace": "Робоче середовище", + + // "mydspace.title": "MyDSpace", + "mydspace.title": "Моє середовище", + + // "mydspace.upload.upload-failed": "Error creating new workspace. Please verify the content uploaded before retry.", + "mydspace.upload.upload-failed": "Помилка створення нового робочого середовища. Перш ніж повторити спробу, перевірте завантажений вміст.", + + // "mydspace.upload.upload-failed-manyentries": "Unprocessable file. Detected too many entries but allowed only one for file.", + + "mydspace.upload.upload-failed-manyentries": "Файл неможливо опрацювати. Виявлено забагато звернень, але дозволено лише одне для файлу..", + + // "mydspace.upload.upload-failed-moreonefile": "Unprocessable request. Only one file is allowed.", + + "mydspace.upload.upload-failed-moreonefile": "Запит неможливо працювати. Дозволений тільки один файл", + + // "mydspace.upload.upload-multiple-successful": "{{qty}} new workspace items created.", + "mydspace.upload.upload-multiple-successful": "{{qty}} нових робочих середовищ створено.", + + // "mydspace.upload.upload-successful": "New workspace item created. Click {{here}} for edit it.", + "mydspace.upload.upload-successful": "Створено нове робоче середовище. Клацніть {{here}} для редагування.", + + // "mydspace.view-btn": "View", + "mydspace.view-btn": "Перегляд", + + + + // "nav.browse.header": "All of DSpace", + "nav.browse.header": "Пошук за критеріями", + + // "nav.community-browse.header": "By Community", + "nav.community-browse.header": "У фонді", + + // "nav.language": "Language switch", + "nav.language": "Змінити мову", + + // "nav.login": "Log In", + "nav.login": "Увійти", + + // "nav.logout": "Log Out", + "nav.logout": "Вийти", + + // "nav.mydspace": "MyDSpace", + "nav.mydspace": "Моє середовище", + + // "nav.profile": "Profile", + "nav.profile": "Профіль", + + // "nav.search": "Search", + "nav.search": "Пошук", + + // "nav.statistics.header": "Statistics", + "nav.statistics.header": "Статистика", + + // "nav.stop-impersonating": "Stop impersonating EPerson", + + "nav.stop-impersonating": "Припиніть видавати себе за користувача", + + + + // "orgunit.listelement.badge": "Organizational Unit", + "orgunit.listelement.badge": "Організаціна одиниця", + + // "orgunit.page.city": "City", + "orgunit.page.city": "Місто", + + // "orgunit.page.country": "Country", + "orgunit.page.country": "Країна", + + // "orgunit.page.dateestablished": "Date established", + "orgunit.page.dateestablished": "Дата встановлення", + + // "orgunit.page.description": "Description", + "orgunit.page.description": "Опис", + + // "orgunit.page.edit": "Edit this item", + + "orgunit.page.edit": "Редагувати цей документ", + + // "orgunit.page.id": "ID", + "orgunit.page.id": "ID", + + // "orgunit.page.titleprefix": "Organizational Unit: ", + "orgunit.page.titleprefix": "Організаціна одиниця: ", + + + + // "pagination.results-per-page": "Results Per Page", + "pagination.results-per-page": "Результатів на сторінці", + + // "pagination.showing.detail": "{{ range }} of {{ total }}", + "pagination.showing.detail": "{{ range }} з {{ total }}", + + // "pagination.showing.label": "Now showing ", + "pagination.showing.label": "Зараз показуємо ", + + // "pagination.sort-direction": "Sort Options", + "pagination.sort-direction": "Налаштування сортування", + + + + // "person.listelement.badge": "Person", + "person.listelement.badge": "Користувач", + + // "person.listelement.no-title": "No name found", + + "person.listelement.no-title": "Ім'я не знайдено", + + // "person.page.birthdate": "Birth Date", + "person.page.birthdate": "Дата народження", + + // "person.page.edit": "Edit this item", + + "person.page.edit": "Редагувати документ", + + // "person.page.email": "Email Address", + "person.page.email": "Email", + + // "person.page.firstname": "First Name", + "person.page.firstname": "Ім'я", + + // "person.page.jobtitle": "Job Title", + "person.page.jobtitle": "Посада", + + // "person.page.lastname": "Last Name", + "person.page.lastname": "Прізвище", + + // "person.page.link.full": "Show all metadata", + "person.page.link.full": "Показати всі метадані", + + // "person.page.orcid": "ORCID", + "person.page.orcid": "ORCID", + + // "person.page.staffid": "Staff ID", + "person.page.staffid": "Персональний ID", + + // "person.page.titleprefix": "Person: ", + "person.page.titleprefix": "Користувач: ", + + // "person.search.results.head": "Person Search Results", + "person.search.results.head": "Результати пошуку користувача", + + // "person.search.title": "DSpace Angular :: Person Search", + "person.search.title": "Репозиторій :: Користувацький пошук", + + + + // "process.new.select-parameters": "Parameters", + "process.new.select-parameters": "Параметри", + + // "process.new.cancel": "Cancel", + "process.new.cancel": "Відмінити", + + // "process.new.submit": "Submit", + "process.new.submit": "Надіслати", + + // "process.new.select-script": "Script", + "process.new.select-script": "Скрипт", + + // "process.new.select-script.placeholder": "Choose a script...", + "process.new.select-script.placeholder": "Виберіть скрипт...", + + // "process.new.select-script.required": "Script is required", + "process.new.select-script.required": "Потрібно вибрати скрипт", + + // "process.new.parameter.file.upload-button": "Select file...", + "process.new.parameter.file.upload-button": "Виберіть файл...", + + // "process.new.parameter.file.required": "Please select a file", + "process.new.parameter.file.required": "Виберіть файл", + + // "process.new.parameter.string.required": "Parameter value is required", + "process.new.parameter.string.required": "Цей параметр є обв'язковим", + + // "process.new.parameter.type.value": "value", + "process.new.parameter.type.value": "значення", + + // "process.new.parameter.type.file": "file", + "process.new.parameter.type.file": "файл", + + // "process.new.parameter.required.missing": "The following parameters are required but still missing:", + "process.new.parameter.required.missing": "Наступні параметри є обов’язковими, але все ще відсутні:", + + // "process.new.notification.success.title": "Success", + "process.new.notification.success.title": "Успіх", + + // "process.new.notification.success.content": "The process was successfully created", + "process.new.notification.success.content": "Процес успішно запущено", + + // "process.new.notification.error.title": "Error", + "process.new.notification.error.title": "Виникла якась помилка, бодай би вона скисла", + + // "process.new.notification.error.content": "An error occurred while creating this process", + "process.new.notification.error.content": "Виникла помилка при створенні процесу", + + // "process.new.header": "Create a new process", + "process.new.header": "Створити новий процес", + + // "process.new.title": "Create a new process", + "process.new.title": "Створити новий процес", + + // "process.new.breadcrumbs": "Create a new process", + "process.new.breadcrumbs": "Створити новий процес", + + + + // "process.detail.arguments" : "Arguments", + "process.detail.arguments" : "Аргументи", + + // "process.detail.arguments.empty" : "This process doesn't contain any arguments", + "process.detail.arguments.empty" : "Процес не містить жодних аргументів", + + // "process.detail.back" : "Back", + "process.detail.back" : "Повернутись", + + // "process.detail.output" : "Process Output", + "process.detail.output" : "Прооцес завершено", + + // "process.detail.logs.button": "Retrieve process output", + "process.detail.logs.button": "Отримати вихідні дані процесу", + + // "process.detail.logs.loading": "Retrieving", + "process.detail.logs.loading": "Отримання", + + // "process.detail.logs.none": "This process has no output", + "process.detail.logs.none": "Процес не генерує вихідних даних", + + // "process.detail.output-files" : "Output Files", + "process.detail.output-files" : "Вихідні файли", + + // "process.detail.output-files.empty" : "This process doesn't contain any output files", + "process.detail.output-files.empty" : "Процес не містить жодних вихдних файлів", + + // "process.detail.script" : "Script", + "process.detail.script" : "Скрипт", + + // "process.detail.title" : "Process: {{ id }} - {{ name }}", + "process.detail.title" : "Процеси: {{ id }} - {{ name }}", + + // "process.detail.start-time" : "Start time", + "process.detail.start-time" : "Дата старту", + + // "process.detail.end-time" : "Finish time", + "process.detail.end-time" : "Дата завершення", + + // "process.detail.status" : "Status", + "process.detail.status" : "Статус", + + // "process.detail.create" : "Create similar process", + "process.detail.create" : "Створіть аналогічний процес", + + + + // "process.overview.table.finish" : "Finish time", + "process.overview.table.finish" : "Дата завершення", + + // "process.overview.table.id" : "Process ID", + "process.overview.table.id" : "ID процесу", + + // "process.overview.table.name" : "Name", + "process.overview.table.name" : "Назва", + + // "process.overview.table.start" : "Start time", + "process.overview.table.start" : "Дата старту", + + // "process.overview.table.status" : "Status", + "process.overview.table.status" : "Статус", + + // "process.overview.table.user" : "User", + "process.overview.table.user" : "Користувач", + + // "process.overview.title": "Processes Overview", + "process.overview.title": "Огляд процесів", + + // "process.overview.breadcrumbs": "Processes Overview", + "process.overview.breadcrumbs": "Огляд процесів", + + // "process.overview.new": "New", + "process.overview.new": "Новий", + + + // "profile.breadcrumbs": "Update Profile", + "profile.breadcrumbs": "Оновити профіль", + + // "profile.card.identify": "Identify", + "profile.card.identify": "Ідентифікувати", + + // "profile.card.security": "Security", + "profile.card.security": "Безпека", + + // "profile.form.submit": "Update Profile", + "profile.form.submit": "Оновити профіль", + + // "profile.groups.head": "Authorization groups you belong to", + "profile.groups.head": "Групи до яких Ви належите", + + // "profile.head": "Update Profile", + "profile.head": "Оновити профіль", + + // "profile.metadata.form.error.firstname.required": "First Name is required", + "profile.metadata.form.error.firstname.required": "Ім'я є обов'язковим полем", + + // "profile.metadata.form.error.lastname.required": "Last Name is required", + "profile.metadata.form.error.lastname.required": "Прізвище є обов'язковим полем", + + // "profile.metadata.form.label.email": "Email Address", + "profile.metadata.form.label.email": "Email", + + // "profile.metadata.form.label.firstname": "First Name", + "profile.metadata.form.label.firstname": "Ім'я", + + // "profile.metadata.form.label.language": "Language", + "profile.metadata.form.label.language": "Мова", + + // "profile.metadata.form.label.lastname": "Last Name", + "profile.metadata.form.label.lastname": "Прізвище", + + // "profile.metadata.form.label.phone": "Contact Telephone", + "profile.metadata.form.label.phone": "Номер телефону", + + // "profile.metadata.form.notifications.success.content": "Your changes to the profile were saved.", + "profile.metadata.form.notifications.success.content": "Зміни до профілю збережені.", + + // "profile.metadata.form.notifications.success.title": "Profile saved", + "profile.metadata.form.notifications.success.title": "Профіль збережено", + + // "profile.notifications.warning.no-changes.content": "No changes were made to the Profile.", + "profile.notifications.warning.no-changes.content": "Не було змін до профілю.", + + // "profile.notifications.warning.no-changes.title": "No changes", + "profile.notifications.warning.no-changes.title": "Без змін", + + // "profile.security.form.error.matching-passwords": "The passwords do not match.", + "profile.security.form.error.matching-passwords": "Паролі не співпадають.", + + // "profile.security.form.error.password-length": "The password should be at least 6 characters long.", + "profile.security.form.error.password-length": "Довжина пароля повинна становити не менше 6 символів.", + + // "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. It should be at least six characters long.", + "profile.security.form.info": "За бажанням ви можете ввести новий пароль у поле нижче та підтвердити його, ввівши його ще раз у друге поле. Він має містити щонайменше шість символів.", + + // "profile.security.form.label.password": "Password", + "profile.security.form.label.password": "Пароль", + + // "profile.security.form.label.passwordrepeat": "Retype to confirm", + "profile.security.form.label.passwordrepeat": "Повторіть", + + // "profile.security.form.notifications.success.content": "Your changes to the password were saved.", + "profile.security.form.notifications.success.content": "Зміни до паролю збережені.", + + // "profile.security.form.notifications.success.title": "Password saved", + "profile.security.form.notifications.success.title": "Пароль збережено", + + // "profile.security.form.notifications.error.title": "Error changing passwords", + "profile.security.form.notifications.error.title": "Помилка зміни пароля", + + // "profile.security.form.notifications.error.not-long-enough": "The password has to be at least 6 characters long.", + "profile.security.form.notifications.error.not-long-enough": "Пароль повинен містити щонайменше 6 символів.", + + // "profile.security.form.notifications.error.not-same": "The provided passwords are not the same.", + "profile.security.form.notifications.error.not-same": "Паролі не співпадають.", + + // "profile.title": "Update Profile", + "profile.title": "Оновити профіль", + + + + // "project.listelement.badge": "Research Project", + "project.listelement.badge": "Дослідницький проект", + + // "project.page.contributor": "Contributors", + "project.page.contributor": "Дописувачі", + + // "project.page.description": "Description", + "project.page.description": "Опис", + + // "project.page.edit": "Edit this item", + + "project.page.edit": "Редагувати документ", + + // "project.page.expectedcompletion": "Expected Completion", + "project.page.expectedcompletion": "Очікуване завершеня", + + // "project.page.funder": "Funders", + "project.page.funder": "Фундатори", + + // "project.page.id": "ID", + "project.page.id": "ID", + + // "project.page.keyword": "Keywords", + "project.page.keyword": "Ключові слова", + + // "project.page.status": "Status", + "project.page.status": "Статус", + + // "project.page.titleprefix": "Research Project: ", + "project.page.titleprefix": "Досліницький проект: ", + + // "project.search.results.head": "Project Search Results", + "project.search.results.head": "Результати пошуку за проектом", + + + + // "publication.listelement.badge": "Publication", + "publication.listelement.badge": "Публікація", + + // "publication.page.description": "Description", + "publication.page.description": "Опис", + + // "publication.page.edit": "Edit this item", + "publication.page.edit": "Редагувати документ", + + // "publication.page.journal-issn": "Journal ISSN", + "publication.page.journal-issn": "ISSN", + + // "publication.page.journal-title": "Journal Title", + "publication.page.journal-title": "Назва видання", + + // "publication.page.publisher": "Publisher", + "publication.page.publisher": "Видання", + + // "publication.page.titleprefix": "Publication: ", + "publication.page.titleprefix": "Публікація: ", + + // "publication.page.volume-title": "Volume Title", + "publication.page.volume-title": "Назва тому", + + // "publication.search.results.head": "Publication Search Results", + "publication.search.results.head": "Результати пошуку", + + // "publication.search.title": "DSpace Angular :: Publication Search", + "publication.search.title": "Репозиторій :: пощук публікацій", + + + // "register-email.title": "New user registration", + "register-email.title": "Реєстрація нового користувача", + + // "register-page.create-profile.header": "Create Profile", + "register-page.create-profile.header": "Створити профіль", + + // "register-page.create-profile.identification.header": "Identify", + "register-page.create-profile.identification.header": "Ідентифікувати", + + // "register-page.create-profile.identification.email": "Email Address", + "register-page.create-profile.identification.email": "Email", + + // "register-page.create-profile.identification.first-name": "First Name *", + "register-page.create-profile.identification.first-name": "Ім'я *", + + // "register-page.create-profile.identification.first-name.error": "Please fill in a First Name", + "register-page.create-profile.identification.first-name.error": "Заповніть поле імені", + + // "register-page.create-profile.identification.last-name": "Last Name *", + + "register-page.create-profile.identification.last-name": "Прізвище *", + + // "register-page.create-profile.identification.last-name.error": "Please fill in a Last Name", + + "register-page.create-profile.identification.last-name.error": "Заповніть поле прізвища", + + // "register-page.create-profile.identification.contact": "Contact Telephone", + + "register-page.create-profile.identification.contact": "Номер телефону", + + // "register-page.create-profile.identification.language": "Language", + + "register-page.create-profile.identification.language": "Мова", + + // "register-page.create-profile.security.header": "Security", + + "register-page.create-profile.security.header": "Безпека", + + // "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. It should be at least six characters long.", + + "register-page.create-profile.security.info": "Будь ласка, введіть пароль у поле нижче та підтвердьте його, ввівши його ще раз у друге поле. Він має містити щонайменше шість символів.", + + // "register-page.create-profile.security.label.password": "Password *", + + "register-page.create-profile.security.label.password": "Пароль *", + + // "register-page.create-profile.security.label.passwordrepeat": "Retype to confirm *", + + "register-page.create-profile.security.label.passwordrepeat": "Повторіть для ідтвердження *", + + // "register-page.create-profile.security.error.empty-password": "Please enter a password in the box below.", + + "register-page.create-profile.security.error.empty-password": "Введіть пароль у поле нижче.", + + // "register-page.create-profile.security.error.matching-passwords": "The passwords do not match.", + + "register-page.create-profile.security.error.matching-passwords": "Паролі не співпадають.", + + // "register-page.create-profile.security.error.password-length": "The password should be at least 6 characters long.", + + "register-page.create-profile.security.error.password-length": "Довжина пароля повинна бути не менше 6 символів.", + + // "register-page.create-profile.submit": "Complete Registration", + + "register-page.create-profile.submit": "Повна реєстрація", + + // "register-page.create-profile.submit.error.content": "Something went wrong while registering a new user.", + + "register-page.create-profile.submit.error.content": "Щось пішло не так при реєстрації нового користувача.", + + // "register-page.create-profile.submit.error.head": "Registration failed", + + "register-page.create-profile.submit.error.head": "Реєстрація не вдалась", + + // "register-page.create-profile.submit.success.content": "The registration was successful. You have been logged in as the created user.", + + "register-page.create-profile.submit.success.content": "Реєстрація пройшла успішно. Ви увійшли як новий користувач.", + + // "register-page.create-profile.submit.success.head": "Registration completed", + + "register-page.create-profile.submit.success.head": "Реєстрацію завершено", + + + // "register-page.registration.header": "New user registration", + + "register-page.registration.header": "Реєстрація нового користувача", + + // "register-page.registration.info": "Register an account to subscribe to collections for email updates, and submit new items to DSpace.", + + "register-page.registration.info": "Зареєструйтесь для отримання сповіщень про нові надходження та отримайте можливіть надсилати нові документи у репозиторій.", + + // "register-page.registration.email": "Email Address *", + + "register-page.registration.email": "Email *", + + // "register-page.registration.email.error.required": "Please fill in an email address", + + "register-page.registration.email.error.required": "Заповніть поле email", + + // "register-page.registration.email.error.pattern": "Please fill in a valid email address", + + "register-page.registration.email.error.pattern": "Введіть правильну емейл адресу", + + // "register-page.registration.email.hint": "This address will be verified and used as your login name.", + + "register-page.registration.email.hint": "Використовуйие цю адресу щоб увійти у репозиторій. Але перед тим вона буде перевірена", + + // "register-page.registration.submit": "Register", + + "register-page.registration.submit": "Реєстрація", + + // "register-page.registration.success.head": "Verification email sent", + + "register-page.registration.success.head": "email для підтвердження Вам надіслано", + + // "register-page.registration.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", + + "register-page.registration.success.content": "email для підтвердження Вам надіслано на вказану скриньку {{ email }}. Там Ви знайдете покликання для підтвердження адреми та подальші кроки.", + + // "register-page.registration.error.head": "Error when trying to register email", + + "register-page.registration.error.head": "Виникла помилка при реєстрації email", + + // "register-page.registration.error.content": "An error occured when registering the following email address: {{ email }}", + + "register-page.registration.error.content": "Виникла помилка при реєстрації email: {{ email }}", + + + + // "relationships.add.error.relationship-type.content": "No suitable match could be found for relationship type {{ type }} between the two items", + // TODO New key - Add a translation + "relationships.add.error.relationship-type.content": "Не вдалося знайти відповідний тип відносин {{ type }} між двома документами", + + // "relationships.add.error.server.content": "The server returned an error", + + "relationships.add.error.server.content": "Сервер повідомив про помилку", + + // "relationships.add.error.title": "Unable to add relationship", + + "relationships.add.error.title": "Неможливо додати зв'язок", + + // "relationships.isAuthorOf": "Authors", + "relationships.isAuthorOf": "Автори", + + // "relationships.isAuthorOf.Person": "Authors (persons)", + + "relationships.isAuthorOf.Person": "Автори", + + // "relationships.isAuthorOf.OrgUnit": "Authors (organizational units)", + + "relationships.isAuthorOf.OrgUnit": "Автори (структурна одиниця, наприклад університет)", + + // "relationships.isIssueOf": "Journal Issues", + "relationships.isIssueOf": "Випуски видання", + + // "relationships.isJournalIssueOf": "Journal Issue", + "relationships.isJournalIssueOf": "Випуск видання", + + // "relationships.isJournalOf": "Journals", + "relationships.isJournalOf": "Видання", + + // "relationships.isOrgUnitOf": "Organizational Units", + "relationships.isOrgUnitOf": "Структурні одиниці", + + // "relationships.isPersonOf": "Authors", + "relationships.isPersonOf": "Автори", + + // "relationships.isProjectOf": "Research Projects", + "relationships.isProjectOf": "Дослідницькі проекти", + + // "relationships.isPublicationOf": "Publications", + "relationships.isPublicationOf": "Публікації", + + // "relationships.isPublicationOfJournalIssue": "Articles", + "relationships.isPublicationOfJournalIssue": "Публікації", + + // "relationships.isSingleJournalOf": "Journal", + "relationships.isSingleJournalOf": "Видання", + + // "relationships.isSingleVolumeOf": "Journal Volume", + "relationships.isSingleVolumeOf": "Том видання", + + // "relationships.isVolumeOf": "Journal Volumes", + "relationships.isVolumeOf": "Томи видання", + + // "relationships.isContributorOf": "Contributors", + "relationships.isContributorOf": "Дописувачі", + + + + // "resource-policies.add.button": "Add", + + "resource-policies.add.button": "Додати", + + // "resource-policies.add.for.": "Add a new policy", + + "resource-policies.add.for.": "Додати нову політику", + + // "resource-policies.add.for.bitstream": "Add a new Bitstream policy", + + "resource-policies.add.for.bitstream": "Додати нову політику для файлу", + + // "resource-policies.add.for.bundle": "Add a new Bundle policy", + + "resource-policies.add.for.bundle": "AДодати нову політику для контейнеру файлів", + + // "resource-policies.add.for.item": "Add a new Item policy", + + "resource-policies.add.for.item": "Додати нову політику для документа", + + // "resource-policies.add.for.community": "Add a new Community policy", + + "resource-policies.add.for.community": "Додати нову політику для фонду", + + // "resource-policies.add.for.collection": "Add a new Collection policy", + + "resource-policies.add.for.collection": "Додати нову політику для зібрання", + + // "resource-policies.create.page.heading": "Create new resource policy for ", + + "resource-policies.create.page.heading": "Створити нову ресурсну політику для ", + + // "resource-policies.create.page.failure.content": "An error occurred while creating the resource policy.", + + "resource-policies.create.page.failure.content": "Виникла помилка при створенні ресурсної політики.", + + // "resource-policies.create.page.success.content": "Operation successful", + + "resource-policies.create.page.success.content": "Операція пройшла успішно", + + // "resource-policies.create.page.title": "Create new resource policy", + + "resource-policies.create.page.title": "Створити нову ресурсну політику", + + // "resource-policies.delete.btn": "Delete selected", + + "resource-policies.delete.btn": "Видалити вибране", + + // "resource-policies.delete.btn.title": "Delete selected resource policies", + + "resource-policies.delete.btn.title": "Видалити вибрані ресурсні політики", + + // "resource-policies.delete.failure.content": "An error occurred while deleting selected resource policies.", + + "resource-policies.delete.failure.content": "Виникла помилка при видаленні вибраних ресурсних політик.", + + // "resource-policies.delete.success.content": "Операція пройшла успішно", + + "resource-policies.delete.success.content": "Операція пройшла успішно", + + // "resource-policies.edit.page.heading": "Edit resource policy ", + + "resource-policies.edit.page.heading": "Редагувати ресурсну політику ", + + // "resource-policies.edit.page.failure.content": "An error occurred while editing the resource policy.", + + "resource-policies.edit.page.failure.content": "Виникла помилка при редагуванні ресурсної політики.", + + // "resource-policies.edit.page.success.content": "Operation successful", + + "resource-policies.edit.page.success.content": "Операція пройшла успішно", + + // "resource-policies.edit.page.title": "Edit resource policy", + + "resource-policies.edit.page.title": "Редагувати ресурсну політику", + + // "resource-policies.form.action-type.label": "Select the action type", + + "resource-policies.form.action-type.label": "Виберіть тип дії", + + // "resource-policies.form.action-type.required": "You must select the resource policy action.", + + "resource-policies.form.action-type.required": "Ви повинні вибрати дію політики ресурсів.", + + // "resource-policies.form.eperson-group-list.label": "The eperson or group that will be granted the permission", + + "resource-policies.form.eperson-group-list.label": "Особа чи група, якій буде надано дозвіл", + + // "resource-policies.form.eperson-group-list.select.btn": "Select", + + "resource-policies.form.eperson-group-list.select.btn": "Вибрати", + + // "resource-policies.form.eperson-group-list.tab.eperson": "Search for a ePerson", + + "resource-policies.form.eperson-group-list.tab.eperson": "Вибрати користувача", + + // "resource-policies.form.eperson-group-list.tab.group": "Search for a group", + + "resource-policies.form.eperson-group-list.tab.group": "Шукати групу", + + // "resource-policies.form.eperson-group-list.table.headers.action": "Action", + + "resource-policies.form.eperson-group-list.table.headers.action": "Дія", + + // "resource-policies.form.eperson-group-list.table.headers.id": "ID", + // TODO New key - Add a translation + "resource-policies.form.eperson-group-list.table.headers.id": "ID", + + // "resource-policies.form.eperson-group-list.table.headers.name": "Name", + + "resource-policies.form.eperson-group-list.table.headers.name": "Назва", + + // "resource-policies.form.date.end.label": "End Date", + + "resource-policies.form.date.end.label": "Кінцева дата", + + // "resource-policies.form.date.start.label": "Start Date", + + "resource-policies.form.date.start.label": "Дата початку", + + // "resource-policies.form.description.label": "Description", + + "resource-policies.form.description.label": "Опис", + + // "resource-policies.form.name.label": "Name", + + "resource-policies.form.name.label": "Назва", + + // "resource-policies.form.policy-type.label": "Select the policy type", + + "resource-policies.form.policy-type.label": "Виберіть тип політики", + + // "resource-policies.form.policy-type.required": "You must select the resource policy type.", + + "resource-policies.form.policy-type.required": "Спочатку ви повинні вибрати тип політики ресурсів.", + + // "resource-policies.table.headers.action": "Action", + + "resource-policies.table.headers.action": "Дія", + + // "resource-policies.table.headers.date.end": "End Date", + + "resource-policies.table.headers.date.end": "Кінцева дата", + + // "resource-policies.table.headers.date.start": "Start Date", + + "resource-policies.table.headers.date.start": "Дата початку", + + // "resource-policies.table.headers.edit": "Edit", + + "resource-policies.table.headers.edit": "Редагувати", + + // "resource-policies.table.headers.edit.group": "Edit group", + + "resource-policies.table.headers.edit.group": "Редагувати групу", + + // "resource-policies.table.headers.edit.policy": "Edit policy", + + "resource-policies.table.headers.edit.policy": "Редагувати політику", + + // "resource-policies.table.headers.eperson": "EPerson", + + "resource-policies.table.headers.eperson": "Користувач", + + // "resource-policies.table.headers.group": "Group", + + "resource-policies.table.headers.group": "Група", + + // "resource-policies.table.headers.id": "ID", + + "resource-policies.table.headers.id": "ID", + + // "resource-policies.table.headers.name": "Name", + + "resource-policies.table.headers.name": "Назва", + + // "resource-policies.table.headers.policyType": "type", + + "resource-policies.table.headers.policyType": "Тип", + + // "resource-policies.table.headers.title.for.bitstream": "Policies for Bitstream", + + "resource-policies.table.headers.title.for.bitstream": "Політики для файла", + + // "resource-policies.table.headers.title.for.bundle": "Policies for Bundle", + + "resource-policies.table.headers.title.for.bundle": "Політики для контейнера файлів", + + // "resource-policies.table.headers.title.for.item": "Policies for Item", + + "resource-policies.table.headers.title.for.item": "Політики для документа", + + // "resource-policies.table.headers.title.for.community": "Policies for Community", + + "resource-policies.table.headers.title.for.community": "Політики для фонду", + + // "resource-policies.table.headers.title.for.collection": "Policies for Collection", + + "resource-policies.table.headers.title.for.collection": "Політики для зібрання", + + + + // "search.description": "", + "search.description": "", + + // "search.switch-configuration.title": "Show", + "search.switch-configuration.title": "Показати", + + // "search.title": "DSpace Angular :: Search", + "search.title": "Репозиторій :: Пошук", + + // "search.breadcrumbs": "Search", + "search.breadcrumbs": "Шукати", + + + // "search.filters.applied.f.author": "Author", + "search.filters.applied.f.author": "Автор", + + // "search.filters.applied.f.dateIssued.max": "End date", + "search.filters.applied.f.dateIssued.max": "Кінцева дата", + + // "search.filters.applied.f.dateIssued.min": "Start date", + "search.filters.applied.f.dateIssued.min": "Дата початку", + + // "search.filters.applied.f.dateSubmitted": "Date submitted", + "search.filters.applied.f.dateSubmitted": "Дата надсилання", + + // "search.filters.applied.f.discoverable": "Private", + "search.filters.applied.f.discoverable": "Закритий", + + // "search.filters.applied.f.entityType": "Item Type", + "search.filters.applied.f.entityType": "Тип документа", + + // "search.filters.applied.f.has_content_in_original_bundle": "Has files", + "search.filters.applied.f.has_content_in_original_bundle": "Містить файли", + + // "search.filters.applied.f.itemtype": "Type", + "search.filters.applied.f.itemtype": "Тип", + + // "search.filters.applied.f.namedresourcetype": "Status", + "search.filters.applied.f.namedresourcetype": "Статус", + + // "search.filters.applied.f.subject": "Subject", + "search.filters.applied.f.subject": "Ключові слова", + + // "search.filters.applied.f.submitter": "Submitter", + "search.filters.applied.f.submitter": "Надсилач", + + // "search.filters.applied.f.jobTitle": "Job Title", + "search.filters.applied.f.jobTitle": "Посада", + + // "search.filters.applied.f.birthDate.max": "End birth date", + "search.filters.applied.f.birthDate.max": "End birth date", + + // "search.filters.applied.f.birthDate.min": "Start birth date", + "search.filters.applied.f.birthDate.min": "Start birth date", + + // "search.filters.applied.f.withdrawn": "Withdrawn", + "search.filters.applied.f.withdrawn": "Вилучено", + + + + // "search.filters.filter.author.head": "Author", + "search.filters.filter.author.head": "Автор", + + // "search.filters.filter.author.placeholder": "Author name", + "search.filters.filter.author.placeholder": "Ім'я автора", + + // "search.filters.filter.birthDate.head": "Birth Date", + "search.filters.filter.birthDate.head": "Дата народження", + + // "search.filters.filter.birthDate.placeholder": "Birth Date", + "search.filters.filter.birthDate.placeholder": "Дата народження", + + // "search.filters.filter.creativeDatePublished.head": "Date Published", + "search.filters.filter.creativeDatePublished.head": "Дата публікації", + + // "search.filters.filter.creativeDatePublished.placeholder": "Date Published", + "search.filters.filter.creativeDatePublished.placeholder": "Дата публікації", + + // "search.filters.filter.creativeWorkEditor.head": "Editor", + "search.filters.filter.creativeWorkEditor.head": "Редактор", + + // "search.filters.filter.creativeWorkEditor.placeholder": "Editor", + "search.filters.filter.creativeWorkEditor.placeholder": "Редактор", + + // "search.filters.filter.creativeWorkKeywords.head": "Subject", + "search.filters.filter.creativeWorkKeywords.head": "Ключові слова", + + // "search.filters.filter.creativeWorkKeywords.placeholder": "Subject", + "search.filters.filter.creativeWorkKeywords.placeholder": "Ключові слова", + + // "search.filters.filter.creativeWorkPublisher.head": "Publisher", + "search.filters.filter.creativeWorkPublisher.head": "Видавець", + + // "search.filters.filter.creativeWorkPublisher.placeholder": "Publisher", + "search.filters.filter.creativeWorkPublisher.placeholder": "Видавець", + + // "search.filters.filter.dateIssued.head": "Date", + "search.filters.filter.dateIssued.head": "Дата", + + // "search.filters.filter.dateIssued.max.placeholder": "Minimum Date", + "search.filters.filter.dateIssued.max.placeholder": "Мінімальна дата", + + // "search.filters.filter.dateIssued.min.placeholder": "Maximum Date", + "search.filters.filter.dateIssued.min.placeholder": "Максимальна дата", + + // "search.filters.filter.dateSubmitted.head": "Date submitted", + "search.filters.filter.dateSubmitted.head": "Дата надсилання", + + // "search.filters.filter.dateSubmitted.placeholder": "Date submitted", + "search.filters.filter.dateSubmitted.placeholder": "Дата надсилання", + + // "search.filters.filter.discoverable.head": "Private", + "search.filters.filter.discoverable.head": "Закритий", + + // "search.filters.filter.withdrawn.head": "Withdrawn", + "search.filters.filter.withdrawn.head": "Вилучено", + + // "search.filters.filter.entityType.head": "Item Type", + "search.filters.filter.entityType.head": "Тип документа", + + // "search.filters.filter.entityType.placeholder": "Item Type", + "search.filters.filter.entityType.placeholder": "Тип документа", + + // "search.filters.filter.has_content_in_original_bundle.head": "Has files", + "search.filters.filter.has_content_in_original_bundle.head": "Містить файли", + + // "search.filters.filter.itemtype.head": "Type", + "search.filters.filter.itemtype.head": "Тип", + + // "search.filters.filter.itemtype.placeholder": "Type", + "search.filters.filter.itemtype.placeholder": "Тип", + + // "search.filters.filter.jobTitle.head": "Job Title", + "search.filters.filter.jobTitle.head": "Посада", + + // "search.filters.filter.jobTitle.placeholder": "Job Title", + "search.filters.filter.jobTitle.placeholder": "Посада", + + // "search.filters.filter.knowsLanguage.head": "Known language", + "search.filters.filter.knowsLanguage.head": "Known language", + + // "search.filters.filter.knowsLanguage.placeholder": "Known language", + "search.filters.filter.knowsLanguage.placeholder": "Known language", + + // "search.filters.filter.namedresourcetype.head": "Status", + "search.filters.filter.namedresourcetype.head": "Статус", + + // "search.filters.filter.namedresourcetype.placeholder": "Status", + "search.filters.filter.namedresourcetype.placeholder": "Статус", + + // "search.filters.filter.objectpeople.head": "People", + "search.filters.filter.objectpeople.head": "Користувачі", + + // "search.filters.filter.objectpeople.placeholder": "People", + "search.filters.filter.objectpeople.placeholder": "Користувачі", + + // "search.filters.filter.organizationAddressCountry.head": "Country", + "search.filters.filter.organizationAddressCountry.head": "Країна", + + // "search.filters.filter.organizationAddressCountry.placeholder": "Country", + "search.filters.filter.organizationAddressCountry.placeholder": "Країна", + + // "search.filters.filter.organizationAddressLocality.head": "City", + "search.filters.filter.organizationAddressLocality.head": "Місто", + + // "search.filters.filter.organizationAddressLocality.placeholder": "City", + "search.filters.filter.organizationAddressLocality.placeholder": "Місто", + + // "search.filters.filter.organizationFoundingDate.head": "Date Founded", + "search.filters.filter.organizationFoundingDate.head": "Дата заснування", + + // "search.filters.filter.organizationFoundingDate.placeholder": "Date Founded", + "search.filters.filter.organizationFoundingDate.placeholder": "Дата заснування", + + // "search.filters.filter.scope.head": "Scope", + "search.filters.filter.scope.head": "Область застосування", + + // "search.filters.filter.scope.placeholder": "Scope filter", + "search.filters.filter.scope.placeholder": "Фільтр області застосування", + + // "search.filters.filter.show-less": "Collapse", + "search.filters.filter.show-less": "Згорнути", + + // "search.filters.filter.show-more": "Show more", + "search.filters.filter.show-more": "Детальніше", + + // "search.filters.filter.subject.head": "Subject", + "search.filters.filter.subject.head": "Ключові слова", + + // "search.filters.filter.subject.placeholder": "Subject", + "search.filters.filter.subject.placeholder": "Ключові слова", + + // "search.filters.filter.submitter.head": "Submitter", + "search.filters.filter.submitter.head": "Користувач, який надсилає", + + // "search.filters.filter.submitter.placeholder": "Submitter", + "search.filters.filter.submitter.placeholder": "Користувач, який надсилає", + + + + // "search.filters.entityType.JournalIssue": "Journal Issue", + "search.filters.entityType.JournalIssue": "Випуск видання", + + // "search.filters.entityType.JournalVolume": "Journal Volume", + "search.filters.entityType.JournalVolume": "Том видання", + + // "search.filters.entityType.OrgUnit": "Organizational Unit", + "search.filters.entityType.OrgUnit": "Структурна одиниця", + + // "search.filters.has_content_in_original_bundle.true": "Yes", + "search.filters.has_content_in_original_bundle.true": "Так", + + // "search.filters.has_content_in_original_bundle.false": "No", + "search.filters.has_content_in_original_bundle.false": "Ні", + + // "search.filters.discoverable.true": "No", + "search.filters.discoverable.true": "Ні", + + // "search.filters.discoverable.false": "Yes", + "search.filters.discoverable.false": "Так", + + // "search.filters.withdrawn.true": "Yes", + "search.filters.withdrawn.true": "Так", + + // "search.filters.withdrawn.false": "No", + "search.filters.withdrawn.false": "Ні", + + + // "search.filters.head": "Filters", + "search.filters.head": "Фільтри", + + // "search.filters.reset": "Reset filters", + "search.filters.reset": "Скинути фільтри", + + + + // "search.form.search": "Search", + "search.form.search": "Пошук", + + // "search.form.search_dspace": "Search DSpace", + "search.form.search_dspace": "Пошук у репозиторії", + + // "search.form.search_mydspace": "Search MyDSpace", + "search.form.search_mydspace": "Шукати у своїх документах", + + + + // "search.results.head": "Search Results", + "search.results.head": "Результати пошуку", + + // "search.results.no-results": "Your search returned no results. Having trouble finding what you're looking for? Try putting", + "search.results.no-results": "Ваш пошук не дав результатів. Вам важко знайти те, що ви шукаєте? Спробуйте поставити", + + // "search.results.no-results-link": "quotes around it", + "search.results.no-results-link": "лапки навколо запиту", + + // "search.results.empty": "Your search returned no results.", + "search.results.empty": "Ваш пошук не дав результатів.", + + + + // "search.sidebar.close": "Back to results", + "search.sidebar.close": "Повернутись до результатів", + + // "search.sidebar.filters.title": "Filters", + "search.sidebar.filters.title": "Фільтри", + + // "search.sidebar.open": "Search Tools", + "search.sidebar.open": "Засоби пошуку", + + // "search.sidebar.results": "results", + "search.sidebar.results": "результатів", + + // "search.sidebar.settings.rpp": "Results per page", + "search.sidebar.settings.rpp": "Результатів на сторінку", + + // "search.sidebar.settings.sort-by": "Sort By", + "search.sidebar.settings.sort-by": "Сортувати за", + + // "search.sidebar.settings.title": "Settings", + "search.sidebar.settings.title": "Налаштування", + + + + // "search.view-switch.show-detail": "Show detail", + "search.view-switch.show-detail": "Показати деталі", + + // "search.view-switch.show-grid": "Show as grid", + "search.view-switch.show-grid": "Показати у вигляді матриці", + + // "search.view-switch.show-list": "Show as list", + "search.view-switch.show-list": "Показати як список", + + + + // "sorting.ASC": "Ascending", + + "sorting.ASC": "У порядку збільшення", + + // "sorting.DESC": "Descending", + + "sorting.DESC": "У порядку зменшення", + + // "sorting.dc.title.ASC": "Title Ascending", + "sorting.dc.title.ASC": "У порядку збільшення за назвою", + + // "sorting.dc.title.DESC": "Title Descending", + "sorting.dc.title.DESC": "У порядку зменшення за назвою", + + // "sorting.score.DESC": "Relevance", + "sorting.score.DESC": "Актуальність", + + + + // "statistics.title": "Statistics", + + "statistics.title": "Статистика", + + // "statistics.header": "Statistics for {{ scope }}", + + "statistics.header": "Статистика для {{ scope }}", + + // "statistics.breadcrumbs": "Statistics", + + "statistics.breadcrumbs": "Статистика", + + // "statistics.page.no-data": "No data available", + + "statistics.page.no-data": "Дані відсутні", + + // "statistics.table.no-data": "No data available", + + "statistics.table.no-data": "Дані відсутні", + + // "statistics.table.title.TotalVisits": "Total visits", + + "statistics.table.title.TotalVisits": "Всього відвідувань", + + // "statistics.table.title.TotalVisitsPerMonth": "Total visits per month", + + "statistics.table.title.TotalVisitsPerMonth": "Всього відвідувань за місяць", + + // "statistics.table.title.TotalDownloads": "File Visits", + + "statistics.table.title.TotalDownloads": "Скачувань файлів", + + // "statistics.table.title.TopCountries": "Top country views", + + "statistics.table.title.TopCountries": "Топ за країнами", + + // "statistics.table.title.TopCities": "Top city views", + + "statistics.table.title.TopCities": "Топ за містами", + + // "statistics.table.header.views": "Views", + + "statistics.table.header.views": "Перегляди", + + + + // "submission.edit.title": "Edit Submission", + "submission.edit.title": "Редагувати внесення нового документа", + + // "submission.general.cannot_submit": "You have not the privilege to make a new submission.", + "submission.general.cannot_submit": "У Вас немає повноважень щоб внести новий документ.", + + // "submission.general.deposit": "Deposit", + "submission.general.deposit": "Депозит", + + // "submission.general.discard.confirm.cancel": "Cancel", + "submission.general.discard.confirm.cancel": "Відмінити", + + // "submission.general.discard.confirm.info": "This operation can't be undone. Are you sure?", + "submission.general.discard.confirm.info": "Цю операцію неможливо відмінити. Ви впевнені?", + + // "submission.general.discard.confirm.submit": "Yes, I'm sure", + "submission.general.discard.confirm.submit": "Так, я впевнений", + + // "submission.general.discard.confirm.title": "Discard submission", + "submission.general.discard.confirm.title": "Скасувати надсилання документа", + + // "submission.general.discard.submit": "Discard", + "submission.general.discard.submit": "Скасувати", + + // "submission.general.save": "Save", + "submission.general.save": "Зберегти", + + // "submission.general.save-later": "Save for later", + "submission.general.save-later": "Зберегти на потім", + + + // "submission.import-external.page.title": "Import metadata from an external source", + + "submission.import-external.page.title": "Імпортувати метадані із зовнішніх джерел", + + // "submission.import-external.title": "Import metadata from an external source", + + "submission.import-external.title": "Імпортувати метадані із зовнішніх джерел", + + // "submission.import-external.page.hint": "Enter a query above to find items from the web to import in to DSpace.", + + "submission.import-external.page.hint": "Введіть запит вище, щоб знайти елементи з Інтернету для імпорту в репозиторій", + + // "submission.import-external.back-to-my-dspace": "Back to MyDSpace", + + "submission.import-external.back-to-my-dspace": "Повернутись до репозиторію", + + // "submission.import-external.search.placeholder": "Search the external source", + + "submission.import-external.search.placeholder": "Шукати зовнішнє джерело даних", + + // "submission.import-external.search.button": "Search", + + "submission.import-external.search.button": "Пошук", + + // "submission.import-external.search.button.hint": "Write some words to search", + + "submission.import-external.search.button.hint": "Напишіть ключові слова для пошуку", + + // "submission.import-external.search.source.hint": "Pick an external source", + + "submission.import-external.search.source.hint": "Виберіть зовнішнє джерело даних", + + // "submission.import-external.source.arxiv": "arXiv", + + "submission.import-external.source.arxiv": "arXiv", + + // "submission.import-external.source.loading": "Loading ...", + + "submission.import-external.source.loading": "Завантажуюсь ...", + + // "submission.import-external.source.sherpaJournal": "SHERPA Journals", + + "submission.import-external.source.sherpaJournal": "SHERPA видання", + + // "submission.import-external.source.sherpaPublisher": "SHERPA Publishers", + + "submission.import-external.source.sherpaPublisher": "SHERPA видавці", + + // "submission.import-external.source.orcid": "ORCID", + + "submission.import-external.source.orcid": "ORCID", + + // "submission.import-external.source.pubmed": "Pubmed", + + "submission.import-external.source.pubmed": "Pubmed", + + // "submission.import-external.source.lcname": "Library of Congress Names", + + "submission.import-external.source.lcname": "Бібліотека Congress Names", + + // "submission.import-external.preview.title": "Item Preview", + + "submission.import-external.preview.title": "Перегляд документа", + + // "submission.import-external.preview.subtitle": "The metadata below was imported from an external source. It will be pre-filled when you start the submission.", + + "submission.import-external.preview.subtitle": "Наведені нижче метадані імпортовано із зовнішнього джерела. Їх буде попередньо заповнено, коли ви почнете надсилати документ у репозиторій.", + + // "submission.import-external.preview.button.import": "Start submission", + + "submission.import-external.preview.button.import": "Почати вносити документ", + + // "submission.import-external.preview.error.import.title": "Submission error", + + "submission.import-external.preview.error.import.title": "Виникла помилка при внесенні документа", + + // "submission.import-external.preview.error.import.body": "An error occurs during the external source entry import process.", + + "submission.import-external.preview.error.import.body": "Виникла помилка при імпорті даних.", + + // "submission.sections.describe.relationship-lookup.close": "Close", + "submission.sections.describe.relationship-lookup.close": "Закрити", + + // "submission.sections.describe.relationship-lookup.external-source.added": "Successfully added local entry to the selection", + "submission.sections.describe.relationship-lookup.external-source.added": "Локальний запис успішно додано", + + // "submission.sections.describe.relationship-lookup.external-source.import-button-title.isAuthorOfPublication": "Import remote author", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.isAuthorOfPublication": "Імпорт віддаленого автора", + + // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal": "Import remote journal", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal": "Імпорт віддаленого видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Issue": "Import remote journal issue", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Issue": "Імпорт віддаленого випуску видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Volume": "Import remote journal volume", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Volume": "Імпорт віддаленого тому видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.title": "Import Remote Author", + + "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.title": "Імпорт віддаленого автора", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.local-entity": "Successfully added local author to the selection", + + "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.local-entity": "Локального автора успішно додано до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.new-entity": "Successfully imported and added external author to the selection", + + "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.new-entity": "Локального автора успішно імпортовано та додано до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Authority", + "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Authority", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.authority.new": "Import as a new local authority entry", + "submission.sections.describe.relationship-lookup.external-source.import-modal.authority.new": "Import as a new local authority entry", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.cancel": "Cancel", + "submission.sections.describe.relationship-lookup.external-source.import-modal.cancel": "Відмінити", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.collection": "Select a collection to import new entries to", + "submission.sections.describe.relationship-lookup.external-source.import-modal.collection": "Виберіть зібрання, до якого потрібно імпортувати нові записи", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.entities": "Entities", + "submission.sections.describe.relationship-lookup.external-source.import-modal.entities": "Сутності", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.entities.new": "Import as a new local entity", + "submission.sections.describe.relationship-lookup.external-source.import-modal.entities.new": "Імпотрувати як нову локальну сутність", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.head.lcname": "Importing from LC Name", + "submission.sections.describe.relationship-lookup.external-source.import-modal.head.lcname": "Імпортувати з LC назвою", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.head.orcid": "Importing from ORCID", + "submission.sections.describe.relationship-lookup.external-source.import-modal.head.orcid": "Імпортувати з ORCID", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.head.sherpaJournal": "Importing from Sherpa Journal", + "submission.sections.describe.relationship-lookup.external-source.import-modal.head.sherpaJournal": "Імпортувати з Sherpa видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.head.sherpaPublisher": "Importing from Sherpa Publisher", + "submission.sections.describe.relationship-lookup.external-source.import-modal.head.sherpaPublisher": "Імпортувати з Sherpa видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.head.pubmed": "Importing from PubMed", + + "submission.sections.describe.relationship-lookup.external-source.import-modal.head.pubmed": "Імпортувати з PubMed", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.head.arxiv": "Importing from arXiv", + + "submission.sections.describe.relationship-lookup.external-source.import-modal.head.arxiv": "Імпортувати з arXiv", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.import": "Import", + "submission.sections.describe.relationship-lookup.external-source.import-modal.import": "Імпорт", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.title": "Import Remote Journal", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.title": "Імпорт віддаленого видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.local-entity": "Successfully added local journal to the selection", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.local-entity": "Нове видання успішно додане до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.new-entity": "Successfully imported and added external journal to the selection", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.new-entity": "Нове видання успішно імпортовано та додане до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.title": "Import Remote Journal Issue", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.title": "Імпорт віддаленого випуску видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.local-entity": "Successfully added local journal issue to the selection", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.local-entity": "Випуск видання успішно додане до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.new-entity": "Successfully imported and added external journal issue to the selection", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.new-entity": "Випуск видання успішно імпортовано та додане до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.title": "Import Remote Journal Volume", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.title": "Імпорт віддаленого тому видання", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.local-entity": "Successfully added local journal volume to the selection", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.local-entity": "Том видання успішно додано до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.new-entity": "Successfully imported and added external journal volume to the selection", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.new-entity": "Том видання успішно імпортовано та додано до вибраних", + + // "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Select a local match:", + "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Виберіть локальний збіг", + + // "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all", + "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Видалити позначення для всіх", + + // "submission.sections.describe.relationship-lookup.search-tab.deselect-page": "Deselect page", + "submission.sections.describe.relationship-lookup.search-tab.deselect-page": "Видалити позначення сторінки", + + // "submission.sections.describe.relationship-lookup.search-tab.loading": "Loading...", + "submission.sections.describe.relationship-lookup.search-tab.loading": "Вантажиться...", + + // "submission.sections.describe.relationship-lookup.search-tab.placeholder": "Search query", + "submission.sections.describe.relationship-lookup.search-tab.placeholder": "Пошуковий запит", + + // "submission.sections.describe.relationship-lookup.search-tab.search": "Go", + "submission.sections.describe.relationship-lookup.search-tab.search": "Вперед", + + // "submission.sections.describe.relationship-lookup.search-tab.select-all": "Select all", + "submission.sections.describe.relationship-lookup.search-tab.select-all": "Виділити все", + + // "submission.sections.describe.relationship-lookup.search-tab.select-page": "Select page", + "submission.sections.describe.relationship-lookup.search-tab.select-page": "Виділити сторінку", + + // "submission.sections.describe.relationship-lookup.selected": "Selected {{ size }} items", + "submission.sections.describe.relationship-lookup.selected": "Позначені {{ size }} документи", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Local Authors ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Локальні автори ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Local Journals ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Локальні видання ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Project": "Local Projects ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Project": "Локальні проекти ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Publication": "Local Publications ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Publication": "Локальні публікації ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Local Authors ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Локальні автори ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.OrgUnit": "Local Organizational Units ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.OrgUnit": "Локальні структурні одиниці({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataPackage": "Local Data Packages ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataPackage": "Локальні пакети даних ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataFile": "Local Data Files ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataFile": "Локальні файли даних ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Local Journals ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Локальні видання ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalIssueOfPublication": "Local Journal Issues ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalIssueOfPublication": "Локальні випуски видань ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalIssue": "Local Journal Issues ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalIssue": "Локальні випуски видань ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalVolumeOfPublication": "Local Journal Volumes ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalVolumeOfPublication": "Локальні томи видань ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalVolume": "Local Journal Volumes ({{ count }})", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalVolume": "Локальні томи видань({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaJournal": "Sherpa Journals ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaJournal": "Sherpa видання ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa Publishers ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa видавці ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.orcid": "ORCID ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.orcid": "ORCID ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.lcname": "LC Names ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.lcname": "LC назви ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.pubmed": "PubMed ({{ count }})", + // TODO New key - Add a translation + "submission.sections.describe.relationship-lookup.search-tab.tab-title.pubmed": "PubMed ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.arxiv": "arXiv ({{ count }})", + // TODO New key - Add a translation + "submission.sections.describe.relationship-lookup.search-tab.tab-title.arxiv": "arXiv ({{ count }})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfPublication": "Search for Funding Agencies", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfPublication": "Пошук фінансових агентств", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingOfPublication": "Search for Funding", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingOfPublication": "Пошук фінансування", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isChildOrgUnitOf": "Search for Organizational Units", + + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isChildOrgUnitOf": "Пошук структурних одиниць", + + // "submission.sections.describe.relationship-lookup.selection-tab.tab-title": "Current Selection ({{ count }})", + "submission.sections.describe.relationship-lookup.selection-tab.tab-title": "Вибрані позиції ({{ count }})", + + // "submission.sections.describe.relationship-lookup.title.isJournalIssueOfPublication": "Journal Issues", + + "submission.sections.describe.relationship-lookup.title.isJournalIssueOfPublication": "Випуски видання", + + // "submission.sections.describe.relationship-lookup.title.JournalIssue": "Journal Issues", + + "submission.sections.describe.relationship-lookup.title.JournalIssue": "Випуски видання", + + // "submission.sections.describe.relationship-lookup.title.isJournalVolumeOfPublication": "Journal Volumes", + + "submission.sections.describe.relationship-lookup.title.isJournalVolumeOfPublication": "Томи видання", + + // "submission.sections.describe.relationship-lookup.title.JournalVolume": "Journal Volumes", + + "submission.sections.describe.relationship-lookup.title.JournalVolume": "Томи видання", + + // "submission.sections.describe.relationship-lookup.title.isJournalOfPublication": "Journals", + + "submission.sections.describe.relationship-lookup.title.isJournalOfPublication": "Видання", + + // "submission.sections.describe.relationship-lookup.title.isAuthorOfPublication": "Authors", + + "submission.sections.describe.relationship-lookup.title.isAuthorOfPublication": "Автори", + + // "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfPublication": "Funding Agency", + + "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfPublication": "Фінансова агенція", + + // "submission.sections.describe.relationship-lookup.title.Project": "Projects", + + "submission.sections.describe.relationship-lookup.title.Project": "Проекти", + + // "submission.sections.describe.relationship-lookup.title.Publication": "Publications", + + "submission.sections.describe.relationship-lookup.title.Publication": "Публікації", + + // "submission.sections.describe.relationship-lookup.title.Person": "Authors", + + "submission.sections.describe.relationship-lookup.title.Person": "Автори", + + // "submission.sections.describe.relationship-lookup.title.OrgUnit": "Organizational Units", + + "submission.sections.describe.relationship-lookup.title.OrgUnit": "Структурні одиниці", + + // "submission.sections.describe.relationship-lookup.title.DataPackage": "Data Packages", + + "submission.sections.describe.relationship-lookup.title.DataPackage": "Пакети даних", + + // "submission.sections.describe.relationship-lookup.title.DataFile": "Data Files", + + "submission.sections.describe.relationship-lookup.title.DataFile": "Файли даних", + + // "submission.sections.describe.relationship-lookup.title.Funding Agency": "Funding Agency", + "submission.sections.describe.relationship-lookup.title.Funding Agency": "Фінансова агенція", + + // "submission.sections.describe.relationship-lookup.title.isFundingOfPublication": "Funding", + + "submission.sections.describe.relationship-lookup.title.isFundingOfPublication": "Фінансування", + + // "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Parent Organizational Unit", + + "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Головна структурна одиниця", + + // "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown", + "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Перемкнути спадне меню", + + // "submission.sections.describe.relationship-lookup.selection-tab.settings": "Settings", + "submission.sections.describe.relationship-lookup.selection-tab.settings": "Налаштування", + + // "submission.sections.describe.relationship-lookup.selection-tab.no-selection": "Your selection is currently empty.", + "submission.sections.describe.relationship-lookup.selection-tab.no-selection": "Ви нічого не вибрали", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.isAuthorOfPublication": "Selected Authors", + + "submission.sections.describe.relationship-lookup.selection-tab.title.isAuthorOfPublication": "Вибрані автори", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalOfPublication": "Selected Journals", + + "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalOfPublication": "Вибрані видання", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalVolumeOfPublication": "Selected Journal Volume", + + "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalVolumeOfPublication": "Вибрані томи видання", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.Project": "Selected Projects", + + "submission.sections.describe.relationship-lookup.selection-tab.title.Project": "Вибрані проекти", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.Publication": "Selected Publications", + + "submission.sections.describe.relationship-lookup.selection-tab.title.Publication": "Вибрані публікації", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.Person": "Selected Authors", + + "submission.sections.describe.relationship-lookup.selection-tab.title.Person": "Вибрані автори", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.OrgUnit": "Selected Organizational Units", + + "submission.sections.describe.relationship-lookup.selection-tab.title.OrgUnit": "Вибрані структурні одиниці", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.DataPackage": "Selected Data Packages", + + "submission.sections.describe.relationship-lookup.selection-tab.title.DataPackage": "Вибрані пакети даних", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.DataFile": "Selected Data Files", + + "submission.sections.describe.relationship-lookup.selection-tab.title.DataFile": "ВИбрані файли даних", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.Journal": "Selected Journals", + "submission.sections.describe.relationship-lookup.selection-tab.title.Journal": "Вибрані видання", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalIssueOfPublication": "Selected Issue", + + "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalIssueOfPublication": "Вибрані випуски", + // "submission.sections.describe.relationship-lookup.selection-tab.title.JournalVolume": "Selected Journal Volume", + + "submission.sections.describe.relationship-lookup.selection-tab.title.JournalVolume": "Вибрані томи видання", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingAgencyOfPublication": "Selected Funding Agency", + + "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingAgencyOfPublication": "Вибрана фінансова агенція", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingOfPublication": "Selected Funding", + + "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingOfPublication": "Вибране фінансування", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.JournalIssue": "Selected Issue", + + "submission.sections.describe.relationship-lookup.selection-tab.title.JournalIssue": "Вибрані випуски", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.isChildOrgUnitOf": "Selected Organizational Unit", + + "submission.sections.describe.relationship-lookup.selection-tab.title.isChildOrgUnitOf": "Вибрані структурні одиниці", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.sherpaJournal": "Search Results", + "submission.sections.describe.relationship-lookup.selection-tab.title.sherpaJournal": "Результати пошуку", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.sherpaPublisher": "Search Results", + "submission.sections.describe.relationship-lookup.selection-tab.title.sherpaPublisher": "Результати пошуку", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.orcid": "Search Results", + "submission.sections.describe.relationship-lookup.selection-tab.title.orcid": "Результати пошуку", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.orcidv2": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title.orcidv2": "Результати пошуку", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.lcname": "Search Results", + "submission.sections.describe.relationship-lookup.selection-tab.title.lcname": "Результати пошуку", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.pubmed": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title.pubmed": "Результати пошуку", + + // "submission.sections.describe.relationship-lookup.selection-tab.title.arxiv": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title.arxiv": "Результати пошуку", + + // "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.", + "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Чи бажаєте ви зберегти \"{{ value }}\" як варіант імені для цього користувача, щоб ви та інші могли повторно використовувати його для майбутніх надсилань? Якщо ви цього не зробите, ви можете використовувати його для цього надсилання.", + + // "submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant", + "submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Збережіть новий варіант імені", + + // "submission.sections.describe.relationship-lookup.name-variant.notification.decline": "Use only for this submission", + "submission.sections.describe.relationship-lookup.name-variant.notification.decline": "Використовуйте тільки для даного надсилання документів", + + // "submission.sections.ccLicense.type": "License Type", + + "submission.sections.ccLicense.type": "Тип ліцензії", + + // "submission.sections.ccLicense.select": "Select a license type…", + + "submission.sections.ccLicense.select": "Биберіть тип ліцензії…", + + // "submission.sections.ccLicense.change": "Change your license type…", + + "submission.sections.ccLicense.change": "Змініть тип ліцензії…", + + // "submission.sections.ccLicense.none": "No licenses available", + + "submission.sections.ccLicense.none": "Нема ліцензії", + + // "submission.sections.ccLicense.option.select": "Select an option…", + + "submission.sections.ccLicense.option.select": "Виберіть опцію…", + + // "submission.sections.ccLicense.link": "You’ve selected the following license:", + + "submission.sections.ccLicense.link": "Ви вибрали ліцензію:", + + // "submission.sections.ccLicense.confirmation": "I grant the license above", + + "submission.sections.ccLicense.confirmation": "Я даю згоду на цю ліцензію", + + // "submission.sections.general.add-more": "Add more", + "submission.sections.general.add-more": "Додати більше", + + // "submission.sections.general.collection": "Collection", + "submission.sections.general.collection": "Зібрання", + + // "submission.sections.general.deposit_error_notice": "There was an issue when submitting the item, please try again later.", + "submission.sections.general.deposit_error_notice": "Під час надсилання елемента виникла проблема. Повторіть спробу пізніше.", + + // "submission.sections.general.deposit_success_notice": "Submission deposited successfully.", + "submission.sections.general.deposit_success_notice": "Подання документа успшно збережено.", + + // "submission.sections.general.discard_error_notice": "There was an issue when discarding the item, please try again later.", + "submission.sections.general.discard_error_notice": "Під час внесення документа виникла проблема. Повторіть спробу пізніше", + + // "submission.sections.general.discard_success_notice": "Submission discarded successfully.", + "submission.sections.general.discard_success_notice": "Подання успішно відхилено.", + + // "submission.sections.general.metadata-extracted": "New metadata have been extracted and added to the {{sectionId}} section.", + "submission.sections.general.metadata-extracted": "Нові метадані видобуто та додано до {{sectionId}} секції.", + + // "submission.sections.general.metadata-extracted-new-section": "New {{sectionId}} section has been added to submission.", + "submission.sections.general.metadata-extracted-new-section": "Нова {{sectionId}} секція додана до подання документа.", + + // "submission.sections.general.no-collection": "No collection found", + "submission.sections.general.no-collection": "Зібрання не знайдено", + + // "submission.sections.general.no-sections": "No options available", + "submission.sections.general.no-sections": "Опції відсутні", + + // "submission.sections.general.save_error_notice": "There was an issue when saving the item, please try again later.", + "submission.sections.general.save_error_notice": "Під час збереження документа виникла проблема. Повторіть спробу пізніше.", + + // "submission.sections.general.save_success_notice": "Submission saved successfully.", + "submission.sections.general.save_success_notice": "Подання успішно збережено.", + + // "submission.sections.general.search-collection": "Search for a collection", + "submission.sections.general.search-collection": "Пошук зібрання", + + // "submission.sections.general.sections_not_valid": "There are incomplete sections.", + "submission.sections.general.sections_not_valid": "Неповні розділи.", + + + + // "submission.sections.submit.progressbar.CClicense": "Creative commons license", + + "submission.sections.submit.progressbar.CClicense": "СС ліцензії", + + // "submission.sections.submit.progressbar.describe.recycle": "Recycle", + "submission.sections.submit.progressbar.describe.recycle": "Переробити", + + // "submission.sections.submit.progressbar.describe.stepcustom": "Describe", + "submission.sections.submit.progressbar.describe.stepcustom": "Описати", + + // "submission.sections.submit.progressbar.describe.stepone": "Describe", + "submission.sections.submit.progressbar.describe.stepone": "Описати", + + // "submission.sections.submit.progressbar.describe.steptwo": "Describe", + "submission.sections.submit.progressbar.describe.steptwo": "Описати", + + // "submission.sections.submit.progressbar.detect-duplicate": "Potential duplicates", + "submission.sections.submit.progressbar.detect-duplicate": "Можливі дублікати", + + // "submission.sections.submit.progressbar.license": "Deposit license", + "submission.sections.submit.progressbar.license": "Ліцензія", + + // "submission.sections.submit.progressbar.upload": "Upload files", + "submission.sections.submit.progressbar.upload": "Завантажені файли", + + + + // "submission.sections.upload.delete.confirm.cancel": "Cancel", + "submission.sections.upload.delete.confirm.cancel": "Відмінити", + + // "submission.sections.upload.delete.confirm.info": "This operation can't be undone. Are you sure?", + "submission.sections.upload.delete.confirm.info": "Ця операція не може бути відмінена. Ви впевнені?", + + // "submission.sections.upload.delete.confirm.submit": "Yes, I'm sure", + "submission.sections.upload.delete.confirm.submit": "Так, я впевнений", + + // "submission.sections.upload.delete.confirm.title": "Delete bitstream", + "submission.sections.upload.delete.confirm.title": "Видалити файл", + + // "submission.sections.upload.delete.submit": "Delete", + "submission.sections.upload.delete.submit": "Видалити", + + // "submission.sections.upload.drop-message": "Drop files to attach them to the item", + "submission.sections.upload.drop-message": "Перетягніть файли, щоб приєднати їх до документа", + + // "submission.sections.upload.form.access-condition-label": "Access condition type", + "submission.sections.upload.form.access-condition-label": "Умови доступу", + + // "submission.sections.upload.form.date-required": "Date is required.", + "submission.sections.upload.form.date-required": "Поле дати є обов\'язковим для заповнення.", + + // "submission.sections.upload.form.from-label": "Grant access from", + + "submission.sections.upload.form.from-label": "Надати доступ з", + + // "submission.sections.upload.form.from-placeholder": "From", + "submission.sections.upload.form.from-placeholder": "З", + + // "submission.sections.upload.form.group-label": "Group", + "submission.sections.upload.form.group-label": "Група", + + // "submission.sections.upload.form.group-required": "Group is required.", + "submission.sections.upload.form.group-required": "Група є обов\'язковою.", + + // "submission.sections.upload.form.until-label": "Grant access until", + + "submission.sections.upload.form.until-label": "Надати доступ до", + + // "submission.sections.upload.form.until-placeholder": "Until", + "submission.sections.upload.form.until-placeholder": "До", + + // "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": "Завантажені файли зібрань {{collectionName}} будуть доступні групам:", + + // "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": "Зверніть увагу, що завантажені файли в {{collectionName}} зібрання будуть доступні, для користувачів, що входять у групу(и):", + + // "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 just dragging & dropping them everywhere in the page", + "submission.sections.upload.info": "Тут ви знайдете всі файли, які зараз містяться в документі. Ви можете оновити метадані файлу та умови доступу або завантажити додаткові файли, просто перетягнувши їх сюди на сторінці", + + // "submission.sections.upload.no-entry": "No", + "submission.sections.upload.no-entry": "Ні", + + // "submission.sections.upload.no-file-uploaded": "No file uploaded yet.", + "submission.sections.upload.no-file-uploaded": "Наразі файл не завантажено.", + + // "submission.sections.upload.save-metadata": "Save metadata", + "submission.sections.upload.save-metadata": "Зберегти метадані", + + // "submission.sections.upload.undo": "Cancel", + "submission.sections.upload.undo": "Відмінити", + + // "submission.sections.upload.upload-failed": "Upload failed", + "submission.sections.upload.upload-failed": "Помилка завантаження", + + // "submission.sections.upload.upload-successful": "Upload successful", + "submission.sections.upload.upload-successful": "Успішно завантажено", + + + + // "submission.submit.title": "Submission", + "submission.submit.title": "Надсилання документів", + + + + // "submission.workflow.generic.delete": "Delete", + "submission.workflow.generic.delete": "Видалити", + + // "submission.workflow.generic.delete-help": "If you would to discard this item, select \"Delete\". You will then be asked to confirm it.", + "submission.workflow.generic.delete-help": "Якщо ви хочете видалити цей елемент, виберіть \"Видалити\". Потім вас попросять підтвердити це.", + + // "submission.workflow.generic.edit": "Edit", + "submission.workflow.generic.edit": "Редагувати", + + // "submission.workflow.generic.edit-help": "Select this option to change the item's metadata.", + "submission.workflow.generic.edit-help": "Виберіть цю опцію для зміни метаданих документа.", + + // "submission.workflow.generic.view": "View", + "submission.workflow.generic.view": "Перегляд", + + // "submission.workflow.generic.view-help": "Select this option to view the item's metadata.", + "submission.workflow.generic.view-help": "Виберіть цю опцію для перегляду метаданих документа.", + + + + // "submission.workflow.tasks.claimed.approve": "Approve", + "submission.workflow.tasks.claimed.approve": "Затвердити", + + // "submission.workflow.tasks.claimed.approve_help": "If you have reviewed the item and it is suitable for inclusion in the collection, select \"Approve\".", + "submission.workflow.tasks.claimed.approve_help": "Якщо ви переглянули документ і він підходить для включення в зібрання, виберіть \"Затвердити\".", + + // "submission.workflow.tasks.claimed.edit": "Edit", + "submission.workflow.tasks.claimed.edit": "Редагувати", + + // "submission.workflow.tasks.claimed.edit_help": "Select this option to change the item's metadata.", + "submission.workflow.tasks.claimed.edit_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.", + "submission.workflow.tasks.claimed.reject.reason.info": "Будь ласка, введіть причину відхилення надсилання документа в полі нижче, вказавши, чи може заявник вирішити проблему та повторно подати.", + + // "submission.workflow.tasks.claimed.reject.reason.placeholder": "Describe the reason of reject", + "submission.workflow.tasks.claimed.reject.reason.placeholder": "Опишіть причину відхилення", + + // "submission.workflow.tasks.claimed.reject.reason.submit": "Reject item", + "submission.workflow.tasks.claimed.reject.reason.submit": "Вдхилити документ", + + // "submission.workflow.tasks.claimed.reject.reason.title": "Reason", + "submission.workflow.tasks.claimed.reject.reason.title": "Причина", + + // "submission.workflow.tasks.claimed.reject.submit": "Reject", + "submission.workflow.tasks.claimed.reject.submit": "Відхилити", + + // "submission.workflow.tasks.claimed.reject_help": "If you have reviewed the item and found it is not suitable for inclusion in the collection, select \"Reject\". You will then be asked to enter a message indicating why the item is unsuitable, and whether the submitter should change something and resubmit.", + "submission.workflow.tasks.claimed.reject_help": "Якщо ви переглянули документ і виявили, що він не підходить для включення в зібрання, виберіть \"Відхилити\". Після цього вас попросять ввести інформацію про причину відхилення і чи повинен автор щось змінити та повторно подати.", + + // "submission.workflow.tasks.claimed.return": "Return to pool", + "submission.workflow.tasks.claimed.return": "Повернути до черги", + + // "submission.workflow.tasks.claimed.return_help": "Return the task to the pool so that another user may perform the task.", + "submission.workflow.tasks.claimed.return_help": "Поверніть завдання до черги, щоб інший користувач міг виконати завдання.", + + + + // "submission.workflow.tasks.generic.error": "Error occurred during operation...", + "submission.workflow.tasks.generic.error": "Виникла помилка...", + + // "submission.workflow.tasks.generic.processing": "Processing...", + "submission.workflow.tasks.generic.processing": "Опрацювання...", + + // "submission.workflow.tasks.generic.submitter": "Submitter", + "submission.workflow.tasks.generic.submitter": "Користувач, котрий надсилає", + + // "submission.workflow.tasks.generic.success": "Operation successful", + "submission.workflow.tasks.generic.success": "Операція пройшла успішно", + + + + // "submission.workflow.tasks.pool.claim": "Claim", + "submission.workflow.tasks.pool.claim": "Претензія", + + // "submission.workflow.tasks.pool.claim_help": "Assign this task to yourself.", + "submission.workflow.tasks.pool.claim_help": "Доручіть собі це завдання.", + + // "submission.workflow.tasks.pool.hide-detail": "Hide detail", + "submission.workflow.tasks.pool.hide-detail": "Приховати деталі", + + // "submission.workflow.tasks.pool.show-detail": "Show detail", + "submission.workflow.tasks.pool.show-detail": "Показати деталі", + + + + // "title": "DSpace", + "title": "Репозиторій", + + + + // "vocabulary-treeview.header": "Hierarchical tree view", + + "vocabulary-treeview.header": "Перегляд деревовидної структури", + + // "vocabulary-treeview.load-more": "Load more", + + "vocabulary-treeview.load-more": "Детальніше", + + // "vocabulary-treeview.search.form.reset": "Reset", + + "vocabulary-treeview.search.form.reset": "Скинути", + + // "vocabulary-treeview.search.form.search": "Search", + + "vocabulary-treeview.search.form.search": "Шукати", + + // "vocabulary-treeview.search.no-result": "There were no items to show", + + "vocabulary-treeview.search.no-result": "Не знайдено жодних документів", + + // "vocabulary-treeview.tree.description.nsi": "The Norwegian Science Index", + + "vocabulary-treeview.tree.description.nsi": "Norwegian Science індекс", + + // "vocabulary-treeview.tree.description.srsc": "Research Subject Categories", + + "vocabulary-treeview.tree.description.srsc": "Категорії дослідницьких напрямків", + + + + // "uploader.browse": "browse", + "uploader.browse": "перегляд", + + // "uploader.drag-message": "Drag & Drop your files here", + "uploader.drag-message": "Перетягніть файли сюди", + + // "uploader.or": ", or ", + // TODO Source message changed - Revise the translation + "uploader.or": ", або", + + // "uploader.processing": "Processing", + "uploader.processing": "Опрацювання", + + // "uploader.queue-length": "Queue length", + "uploader.queue-length": "Довжина черги", + + // "virtual-metadata.delete-item.info": "Select the types for which you want to save the virtual metadata as real metadata", + "virtual-metadata.delete-item.info": "Виберіть типи, для яких ви хочете зберегти віртуальні метадані як справжні метадані", + + // "virtual-metadata.delete-item.modal-head": "The virtual metadata of this relation", + "virtual-metadata.delete-item.modal-head": "Віртуальні метадані цього відношення", + + // "virtual-metadata.delete-relationship.modal-head": "Select the items for which you want to save the virtual metadata as real metadata", + "virtual-metadata.delete-relationship.modal-head": "Виберіть документи, для яких ви хочете зберегти віртуальні метадані як справжні метадані", + + + + // "workflowAdmin.search.results.head": "Administer Workflow", + "workflowAdmin.search.results.head": "Робочий процес адміністратора", + + + + // "workflow-item.delete.notification.success.title": "Deleted", + + "workflow-item.delete.notification.success.title": "Видалено", + + // "workflow-item.delete.notification.success.content": "This workflow item was successfully deleted", + + "workflow-item.delete.notification.success.content": "Робочий процес внесення документа був успішно видалений", + + // "workflow-item.delete.notification.error.title": "Something went wrong", + + "workflow-item.delete.notification.error.title": "Щось пішло не так", + + // "workflow-item.delete.notification.error.content": "The workflow item could not be deleted", + + "workflow-item.delete.notification.error.content": "Документ з робочого процесу не може бути видалений", + + // "workflow-item.delete.title": "Delete workflow item", + + "workflow-item.delete.title": "Видалити документ з робочого процесу", + + // "workflow-item.delete.header": "Delete workflow item", + + "workflow-item.delete.header": "Видалити документ з робочого процесу", + + // "workflow-item.delete.button.cancel": "Cancel", + + "workflow-item.delete.button.cancel": "Відмінити", + + // "workflow-item.delete.button.confirm": "Delete", + + "workflow-item.delete.button.confirm": "Видалити", + + + // "workflow-item.send-back.notification.success.title": "Sent back to submitter", + + "workflow-item.send-back.notification.success.title": "Відіслати назад до користувача, котрий надіслав цей документ", + + // "workflow-item.send-back.notification.success.content": "This workflow item was successfully sent back to the submitter", + + "workflow-item.send-back.notification.success.content": "Цей документ був відісланий назад до користувача, котрий надіслав цей документ", + + // "workflow-item.send-back.notification.error.title": "Something went wrong", + + "workflow-item.send-back.notification.error.title": "Щось пішло не так", + + // "workflow-item.send-back.notification.error.content": "The workflow item could not be sent back to the submitter", + + "workflow-item.send-back.notification.error.content": "Цей документ НЕ може біти відісланий назад до користувача, котрий надіслав цей документ", + + // "workflow-item.send-back.title": "Send workflow item back to submitter", + + "workflow-item.send-back.title": "Надіслати документ назад до користувача, котрий його надіслав", + + // "workflow-item.send-back.header": "Send workflow item back to submitter", + + "workflow-item.send-back.header": "Надіслати документ назад до користувача, котрий його надіслав", + + // "workflow-item.send-back.button.cancel": "Cancel", + + "workflow-item.send-back.button.cancel": "Відмінити", + + // "workflow-item.send-back.button.confirm": "Send back" + + "workflow-item.send-back.button.confirm": "Надіслати назад" + + +} From c6fd55baf49878f1e7deb9466505ec86542cf2c8 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 2 Nov 2022 12:55:21 +0100 Subject: [PATCH 101/758] 96062: Alter test to match themed component --- .../collection-selector.component.spec.ts | 7 +++++-- ...submission-import-external-collection.component.spec.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts b/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts index ce54d326fc8..af043b447bf 100644 --- a/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts +++ b/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts @@ -128,10 +128,13 @@ describe('CollectionSelectorComponent', () => { beforeEach(() => { scheduler = getTestScheduler(); - fixture = TestBed.createComponent(CollectionSelectorComponent); + fixture = TestBed.overrideComponent(CollectionSelectorComponent, { + set: { + template: '' + } + }).createComponent(CollectionSelectorComponent); component = fixture.componentInstance; fixture.detectChanges(); - }); it('should create', () => { diff --git a/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts b/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts index 4f3c54b6428..9edab06c6f7 100644 --- a/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts +++ b/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts @@ -122,7 +122,7 @@ describe('SubmissionImportExternalCollectionComponent test suite', () => { fixture.detectChanges(); fixture.whenStable().then(() => { - const dropdownMenu = fixture.debugElement.query(By.css('ds-collection-dropdown')).nativeElement; + const dropdownMenu = fixture.debugElement.query(By.css('ds-themed-collection-dropdown')).nativeElement; expect(dropdownMenu.classList).toContain('d-none'); }); })); From 434e0cb9071b65720d871b2ca4b6e5631350265d Mon Sep 17 00:00:00 2001 From: nwoodward Date: Thu, 27 Oct 2022 16:11:03 -0500 Subject: [PATCH 102/758] fix function to match default value of websvc.opensearch.svccontext --- src/app/shared/rss-feed/rss.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 3fdb859bdce..0dd523c6bc0 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -114,7 +114,7 @@ export class RSSComponent implements OnInit, OnDestroy { * @returns The combine URL to opensearch */ formulateRoute(uuid: string, opensearch: string, sort: SortOptions, query: string): string { - let route = 'search?format=atom'; + let route = '?format=atom'; if (uuid) { route += `&scope=${uuid}`; } @@ -126,7 +126,7 @@ export class RSSComponent implements OnInit, OnDestroy { } else { route += `&query=*`; } - route = '/' + opensearch + '/' + route; + route = '/' + opensearch + route; return route; } From 2235072beaad9a82f657b8dbc72b0dffcfd90127 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Thu, 27 Oct 2022 17:07:14 -0500 Subject: [PATCH 103/758] changed path in tests to match default value for websvc.opensearch.svccontext --- src/app/shared/rss-feed/rss.component.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/shared/rss-feed/rss.component.spec.ts b/src/app/shared/rss-feed/rss.component.spec.ts index fd7f2c53216..aa2a2a63bcd 100644 --- a/src/app/shared/rss-feed/rss.component.spec.ts +++ b/src/app/shared/rss-feed/rss.component.spec.ts @@ -96,17 +96,17 @@ describe('RssComponent', () => { }); it('should formulate the correct url given params in url', () => { - const route = comp.formulateRoute(uuid, 'opensearch', options, query); + const route = comp.formulateRoute(uuid, 'opensearch/search', options, query); expect(route).toBe('/opensearch/search?format=atom&scope=2cfcf65e-0a51-4bcb-8592-b8db7b064790&sort=dc.title&sort_direction=DESC&query=test'); }); it('should skip uuid if its null', () => { - const route = comp.formulateRoute(null, 'opensearch', options, query); + const route = comp.formulateRoute(null, 'opensearch/search', options, query); expect(route).toBe('/opensearch/search?format=atom&sort=dc.title&sort_direction=DESC&query=test'); }); it('should default to query * if none provided', () => { - const route = comp.formulateRoute(null, 'opensearch', options, null); + const route = comp.formulateRoute(null, 'opensearch/search', options, null); expect(route).toBe('/opensearch/search?format=atom&sort=dc.title&sort_direction=DESC&query=*'); }); }); From 63011bc41dfc31c63fb1921e89bfd0f76b0ac8d6 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Wed, 2 Nov 2022 10:38:55 -0500 Subject: [PATCH 104/758] removed sorting parameters from RSSComponent --- src/app/shared/rss-feed/rss.component.spec.ts | 16 ++++++---------- src/app/shared/rss-feed/rss.component.ts | 10 ++-------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/app/shared/rss-feed/rss.component.spec.ts b/src/app/shared/rss-feed/rss.component.spec.ts index aa2a2a63bcd..61b54a11251 100644 --- a/src/app/shared/rss-feed/rss.component.spec.ts +++ b/src/app/shared/rss-feed/rss.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { RemoteData } from '../../core/data/remote-data'; import { GroupDataService } from '../../core/eperson/group-data.service'; @@ -23,7 +22,6 @@ import { RouterMock } from '../mocks/router.mock'; describe('RssComponent', () => { let comp: RSSComponent; - let options: SortOptions; let fixture: ComponentFixture; let uuid: string; let query: string; @@ -63,7 +61,6 @@ describe('RssComponent', () => { pageSize: 10, currentPage: 1 }), - sort: new SortOptions('dc.title', SortDirection.ASC), })); groupDataService = jasmine.createSpyObj('groupsDataService', { findListByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), @@ -88,7 +85,6 @@ describe('RssComponent', () => { })); beforeEach(() => { - options = new SortOptions('dc.title', SortDirection.DESC); uuid = '2cfcf65e-0a51-4bcb-8592-b8db7b064790'; query = 'test'; fixture = TestBed.createComponent(RSSComponent); @@ -96,18 +92,18 @@ describe('RssComponent', () => { }); it('should formulate the correct url given params in url', () => { - const route = comp.formulateRoute(uuid, 'opensearch/search', options, query); - expect(route).toBe('/opensearch/search?format=atom&scope=2cfcf65e-0a51-4bcb-8592-b8db7b064790&sort=dc.title&sort_direction=DESC&query=test'); + const route = comp.formulateRoute(uuid, 'opensearch/search', query); + expect(route).toBe('/opensearch/search?format=atom&scope=2cfcf65e-0a51-4bcb-8592-b8db7b064790&query=test'); }); it('should skip uuid if its null', () => { - const route = comp.formulateRoute(null, 'opensearch/search', options, query); - expect(route).toBe('/opensearch/search?format=atom&sort=dc.title&sort_direction=DESC&query=test'); + const route = comp.formulateRoute(null, 'opensearch/search', query); + expect(route).toBe('/opensearch/search?format=atom&query=test'); }); it('should default to query * if none provided', () => { - const route = comp.formulateRoute(null, 'opensearch/search', options, null); - expect(route).toBe('/opensearch/search?format=atom&sort=dc.title&sort_direction=DESC&query=*'); + const route = comp.formulateRoute(null, 'opensearch/search', null); + expect(route).toBe('/opensearch/search?format=atom&query=*'); }); }); diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 0dd523c6bc0..8a33aeeb689 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -12,7 +12,6 @@ import { ConfigurationDataService } from '../../core/data/configuration-data.ser import { getFirstCompletedRemoteData } from '../../core/shared/operators'; import { environment } from '../../../../src/environments/environment'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; -import { SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { Router } from '@angular/router'; import { map, switchMap } from 'rxjs/operators'; @@ -39,7 +38,6 @@ export class RSSComponent implements OnInit, OnDestroy { uuid: string; configuration$: Observable; - sortOption$: Observable; subs: Subscription[] = []; @@ -93,7 +91,7 @@ export class RSSComponent implements OnInit, OnDestroy { return null; } this.uuid = this.groupDataService.getUUIDFromString(this.router.url); - const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, openSearchUri, searchOptions.sort, searchOptions.query); + const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, openSearchUri, searchOptions.query); this.addLinks(route); this.linkHeadService.addTag({ href: environment.rest.baseUrl + '/' + openSearchUri + '/service', @@ -109,18 +107,14 @@ export class RSSComponent implements OnInit, OnDestroy { * Function created a route given the different params available to opensearch * @param uuid The uuid if a scope is present * @param opensearch openSearch uri - * @param sort The sort options for the opensearch request * @param query The query string that was provided in the search * @returns The combine URL to opensearch */ - formulateRoute(uuid: string, opensearch: string, sort: SortOptions, query: string): string { + formulateRoute(uuid: string, opensearch: string, query: string): string { let route = '?format=atom'; if (uuid) { route += `&scope=${uuid}`; } - if (sort && sort.direction && sort.field && sort.field !== 'id') { - route += `&sort=${sort.field}&sort_direction=${sort.direction}`; - } if (query) { route += `&query=${query}`; } else { From 9c05a116a847171a1068039bdf899b9d91592739 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Wed, 2 Nov 2022 18:28:24 +0100 Subject: [PATCH 105/758] Fix use case with cc.license.jurisdiction key commented out --- .../cc-license/submission-section-cc-licenses.component.html | 2 +- .../cc-license/submission-section-cc-licenses.component.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html index f10cd04b60a..0796da5a64a 100644 --- a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html +++ b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html @@ -81,7 +81,7 @@
- + {{ option.label }} diff --git a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts index 0e435e7d274..c8cd898f966 100644 --- a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts +++ b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts @@ -167,7 +167,7 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent * @param field the field for which to get the selected option value. */ getSelectedOption(ccLicense: SubmissionCcLicence, field: Field): Option { - if (field.id === 'jurisdiction' && this.defaultJurisdiction !== 'none') { + if (field.id === 'jurisdiction' && this.defaultJurisdiction !== undefined && this.defaultJurisdiction !== 'none') { return field.enums.find(option => option.id === this.defaultJurisdiction); } return this.data.ccLicense.fields[field.id]; @@ -274,7 +274,7 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent getFirstCompletedRemoteData(), getRemoteDataPayload() ).subscribe((remoteData) => { - if (remoteData.values.length === 0) { + if (remoteData === undefined || remoteData.values.length === 0) { // No value configured, use blank value (International jurisdiction) this.defaultJurisdiction = ''; } else { From 98ea3ebbff1a3bb74bcd72769d4c89a51f9b978a Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Thu, 3 Nov 2022 15:33:49 +0530 Subject: [PATCH 106/758] [DSC-807-MAIN] Aggiornare labels nella sezione Health della demo --- src/assets/i18n/en.json5 | 8 ++++---- src/assets/i18n/es.json5 | 8 ++++---- src/assets/i18n/hi.json5 | 8 ++++---- src/assets/i18n/kk.json5 | 16 ++++++++-------- src/assets/i18n/pt-BR.json5 | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 1c9caeba9b6..a806588701f 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1687,13 +1687,13 @@ "health-page.section.geoIp.title": "GeoIp", - "health-page.section.solrAuthorityCore.title": "Sor: authority core", + "health-page.section.solrAuthorityCore.title": "Solr: authority core", - "health-page.section.solrOaiCore.title": "Sor: oai core", + "health-page.section.solrOaiCore.title": "Solr: oai core", - "health-page.section.solrSearchCore.title": "Sor: search core", + "health-page.section.solrSearchCore.title": "Solr: search core", - "health-page.section.solrStatisticsCore.title": "Sor: statistics core", + "health-page.section.solrStatisticsCore.title": "Solr: statistics core", "health-page.section-info.app.title": "Application Backend", diff --git a/src/assets/i18n/es.json5 b/src/assets/i18n/es.json5 index 023baad6de2..02169514c45 100644 --- a/src/assets/i18n/es.json5 +++ b/src/assets/i18n/es.json5 @@ -2430,16 +2430,16 @@ // "health-page.section.geoIp.title": "GeoIp", "health-page.section.geoIp.title": "GeoIp", - // "health-page.section.solrAuthorityCore.title": "Sor: authority core", + // "health-page.section.solrAuthorityCore.title": "Solr: authority core", "health-page.section.solrAuthorityCore.title": "Solr: authority core", - // "health-page.section.solrOaiCore.title": "Sor: oai core", + // "health-page.section.solrOaiCore.title": "Solr: oai core", "health-page.section.solrOaiCore.title": "Solr: oai core", - // "health-page.section.solrSearchCore.title": "Sor: search core", + // "health-page.section.solrSearchCore.title": "Solr: search core", "health-page.section.solrSearchCore.title": "Solr: search core", - // "health-page.section.solrStatisticsCore.title": "Sor: statistics core", + // "health-page.section.solrStatisticsCore.title": "Solr: statistics core", "health-page.section.solrStatisticsCore.title": "Solr: statistics core", // "health-page.section-info.app.title": "Application Backend", diff --git a/src/assets/i18n/hi.json5 b/src/assets/i18n/hi.json5 index 13d0b10cb29..4a59dc1b1b1 100644 --- a/src/assets/i18n/hi.json5 +++ b/src/assets/i18n/hi.json5 @@ -1569,13 +1569,13 @@ "health-page.section.no-issues": "कोई समस्या नहीं मिली", - "health-page.section.solrAuthorityCore.title": "Sor: प्राधिकरण कोर", + "health-page.section.solrAuthorityCore.title": "Solr: प्राधिकरण कोर", - "health-page.section.solrOaiCore.title": "Sor: oai कोर", + "health-page.section.solrOaiCore.title": "Solr: oai कोर", - "health-page.section.solrSearchCore.title": "Sor: मूल खोज", + "health-page.section.solrSearchCore.title": "Solr: मूल खोज", - "health-page.section.solrStatisticsCore.title": "Sor: सांख्यिकी कोर", + "health-page.section.solrStatisticsCore.title": "Solr: सांख्यिकी कोर", "health-page.status": "स्थिति", diff --git a/src/assets/i18n/kk.json5 b/src/assets/i18n/kk.json5 index 66db055f50b..8e46ee73cec 100644 --- a/src/assets/i18n/kk.json5 +++ b/src/assets/i18n/kk.json5 @@ -2445,17 +2445,17 @@ // "health-page.section.geoIp.title": "GeoIp", "health-page.section.geoIp.title": "GeoIp", - // "health-page.section.solrAuthorityCore.title": "Sor: authority core", - "health-page.section.solrAuthorityCore.title": "Sor: биліктің өзегі", + // "health-page.section.solrAuthorityCore.title": "Solr: authority core", + "health-page.section.solrAuthorityCore.title": "Solr: биліктің өзегі", - // "health-page.section.solrOaiCore.title": "Sor: oai core", - "health-page.section.solrOaiCore.title": "Sor: oai өзегі", + // "health-page.section.solrOaiCore.title": "Solr: oai core", + "health-page.section.solrOaiCore.title": "Solr: oai өзегі", - // "health-page.section.solrSearchCore.title": "Sor: search core", - "health-page.section.solrSearchCore.title": "Sor: іздеу өзегі", + // "health-page.section.solrSearchCore.title": "Solr: search core", + "health-page.section.solrSearchCore.title": "Solr: іздеу өзегі", - // "health-page.section.solrStatisticsCore.title": "Sor: statistics core", - "health-page.section.solrStatisticsCore.title": "Sor: статистиканың өзегі", + // "health-page.section.solrStatisticsCore.title": "Solr: statistics core", + "health-page.section.solrStatisticsCore.title": "Solr: статистиканың өзегі", // "health-page.section-info.app.title": "Application Backend", "health-page.section-info.app.title": "Қосымшаның серверлік бөлігі", diff --git a/src/assets/i18n/pt-BR.json5 b/src/assets/i18n/pt-BR.json5 index 04b66d41d90..77b06a6566a 100644 --- a/src/assets/i18n/pt-BR.json5 +++ b/src/assets/i18n/pt-BR.json5 @@ -2378,17 +2378,17 @@ // "health-page.section.geoIp.title": "GeoIp", "health-page.section.geoIp.title": "GeoIp", - // "health-page.section.solrAuthorityCore.title": "Sor: authority core", + // "health-page.section.solrAuthorityCore.title": "Solr: authority core", "health-page.section.solrAuthorityCore.title": "Solr: authority core", - // "health-page.section.solrOaiCore.title": "Sor: oai core", + // "health-page.section.solrOaiCore.title": "Solr: oai core", "health-page.section.solrOaiCore.title": "Solr: oai core", - // "health-page.section.solrSearchCore.title": "Sor: search core", + // "health-page.section.solrSearchCore.title": "Solr: search core", "health-page.section.solrSearchCore.title": "Solr: core pesquisa", - // "health-page.section.solrStatisticsCore.title": "Sor: statistics core", - "health-page.section.solrStatisticsCore.title": "Soilr: estatisticas core", + // "health-page.section.solrStatisticsCore.title": "Solr: statistics core", + "health-page.section.solrStatisticsCore.title": "Solr: estatisticas core", // "health-page.section-info.app.title": "Application Backend", "health-page.section-info.app.title": "Aplicação de Backend", From 5c2c60db8b0e4757a2518b4c81c9cfe89c85e1bf Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 3 Nov 2022 15:08:16 +0100 Subject: [PATCH 107/758] [CST-7376] fix issue with timezone within the test class --- src/app/shared/date.util.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/shared/date.util.spec.ts b/src/app/shared/date.util.spec.ts index d7c418e8aea..992a3f31fa0 100644 --- a/src/app/shared/date.util.spec.ts +++ b/src/app/shared/date.util.spec.ts @@ -22,7 +22,7 @@ describe('Date Utils', () => { }); it('should convert NgbDateStruct to YYYY-MM-DDThh:mm:ssZ string', () => { // NOTE: month is zero indexed which is why it increases by one - const date = new Date(2022, 5, 3); + const date = new Date(Date.UTC(2022, 5, 3)) expect(dateToISOFormat(dateToNgbDateStruct(date))).toEqual('2022-06-03T00:00:00Z'); }); }); @@ -30,22 +30,22 @@ describe('Date Utils', () => { describe('dateToString', () => { it('should convert Date to YYYY-MM-DD string', () => { // NOTE: month is zero indexed which is why it increases by one - expect(dateToString(new Date(2022, 5, 3))).toEqual('2022-06-03'); + expect(dateToString(new Date(Date.UTC(2022, 5, 3)))).toEqual('2022-06-03'); }); it('should convert Date with time to YYYY-MM-DD string', () => { // NOTE: month is zero indexed which is why it increases by one - expect(dateToString(new Date(2022, 5, 3, 3, 24, 0))).toEqual('2022-06-03'); + expect(dateToString(new Date(Date.UTC(2022, 5, 3, 3, 24, 0)))).toEqual('2022-06-03'); }); it('should convert Month only to YYYY-MM-DD string', () => { // NOTE: month is zero indexed which is why it increases by one - expect(dateToString(new Date(2022, 5))).toEqual('2022-06-01'); + expect(dateToString(new Date(Date.UTC(2022, 5)))).toEqual('2022-06-01'); }); it('should convert ISO Date to YYYY-MM-DD string', () => { expect(dateToString(new Date('2022-06-03T03:24:00Z'))).toEqual('2022-06-03'); }); it('should convert NgbDateStruct to YYYY-MM-DD string', () => { // NOTE: month is zero indexed which is why it increases by one - const date = new Date(2022, 5, 3); + const date = new Date(Date.UTC(2022, 5, 3)); expect(dateToString(dateToNgbDateStruct(date))).toEqual('2022-06-03'); }); }); From da6da6209b2c362601620dd32b53951062ddc467 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 3 Nov 2022 15:46:36 +0100 Subject: [PATCH 108/758] [CST-7376] fix lint error --- src/app/shared/date.util.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/date.util.spec.ts b/src/app/shared/date.util.spec.ts index 992a3f31fa0..4576ea497c5 100644 --- a/src/app/shared/date.util.spec.ts +++ b/src/app/shared/date.util.spec.ts @@ -22,7 +22,7 @@ describe('Date Utils', () => { }); it('should convert NgbDateStruct to YYYY-MM-DDThh:mm:ssZ string', () => { // NOTE: month is zero indexed which is why it increases by one - const date = new Date(Date.UTC(2022, 5, 3)) + const date = new Date(Date.UTC(2022, 5, 3)); expect(dateToISOFormat(dateToNgbDateStruct(date))).toEqual('2022-06-03T00:00:00Z'); }); }); From 6d1d446c0d23e939d4e3eb26100feeeae9ee6cbf Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 3 Nov 2022 15:03:47 -0500 Subject: [PATCH 109/758] Update to the latest version of all GitHub actions --- .github/workflows/build.yml | 14 +++++++------- .github/workflows/docker.yml | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3dfb79ddc9b..c58e09edf24 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,11 +32,11 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v2 + uses: actions/checkout@v3 # https://github.com/actions/setup-node - name: Install Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} @@ -61,7 +61,7 @@ jobs: id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" - name: Cache Yarn dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: # Cache entire Yarn cache directory (see previous step) path: ${{ steps.yarn-cache-dir-path.outputs.dir }} @@ -88,7 +88,7 @@ jobs: # Upload coverage reports to Codecov (for one version of Node only) # https://github.com/codecov/codecov-action - name: Upload coverage to Codecov.io - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 if: matrix.node-version == '16.x' # Using docker-compose start backend using CI configuration @@ -103,7 +103,7 @@ jobs: # https://github.com/cypress-io/github-action # (NOTE: to run these e2e tests locally, just use 'ng e2e') - name: Run e2e tests (integration tests) - uses: cypress-io/github-action@v2 + uses: cypress-io/github-action@v4 with: # Run tests in Chrome, headless mode browser: chrome @@ -119,7 +119,7 @@ jobs: # Cypress always creates a video of all e2e tests (whether they succeeded or failed) # Save those in an Artifact - name: Upload e2e test videos to Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: always() with: name: e2e-test-videos @@ -128,7 +128,7 @@ jobs: # If e2e tests fail, Cypress creates a screenshot of what happened # Save those in an Artifact - name: Upload e2e test failure screenshots to Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: failure() with: name: e2e-test-screenshots diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 350fea6c344..908c5c34fdc 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -42,11 +42,11 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v2 + uses: actions/checkout@v3 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures @@ -56,7 +56,7 @@ jobs: - name: Login to DockerHub # Only login if not a PR, as PRs only trigger a Docker build and not a push if: github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -68,7 +68,7 @@ jobs: # Get Metadata for docker_build step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-angular' image id: meta_build - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: dspace/dspace-angular tags: ${{ env.IMAGE_TAGS }} @@ -77,7 +77,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push 'dspace-angular' image id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: ./Dockerfile From 9e843e36fa53c4290459c110a31be884b8867e24 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 4 Nov 2022 10:43:16 +0100 Subject: [PATCH 110/758] [1950] [DURACOM-101] Regex validator improved feat: - New regexp to validate pattern used for regex validation; --- .../form/builder/parsers/field-parser.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index 86a7d99e411..e2a907056e2 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -1,7 +1,7 @@ -import {Inject, InjectionToken} from '@angular/core'; +import { Inject, InjectionToken } from '@angular/core'; -import uniqueId from 'lodash/uniqueId'; -import {DynamicFormControlLayout, DynamicFormControlRelation, MATCH_VISIBLE, OR_OPERATOR} from '@ng-dynamic-forms/core'; +import { uniqueId } from 'lodash'; +import { DynamicFormControlLayout, DynamicFormControlRelation, MATCH_VISIBLE, OR_OPERATOR } from '@ng-dynamic-forms/core'; import { hasValue, isNotEmpty, isNotNull, isNotUndefined } from '../../../empty.util'; import { FormFieldModel } from '../models/form-field.model'; @@ -22,6 +22,7 @@ export const SUBMISSION_ID: InjectionToken = new InjectionToken( export const CONFIG_DATA: InjectionToken = new InjectionToken('configData'); export const INIT_FORM_VALUES: InjectionToken = new InjectionToken('initFormValues'); export const PARSER_OPTIONS: InjectionToken = new InjectionToken('parserOptions'); +export const REGEX_FIELD_VALIDATOR: RegExp = new RegExp('(\\/?)(.+)\\1([gimsuy]*)', 'i'); export abstract class FieldParser { @@ -43,7 +44,7 @@ export abstract class FieldParser { public abstract modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any; public parse() { - if (((this.getInitValueCount() > 1 && !this.configData.repeatable) || (this.configData.repeatable)) + if (((this.getInitValueCount() > 1 && !this.configData.repeatable) || (this.configData.repeatable)) && (this.configData.input.type !== ParserType.List) && (this.configData.input.type !== ParserType.Tag) ) { @@ -315,6 +316,7 @@ export abstract class FieldParser { * fields in type bind, made up of a 'match' outcome (make this field visible), an 'operator' * (OR) and a 'when' condition (the bindValues array). * @param configuredTypeBindValues array of types from the submission definition (CONFIG_DATA) + * @param typeField * @private * @return DynamicFormControlRelation[] array with one relation in it, for type bind matching to show a field */ @@ -344,7 +346,13 @@ export abstract class FieldParser { } protected addPatternValidator(controlModel) { - const regex = new RegExp(this.configData.input.regex); + const validatorMatcher = this.configData.input.regex.match(REGEX_FIELD_VALIDATOR); + let regex; + if (validatorMatcher != null && validatorMatcher.length > 3) { + regex = new RegExp(validatorMatcher[2], validatorMatcher[3]); + } else { + regex = new RegExp(this.configData.input.regex); + } controlModel.validators = Object.assign({}, controlModel.validators, { pattern: regex }); controlModel.errorMessages = Object.assign( {}, From 2ed8f8bad85a5c57299e34e84f6da1ea2150e249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Fern=C3=A1ndez=20Celorio?= Date: Fri, 4 Nov 2022 10:40:29 +0000 Subject: [PATCH 111/758] Numbre of bitstreams in item page is now configurable --- config/config.example.yml | 7 ++++ .../full-file-section.component.ts | 33 ++++++++++--------- .../file-section/file-section.component.ts | 9 +++-- src/config/default-app-config.ts | 11 ++++++- src/config/item-config.interface.ts | 11 +++++++ src/environments/environment.test.ts | 11 ++++++- 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 27400f00411..52a07202308 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -207,6 +207,13 @@ item: undoTimeout: 10000 # 10 seconds # Show the item access status label in items lists showAccessStatuses: false + simpleView: + bitstreamPageSize: 5 + fullView: + # Number of entries in the bitstream list in the full item view page. + # Rounded to the nearest size in the list of selectable sizes on the + # settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. + bitstreamPageSize: 5 # Collection Page Config collection: 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 e21c1a32eb3..89134a321e3 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 @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; @@ -14,6 +14,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat 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'; /** * This component renders the file section of the item @@ -34,26 +35,28 @@ export class FullFileSectionComponent extends FileSectionComponent implements On originals$: Observable>>; licenses$: Observable>>; - pageSize = 5; - originalOptions = Object.assign(new PaginationComponentOptions(), { - id: 'obo', - currentPage: 1, - pageSize: this.pageSize - }); - - licenseOptions = Object.assign(new PaginationComponentOptions(), { - id: 'lbo', - currentPage: 1, - pageSize: this.pageSize - }); + originalOptions: PaginationComponentOptions; + licenseOptions: PaginationComponentOptions; constructor( bitstreamDataService: BitstreamDataService, protected notificationsService: NotificationsService, protected translateService: TranslateService, - protected paginationService: PaginationService + protected paginationService: PaginationService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(bitstreamDataService, notificationsService, translateService); + super(bitstreamDataService, notificationsService, translateService, appConfig); + this.originalOptions = Object.assign(new PaginationComponentOptions(), { + id: 'obo', + currentPage: 1, + pageSize: this.appConfig.item.fullView.bitstreamPageSize + }); + + this.licenseOptions = Object.assign(new PaginationComponentOptions(), { + id: 'lbo', + currentPage: 1, + pageSize: this.appConfig.item.fullView.bitstreamPageSize + }); } ngOnInit(): void { diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.ts index d28b5799963..a0e40376e5b 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; @@ -10,6 +10,7 @@ import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; +import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; /** * This component renders the file section of the item @@ -35,13 +36,15 @@ export class FileSectionComponent implements OnInit { isLastPage: boolean; - pageSize = 5; + pageSize: number; constructor( protected bitstreamDataService: BitstreamDataService, protected notificationsService: NotificationsService, - protected translateService: TranslateService + protected translateService: TranslateService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { + this.pageSize = this.appConfig.item.simpleView.bitstreamPageSize } ngOnInit(): void { diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index f489abc53b9..3940ea1c191 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -245,7 +245,16 @@ export class DefaultAppConfig implements AppConfig { undoTimeout: 10000 // 10 seconds }, // Show the item access status label in items lists - showAccessStatuses: false + showAccessStatuses: false, + simpleView: { + bitstreamPageSize: 5 + }, + fullView: { + // Number of entries in the bitstream list in the full item view page. + // Rounded to the nearest size in the list of selectable sizes on the + // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. + bitstreamPageSize: 5 + } }; // Collection Page Config diff --git a/src/config/item-config.interface.ts b/src/config/item-config.interface.ts index f842c37c053..1351911334d 100644 --- a/src/config/item-config.interface.ts +++ b/src/config/item-config.interface.ts @@ -6,4 +6,15 @@ export interface ItemConfig extends Config { }; // This is used to show the access status label of items in results lists showAccessStatuses: boolean; + + simpleView: { + bitstreamPageSize: number; + }; + + fullView: { + // Number of entries in the bitstream list in the full item view page. + // Rounded to the nearest size in the list of selectable sizes on the + // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. + bitstreamPageSize: number; + } } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index edd1cb91090..7410b17f372 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -229,7 +229,16 @@ export const environment: BuildConfig = { undoTimeout: 10000 // 10 seconds }, // Show the item access status label in items lists - showAccessStatuses: false + showAccessStatuses: false, + simpleView: { + bitstreamPageSize: 5 + }, + fullView: { + // Number of entries in the bitstream list in the full item view page. + // Rounded to the nearest size in the list of selectable sizes on the + // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. + bitstreamPageSize: 5 + } }, collection: { edit: { From 87a211ca669b4f882a56661572887f49ae8fcbe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Fern=C3=A1ndez=20Celorio?= Date: Mon, 7 Nov 2022 12:20:00 +0100 Subject: [PATCH 112/758] Tests fixed --- .../file-section/full-file-section.component.spec.ts | 5 ++++- .../file-section/file-section.component.spec.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) 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 396e6c32160..3a8e45475de 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 @@ -21,6 +21,8 @@ import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-o 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 { APP_CONFIG } from 'src/config/app-config.interface'; +import { environment } from 'src/environments/environment'; describe('FullFileSectionComponent', () => { let comp: FullFileSectionComponent; @@ -72,7 +74,8 @@ describe('FullFileSectionComponent', () => { providers: [ { provide: BitstreamDataService, useValue: bitstreamDataService }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: APP_CONFIG, useValue: environment }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts index 2d185aef9ca..83f92d5af8b 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts @@ -17,6 +17,8 @@ import { MetadataFieldWrapperComponent } from '../../../field-components/metadat import { createPaginatedList } from '../../../../shared/testing/utils.test'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; +import { APP_CONFIG } from 'src/config/app-config.interface'; +import { environment } from 'src/environments/environment'; describe('FileSectionComponent', () => { let comp: FileSectionComponent; @@ -65,7 +67,8 @@ describe('FileSectionComponent', () => { declarations: [FileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent], providers: [ { provide: BitstreamDataService, useValue: bitstreamDataService }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] From 1e523382d06633aaf0c88b700b2a953aeedd9423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Fern=C3=A1ndez=20Celorio?= Date: Mon, 7 Nov 2022 16:50:46 +0100 Subject: [PATCH 113/758] Fix lint error --- .../field-components/file-section/file-section.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.ts index a0e40376e5b..2907a334828 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.ts @@ -44,7 +44,7 @@ export class FileSectionComponent implements OnInit { protected translateService: TranslateService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - this.pageSize = this.appConfig.item.simpleView.bitstreamPageSize + this.pageSize = this.appConfig.item.simpleView.bitstreamPageSize; } ngOnInit(): void { From 6c226be7e80e3364faba8dc0313dde5fd89ab33a Mon Sep 17 00:00:00 2001 From: Pierre Lasou Date: Mon, 7 Nov 2022 13:31:45 -0500 Subject: [PATCH 114/758] Changes to fr.json5 New commit to include changes after review of commit 3bf13c09617a1af4b4dff3b18380b55848c1bb2a --- src/assets/i18n/fr.json5 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5 index f2e28184a59..11d15ec6db0 100644 --- a/src/assets/i18n/fr.json5 +++ b/src/assets/i18n/fr.json5 @@ -4027,10 +4027,10 @@ "process.overview.delete": "Supprimer {{count}} processus", // "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}} processus sont en train d'être supprimer. Patientez jusqu'à ce que la suppression soit terminée. Notez que cela peut prendre un certain temps.", + "process.overview.delete.processing": "{{count}} processus sont en train d'être supprimés. Patientez jusqu'à ce que la suppression soit terminée. Notez que cela peut prendre un certain temps.", // "process.overview.delete.body": "Are you sure you want to delete {{count}} process(es)?", - "process.overview.delete.body": "Êtes vous sûr de vouloir supprimer {{count}} processus?", + "process.overview.delete.body": "Êtes-vous certain-e de vouloir supprimer {{count}} processus?", // "process.overview.delete.header": "Delete processes", "process.overview.delete.header": "Supprimer les processus", @@ -4039,7 +4039,7 @@ "process.bulk.delete.error.head": "Erreur lors de la suppression de processus", // "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": "Le processus numéro {{processId}} n'a pu être supprimé. Les processus restants continuent à être supprimés. ", + "process.bulk.delete.error.body": "Le processus numéro {{processId}} n'a pu être supprimé. Les processus restants continueront à être supprimés. ", // "process.bulk.delete.success": "{{count}} process(es) have been succesfully deleted", "process.bulk.delete.success": "{{count}} processus ont été supprimés.", @@ -5767,13 +5767,13 @@ "submission.sections.sherpa.publisher.policy": "Politique de l'éditeur", // "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": "L'information ci-dessous provient de Sherpa Romeo. Elle vous permet de savoir quelle version vous êtes autorisé-e à déposer et si une restriction de diffusion (embargo) doit être appliquée. Si vous avez des questions, contactez votre administrateur en utilisant le formulaire en pied de page.", + "submission.sections.sherpa.publisher.policy.description": "L'information ci-dessous provient de Sherpa Romeo. Elle vous permet de savoir quelle version vous êtes autorisé-e à déposer et si une restriction de diffusion (embargo) doit être appliquée. Si vous avez des questions, contactez votre administrateur-trice en utilisant le formulaire en pied de page.", // "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": "Les voies de libre accès autorisées par la politique de cette revue sont listées ci-dessous par version d'article. Cliquez sur l'une des voies pour plus de détails.", // "submission.sections.sherpa.publisher.policy.more.information": "For more information, please see the following links:", - "submission.sections.sherpa.publisher.policy.more.information": "Pour plus d'information, cliquez sur le lien suivant:", + "submission.sections.sherpa.publisher.policy.more.information": "Pour plus d'information, cliquez sur le lien suivant :", // "submission.sections.sherpa.publisher.policy.version": "Version", "submission.sections.sherpa.publisher.policy.version": "Version", @@ -5788,7 +5788,7 @@ "submission.sections.sherpa.publisher.policy.nolocation": "Aucun", // "submission.sections.sherpa.publisher.policy.license": "License", - "submission.sections.sherpa.publisher.policy.license": "License", + "submission.sections.sherpa.publisher.policy.license": "Licence", // "submission.sections.sherpa.publisher.policy.prerequisites": "Prerequisites", "submission.sections.sherpa.publisher.policy.prerequisites": "Prérequis", @@ -5909,7 +5909,7 @@ "submission.workspace.generic.view": "Voir", // "submission.workspace.generic.view-help": "Select this option to view the item's metadata.", - "submission.workspace.generic.view-help": "Sélecionner cette option pour voir les métadonnées de l'item.", + "submission.workspace.generic.view-help": "Sélectionner cette option pour voir les métadonnées de l'item.", // "thumbnail.default.alt": "Thumbnail Image", "thumbnail.default.alt": "Vignette d'image", From f7e78dccd129458c6f4828e8ac1fe309d925a64a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Fern=C3=A1ndez=20Celorio?= Date: Mon, 7 Nov 2022 19:27:54 +0000 Subject: [PATCH 115/758] Use the same property for both item pages --- config/config.example.yml | 6 ++--- .../full-file-section.component.ts | 24 +++++++++---------- .../file-section/file-section.component.ts | 2 +- src/config/default-app-config.ts | 9 +++---- src/config/item-config.interface.ts | 10 +++----- src/environments/environment.test.ts | 9 +++---- 6 files changed, 23 insertions(+), 37 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 52a07202308..bb4c691f6bb 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -207,13 +207,11 @@ item: undoTimeout: 10000 # 10 seconds # Show the item access status label in items lists showAccessStatuses: false - simpleView: - bitstreamPageSize: 5 - fullView: + bitstream: # Number of entries in the bitstream list in the full item view page. # Rounded to the nearest size in the list of selectable sizes on the # settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. - bitstreamPageSize: 5 + pageSize: 5 # Collection Page Config collection: 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 89134a321e3..3be0d58c819 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 @@ -35,8 +35,17 @@ export class FullFileSectionComponent extends FileSectionComponent implements On originals$: Observable>>; licenses$: Observable>>; - originalOptions: PaginationComponentOptions; - licenseOptions: PaginationComponentOptions; + originalOptions = Object.assign(new PaginationComponentOptions(), { + id: 'obo', + currentPage: 1, + pageSize: this.appConfig.item.bitstream.pageSize + }); + + licenseOptions = Object.assign(new PaginationComponentOptions(), { + id: 'lbo', + currentPage: 1, + pageSize: this.appConfig.item.bitstream.pageSize + }); constructor( bitstreamDataService: BitstreamDataService, @@ -46,17 +55,6 @@ export class FullFileSectionComponent extends FileSectionComponent implements On @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(bitstreamDataService, notificationsService, translateService, appConfig); - this.originalOptions = Object.assign(new PaginationComponentOptions(), { - id: 'obo', - currentPage: 1, - pageSize: this.appConfig.item.fullView.bitstreamPageSize - }); - - this.licenseOptions = Object.assign(new PaginationComponentOptions(), { - id: 'lbo', - currentPage: 1, - pageSize: this.appConfig.item.fullView.bitstreamPageSize - }); } ngOnInit(): void { diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.ts index 2907a334828..08e792fc8b7 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.ts @@ -44,7 +44,7 @@ export class FileSectionComponent implements OnInit { protected translateService: TranslateService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - this.pageSize = this.appConfig.item.simpleView.bitstreamPageSize; + this.pageSize = this.appConfig.item.bitstream.pageSize; } ngOnInit(): void { diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 3940ea1c191..f87a5b868b8 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -246,14 +246,11 @@ export class DefaultAppConfig implements AppConfig { }, // Show the item access status label in items lists showAccessStatuses: false, - simpleView: { - bitstreamPageSize: 5 - }, - fullView: { - // Number of entries in the bitstream list in the full item view page. + bitstream: { + // Number of entries in the bitstream list in the item view page. // Rounded to the nearest size in the list of selectable sizes on the // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. - bitstreamPageSize: 5 + pageSize: 5 } }; diff --git a/src/config/item-config.interface.ts b/src/config/item-config.interface.ts index 1351911334d..35cb5260aea 100644 --- a/src/config/item-config.interface.ts +++ b/src/config/item-config.interface.ts @@ -7,14 +7,10 @@ export interface ItemConfig extends Config { // This is used to show the access status label of items in results lists showAccessStatuses: boolean; - simpleView: { - bitstreamPageSize: number; - }; - - fullView: { - // Number of entries in the bitstream list in the full item view page. + bitstream: { + // Number of entries in the bitstream list in the item view page. // Rounded to the nearest size in the list of selectable sizes on the // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. - bitstreamPageSize: number; + pageSize: number; } } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 7410b17f372..ec49ff60091 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -230,14 +230,11 @@ export const environment: BuildConfig = { }, // Show the item access status label in items lists showAccessStatuses: false, - simpleView: { - bitstreamPageSize: 5 - }, - fullView: { - // Number of entries in the bitstream list in the full item view page. + bitstream: { + // Number of entries in the bitstream list in the item view page. // Rounded to the nearest size in the list of selectable sizes on the // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. - bitstreamPageSize: 5 + pageSize: 5 } }, collection: { From cc86ba539fc0d66ddcaf0777c2a0fb1e3476b15f Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 24 Oct 2022 16:59:51 -0500 Subject: [PATCH 116/758] Turn on no-unused-imports rule and remove all unused imports --- .eslintrc.json | 4 ++-- .../metadata-import-page.component.ts | 6 +----- .../bitstream-formats/bitstream-formats.component.ts | 3 +-- .../metadata-registry/metadata-registry.component.spec.ts | 3 --- .../metadata-schema/metadata-schema.component.spec.ts | 3 --- .../admin/admin-sidebar/admin-sidebar.component.spec.ts | 1 - src/app/app.component.ts | 1 - .../bitstream-page/legacy-bitstream-url.resolver.spec.ts | 2 +- src/app/browse-by/browse-by-guard.spec.ts | 1 - .../collection-item-mapper.component.spec.ts | 4 ++-- .../collection-source/collection-source.component.ts | 3 +-- .../community-page-sub-collection-list.component.spec.ts | 4 ---- .../community-page-sub-community-list.component.spec.ts | 3 --- src/app/core/auth/selectors.ts | 1 - src/app/core/shared/hal-endpoint.service.spec.ts | 2 +- src/app/core/shared/search/search.service.ts | 1 - src/app/correlation-id/correlation-id.service.spec.ts | 2 +- src/app/curation-form/curation-form.component.ts | 2 +- .../top-level-community-list.component.spec.ts | 3 --- .../item-authorizations.component.spec.ts | 7 ++----- ...aginated-drag-and-drop-bitstream-list.component.spec.ts | 3 --- .../file-section/full-file-section.component.spec.ts | 3 --- .../simple/item-types/shared/item.component.spec.ts | 1 - .../my-dspace-page/my-dspace-configuration.service.spec.ts | 4 ---- .../process-page/detail/process-detail.component.spec.ts | 1 - src/app/register-page/registration.guard.spec.ts | 1 - .../delete-comcol-page.component.spec.ts | 1 - .../ds-dynamic-type-bind-relation.service.spec.ts | 2 +- .../shared/interfaces/modal-before-dismiss.interface.ts | 1 - src/app/shared/menu/menu-item/link-menu-item.component.ts | 2 +- src/app/shared/menu/menu-item/text-menu-item.component.ts | 2 +- .../approve/claimed-task-actions-approve.component.spec.ts | 2 +- .../listable-object/listable-object.decorator.spec.ts | 1 - .../sidebar-search-list-element.component.spec.ts | 1 - ...abstract-paginated-drag-and-drop-list.component.spec.ts | 2 -- src/app/shared/pagination/pagination.component.spec.ts | 3 +-- .../search-label/search-label.component.spec.ts | 2 -- .../starts-with/date/starts-with-date.component.spec.ts | 3 --- .../starts-with/text/starts-with-text.component.spec.ts | 6 +----- src/app/shared/testing/group-mock.ts | 1 - ...submission-import-external-collection.component.spec.ts | 2 +- .../submission/submit/submission-submit.component.spec.ts | 2 +- 42 files changed, 21 insertions(+), 81 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 44f53ecb585..b95b54b979a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,7 @@ "eslint-plugin-import", "eslint-plugin-jsdoc", "eslint-plugin-deprecation", - "eslint-plugin-unused-imports", + "unused-imports", "eslint-plugin-lodash" ], "overrides": [ @@ -205,7 +205,7 @@ "import/order": "off", "import/no-deprecated": "warn", "import/no-namespace": "error", - + "unused-imports/no-unused-imports": "error", "lodash/import-scope": [ "error", "method" diff --git a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts index deb16c0d732..4236d152dcb 100644 --- a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts +++ b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts @@ -1,12 +1,8 @@ import { Location } from '@angular/common'; -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; -import { AuthService } from '../../core/auth/auth.service'; import { METADATA_IMPORT_SCRIPT_NAME, ScriptDataService } from '../../core/data/processes/script-data.service'; -import { EPerson } from '../../core/eperson/models/eperson.model'; import { ProcessParameter } from '../../process-page/processes/process-parameter.model'; import { isNotEmpty } from '../../shared/empty.util'; import { NotificationsService } from '../../shared/notifications/notifications.service'; 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 7fcc86e25d1..162bf2bdb28 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 @@ -1,12 +1,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import {combineLatest, combineLatest as observableCombineLatest, Observable, of as observableOf, zip} from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable} from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { BitstreamFormat } from '../../../core/shared/bitstream-format.model'; import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service'; import { map, mergeMap, switchMap, take, toArray } 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'; 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 74bfc5f0a49..944288a7a51 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 @@ -19,10 +19,7 @@ import { RestResponse } from '../../../core/cache/response.models'; import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { PaginationService } from '../../../core/pagination/pagination.service'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('MetadataRegistryComponent', () => { let comp: MetadataRegistryComponent; 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 d63b4ed84f5..2b660a63634 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 @@ -23,11 +23,8 @@ import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; import { MetadataField } from '../../../core/metadata/metadata-field.model'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { VarDirective } from '../../../shared/utils/var.directive'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-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 { FindListOptions } from '../../../core/data/find-list-options.model'; describe('MetadataSchemaComponent', () => { let comp: MetadataSchemaComponent; diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts index cbcfae43d8c..88efd2a711e 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts @@ -16,7 +16,6 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { RouterTestingModule } from '@angular/router/testing'; import { ActivatedRoute } from '@angular/router'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import createSpy = jasmine.createSpy; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { Item } from '../../core/shared/item.model'; diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 40a7e38606e..ba7b7382278 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -31,7 +31,6 @@ import { models } from './core/core.module'; import { ThemeService } from './shared/theme-support/theme.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { distinctNext } from './core/shared/distinct-next'; -import { ModalBeforeDismiss } from './shared/interfaces/modal-before-dismiss.interface'; @Component({ selector: 'ds-app', diff --git a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts index 045582cb26e..aec8cd22f44 100644 --- a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts +++ b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts @@ -1,5 +1,5 @@ import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver'; -import { of as observableOf, EMPTY } from 'rxjs'; +import { EMPTY } from 'rxjs'; import { BitstreamDataService } from '../core/data/bitstream-data.service'; import { RemoteData } from '../core/data/remote-data'; import { TestScheduler } from 'rxjs/testing'; diff --git a/src/app/browse-by/browse-by-guard.spec.ts b/src/app/browse-by/browse-by-guard.spec.ts index fc483d87e23..933c95a3cb0 100644 --- a/src/app/browse-by/browse-by-guard.spec.ts +++ b/src/app/browse-by/browse-by-guard.spec.ts @@ -1,7 +1,6 @@ import { first } from 'rxjs/operators'; import { BrowseByGuard } from './browse-by-guard'; import { of as observableOf } from 'rxjs'; -import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service'; import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { BrowseDefinition } from '../core/shared/browse-definition.model'; import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator'; diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts index 8304b01daaa..db844b588f9 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts @@ -16,7 +16,7 @@ import { Collection } from '../../core/shared/collection.model'; import { RemoteData } from '../../core/data/remote-data'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { ChangeDetectionStrategy, EventEmitter } from '@angular/core'; +import { EventEmitter } from '@angular/core'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { By } from '@angular/platform-browser'; @@ -41,7 +41,7 @@ import { } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; +import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; import { GroupDataService } from '../../core/eperson/group-data.service'; import { LinkHeadService } from '../../core/services/link-head.service'; diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts index 51e8d926eb8..512faa53118 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts @@ -8,8 +8,7 @@ import { DynamicInputModel, DynamicOptionControlModel, DynamicRadioGroupModel, - DynamicSelectModel, - DynamicTextAreaModel + DynamicSelectModel } from '@ng-dynamic-forms/core'; import { Location } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts index 6a9de52f1f2..bca3c42a950 100644 --- a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts +++ b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts @@ -17,9 +17,6 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { of as observableOf } from 'rxjs'; import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; @@ -29,7 +26,6 @@ import { GroupDataService } from '../../core/eperson/group-data.service'; import { LinkHeadService } from '../../core/services/link-head.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; -import { SearchServiceStub } from '../../shared/testing/search-service.stub'; import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts index c75c5b6f6c3..0a14fe6dd14 100644 --- a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts +++ b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts @@ -17,9 +17,6 @@ import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { CommunityDataService } from '../../core/data/community-data.service'; import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { of as observableOf } from 'rxjs'; import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; diff --git a/src/app/core/auth/selectors.ts b/src/app/core/auth/selectors.ts index 1d002b39082..ce8d38d6ba5 100644 --- a/src/app/core/auth/selectors.ts +++ b/src/app/core/auth/selectors.ts @@ -7,7 +7,6 @@ import { createSelector } from '@ngrx/store'; * notation packages up all of the exports into a single object. */ import { AuthState } from './auth.reducer'; -import { AppState } from '../../app.reducer'; import { CoreState } from '../core-state.model'; import { coreSelector } from '../core.selectors'; diff --git a/src/app/core/shared/hal-endpoint.service.spec.ts b/src/app/core/shared/hal-endpoint.service.spec.ts index 78a296496a7..56e890b3189 100644 --- a/src/app/core/shared/hal-endpoint.service.spec.ts +++ b/src/app/core/shared/hal-endpoint.service.spec.ts @@ -3,7 +3,7 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from './hal-endpoint.service'; import { EndpointMapRequest } from '../data/request.models'; -import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; +import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs'; import { environment } from '../../../environments/environment'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 2b1c6a573b9..9befd8ea134 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -3,7 +3,6 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { Injectable, OnDestroy } from '@angular/core'; import { map, switchMap, take } from 'rxjs/operators'; import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; -import { PaginatedList } from '../../data/paginated-list.model'; import { ResponseParsingService } from '../../data/parsing.service'; import { RemoteData } from '../../data/remote-data'; import { GetRequest } from '../../data/request.models'; diff --git a/src/app/correlation-id/correlation-id.service.spec.ts b/src/app/correlation-id/correlation-id.service.spec.ts index 64a4d1068a1..816c3694a5c 100644 --- a/src/app/correlation-id/correlation-id.service.spec.ts +++ b/src/app/correlation-id/correlation-id.service.spec.ts @@ -1,7 +1,7 @@ import { CorrelationIdService } from './correlation-id.service'; import { CookieServiceMock } from '../shared/mocks/cookie.service.mock'; import { UUIDService } from '../core/shared/uuid.service'; -import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { MockStore } from '@ngrx/store/testing'; import { TestBed } from '@angular/core/testing'; import { Store, StoreModule } from '@ngrx/store'; import { appReducers, AppState, storeModuleConfig } from '../app.reducer'; diff --git a/src/app/curation-form/curation-form.component.ts b/src/app/curation-form/curation-form.component.ts index 422c9550373..4b67580e770 100644 --- a/src/app/curation-form/curation-form.component.ts +++ b/src/app/curation-form/curation-form.component.ts @@ -5,7 +5,7 @@ import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { find, map } from 'rxjs/operators'; import { NotificationsService } from '../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { hasValue, isEmpty, isNotEmpty, hasNoValue } from '../shared/empty.util'; +import { hasValue, isEmpty, isNotEmpty } from '../shared/empty.util'; import { RemoteData } from '../core/data/remote-data'; import { Router } from '@angular/router'; import { ProcessDataService } from '../core/data/processes/process-data.service'; diff --git a/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts b/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts index 5dbf1ff8a27..d1a3d3631f1 100644 --- a/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts +++ b/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts @@ -17,9 +17,6 @@ import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { CommunityDataService } from '../../core/data/community-data.service'; import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { of as observableOf } from 'rxjs'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts index 2fe8a562c67..5d2afbaf4ce 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts @@ -1,12 +1,11 @@ -import { Observable } from 'rxjs/internal/Observable'; import { waitForAsync, ComponentFixture, inject, TestBed } from '@angular/core/testing'; import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { of as observableOf, of } from 'rxjs'; +import { of as observableOf } from 'rxjs'; import { TranslateModule } from '@ngx-translate/core'; import { cold } from 'jasmine-marbles'; -import { ItemAuthorizationsComponent, BitstreamMapValue } from './item-authorizations.component'; +import { ItemAuthorizationsComponent } from './item-authorizations.component'; import { Bitstream } from '../../../core/shared/bitstream.model'; import { Bundle } from '../../../core/shared/bundle.model'; import { Item } from '../../../core/shared/item.model'; @@ -14,8 +13,6 @@ import { LinkService } from '../../../core/cache/builders/link.service'; import { getMockLinkService } from '../../../shared/mocks/link-service.mock'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { createPaginatedList, createTestComponent } from '../../../shared/testing/utils.test'; -import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated-list.model'; -import { PageInfo } from '../../../core/shared/page-info.model'; describe('ItemAuthorizationsComponent test suite', () => { let comp: ItemAuthorizationsComponent; 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 133b13cb279..7317eb93be0 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 @@ -17,10 +17,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { createPaginatedList } from '../../../../../shared/testing/utils.test'; import { RequestService } from '../../../../../core/data/request.service'; import { PaginationService } from '../../../../../core/pagination/pagination.service'; -import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../../../core/cache/models/sort-options.model'; import { PaginationServiceStub } from '../../../../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../../../../core/data/find-list-options.model'; describe('PaginatedDragAndDropBitstreamListComponent', () => { let comp: PaginatedDragAndDropBitstreamListComponent; 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 396e6c32160..1059ed12da3 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 @@ -16,11 +16,8 @@ import { MockBitstreamFormat1 } from '../../../../shared/mocks/item.mock'; import { By } from '@angular/platform-browser'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; -import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-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 { FindListOptions } from '../../../../core/data/find-list-options.model'; describe('FullFileSectionComponent', () => { let comp: FullFileSectionComponent; diff --git a/src/app/item-page/simple/item-types/shared/item.component.spec.ts b/src/app/item-page/simple/item-types/shared/item.component.spec.ts index b14e56a58e1..c479f048090 100644 --- a/src/app/item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/item-page/simple/item-types/shared/item.component.spec.ts @@ -28,7 +28,6 @@ import { TruncatableService } from '../../../../shared/truncatable/truncatable.s import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils'; -import { ItemComponent } from './item.component'; import { createPaginatedList } from '../../../../shared/testing/utils.test'; import { RouteService } from '../../../../core/services/route.service'; import { MetadataValue } from '../../../../core/shared/metadata.models'; 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 b87c77c5e99..669a97764a5 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 @@ -11,10 +11,6 @@ import { cold, hot } from 'jasmine-marbles'; import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; import { Context } from '../core/shared/context.model'; -import { LinkService } from '../core/cache/builders/link.service'; -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 { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service.stub'; import { getMockRemoteDataBuildService } from '../shared/mocks/remote-data-build.service.mock'; diff --git a/src/app/process-page/detail/process-detail.component.spec.ts b/src/app/process-page/detail/process-detail.component.spec.ts index e4ab7d1082d..8749553eaeb 100644 --- a/src/app/process-page/detail/process-detail.component.spec.ts +++ b/src/app/process-page/detail/process-detail.component.spec.ts @@ -15,7 +15,6 @@ import { } from '@angular/core/testing'; import { VarDirective } from '../../shared/utils/var.directive'; import { TranslateModule } from '@ngx-translate/core'; -import { RouterTestingModule } from '@angular/router/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ProcessDetailFieldComponent } from './process-detail-field/process-detail-field.component'; import { Process } from '../processes/process.model'; diff --git a/src/app/register-page/registration.guard.spec.ts b/src/app/register-page/registration.guard.spec.ts index 89eaff7a028..9fb8dd3a338 100644 --- a/src/app/register-page/registration.guard.spec.ts +++ b/src/app/register-page/registration.guard.spec.ts @@ -2,7 +2,6 @@ import { RegistrationGuard } from './registration.guard'; import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { AuthService } from '../core/auth/auth.service'; -import { Location } from '@angular/common'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject, diff --git a/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts index bc73e4134b5..1040e31c579 100644 --- a/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts @@ -11,7 +11,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { DeleteComColPageComponent } from './delete-comcol-page.component'; import { NotificationsService } from '../../../notifications/notifications.service'; import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; -import { RequestService } from '../../../../core/data/request.service'; import { getTestScheduler } from 'jasmine-marbles'; import { ComColDataService } from '../../../../core/data/comcol-data.service'; import { createFailedRemoteDataObject$, createNoContentRemoteDataObject$ } from '../../../remote-data.utils'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts index f8bc7ea8867..7f0c7e2e359 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts @@ -10,7 +10,7 @@ import { } from '@ng-dynamic-forms/core'; import { - mockInputWithTypeBindModel, MockRelationModel, mockDcTypeInputModel + mockInputWithTypeBindModel, MockRelationModel } from '../../../mocks/form-models.mock'; import {DsDynamicTypeBindRelationService} from './ds-dynamic-type-bind-relation.service'; import {FormFieldMetadataValueObject} from '../models/form-field-metadata-value.model'; diff --git a/src/app/shared/interfaces/modal-before-dismiss.interface.ts b/src/app/shared/interfaces/modal-before-dismiss.interface.ts index fca28e1cffa..f884432fb8b 100644 --- a/src/app/shared/interfaces/modal-before-dismiss.interface.ts +++ b/src/app/shared/interfaces/modal-before-dismiss.interface.ts @@ -1,4 +1,3 @@ -import { NgbModalConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'; /** * If a component implementing this interface is used to create a modal (i.e. it is passed to {@link NgbModal#open}), diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.ts b/src/app/shared/menu/menu-item/link-menu-item.component.ts index c9a60f0c286..78d5b2fc6f0 100644 --- a/src/app/shared/menu/menu-item/link-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/link-menu-item.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { LinkMenuItemModel } from './models/link.model'; import { rendersMenuItemForType } from '../menu-item.decorator'; import { isNotEmpty } from '../../empty.util'; diff --git a/src/app/shared/menu/menu-item/text-menu-item.component.ts b/src/app/shared/menu/menu-item/text-menu-item.component.ts index af690d198c4..25549f53a89 100644 --- a/src/app/shared/menu/menu-item/text-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/text-menu-item.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Input } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { TextMenuItemModel } from './models/text.model'; import { rendersMenuItemForType } from '../menu-item.decorator'; import { MenuItemType } from '../menu-item-type.model'; diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts index ce67da1349b..f43316c4e15 100644 --- a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { of, of as observableOf } from 'rxjs'; diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts b/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts index eb5219fab8a..f7d00510f68 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts @@ -1,5 +1,4 @@ /* eslint-disable max-classes-per-file */ -import { Item } from '../../../../core/shared/item.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { DEFAULT_VIEW_MODE, getListableObjectComponent, listableObjectComponent } from './listable-object.decorator'; import { Context } from '../../../../core/shared/context.model'; diff --git a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts index 897ec43491a..226c1be33ef 100644 --- a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts +++ b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts @@ -11,7 +11,6 @@ import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; import { HALResource } from '../../../core/shared/hal-resource.model'; import { ChildHALResource } from '../../../core/shared/child-hal-resource.model'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; -import { DSONameServiceMock } from '../../mocks/dso-name.service.mock'; export function createSidebarSearchListElementTests( componentClass: any, 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 7db53425d5b..bac6b89583f 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 @@ -11,8 +11,6 @@ import { createSuccessfulRemoteDataObject } from '../remote-data.utils'; import { createPaginatedList } from '../testing/utils.test'; import { ObjectValuesPipe } from '../utils/object-values-pipe'; import { PaginationService } from '../../core/pagination/pagination.service'; -import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; import { FieldUpdates } from '../../core/data/object-updates/field-updates.model'; diff --git a/src/app/shared/pagination/pagination.component.spec.ts b/src/app/shared/pagination/pagination.component.spec.ts index 2d913da8a32..30ace4b2b9b 100644 --- a/src/app/shared/pagination/pagination.component.spec.ts +++ b/src/app/shared/pagination/pagination.component.spec.ts @@ -32,8 +32,7 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options import { createTestComponent } from '../testing/utils.test'; import { storeModuleConfig } from '../../app.reducer'; import { PaginationService } from '../../core/pagination/pagination.service'; -import { BehaviorSubject, of as observableOf } from 'rxjs'; -import { PaginationServiceStub } from '../testing/pagination-service.stub'; +import { BehaviorSubject } from 'rxjs'; import { FindListOptions } from '../../core/data/find-list-options.model'; function expectPages(fixture: ComponentFixture, pagesDef: string[]): void { diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts index 50bcbc69381..b2be2ae53f0 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts +++ b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts @@ -12,11 +12,9 @@ import { SearchServiceStub } from '../../../testing/search-service.stub'; import { SearchConfigurationServiceStub } from '../../../testing/search-configuration-service.stub'; import { SearchService } from '../../../../core/shared/search/search.service'; import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-options.model'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { PaginationServiceStub } from '../../../testing/pagination-service.stub'; -import { FindListOptions } from '../../../../core/data/find-list-options.model'; describe('SearchLabelComponent', () => { let comp: SearchLabelComponent; diff --git a/src/app/shared/starts-with/date/starts-with-date.component.spec.ts b/src/app/shared/starts-with/date/starts-with-date.component.spec.ts index 3cd22a625ff..2407f21fdf3 100644 --- a/src/app/shared/starts-with/date/starts-with-date.component.spec.ts +++ b/src/app/shared/starts-with/date/starts-with-date.component.spec.ts @@ -11,11 +11,8 @@ import { StartsWithDateComponent } from './starts-with-date.component'; import { ActivatedRouteStub } from '../../testing/active-router.stub'; import { EnumKeysPipe } from '../../utils/enum-keys-pipe'; import { RouterStub } from '../../testing/router.stub'; -import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; -import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('StartsWithDateComponent', () => { let comp: StartsWithDateComponent; diff --git a/src/app/shared/starts-with/text/starts-with-text.component.spec.ts b/src/app/shared/starts-with/text/starts-with-text.component.spec.ts index c08ef5cfdcf..b717c72d76b 100644 --- a/src/app/shared/starts-with/text/starts-with-text.component.spec.ts +++ b/src/app/shared/starts-with/text/starts-with-text.component.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { CommonModule } from '@angular/common'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; @@ -8,12 +8,8 @@ import { EnumKeysPipe } from '../../utils/enum-keys-pipe'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { StartsWithTextComponent } from './starts-with-text.component'; -import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; -import { of as observableOf } from 'rxjs'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; -import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('StartsWithTextComponent', () => { let comp: StartsWithTextComponent; diff --git a/src/app/shared/testing/group-mock.ts b/src/app/shared/testing/group-mock.ts index a6db4c922e5..0d6f924c01a 100644 --- a/src/app/shared/testing/group-mock.ts +++ b/src/app/shared/testing/group-mock.ts @@ -1,6 +1,5 @@ import { Group } from '../../core/eperson/models/group.model'; import { EPersonMock } from './eperson.mock'; -import { of } from 'rxjs'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; export const GroupMock2: Group = Object.assign(new Group(), { diff --git a/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts b/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts index 4f3c54b6428..0f0ee579a10 100644 --- a/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts +++ b/src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, fakeAsync, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { createTestComponent } from '../../../shared/testing/utils.test'; diff --git a/src/app/submission/submit/submission-submit.component.spec.ts b/src/app/submission/submit/submission-submit.component.spec.ts index 1b76082d1b3..da569e4e5d7 100644 --- a/src/app/submission/submit/submission-submit.component.spec.ts +++ b/src/app/submission/submit/submission-submit.component.spec.ts @@ -1,4 +1,4 @@ -import { waitForAsync, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { NO_ERRORS_SCHEMA, ViewContainerRef } from '@angular/core'; From 3deb5f6eb7e00b9d4d66e3096c3304a5c3d26662 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Thu, 10 Nov 2022 20:53:56 +0530 Subject: [PATCH 117/758] [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 68ecf4815157ce8a2f859193d2075939640efa15 Mon Sep 17 00:00:00 2001 From: LisAtUP <117272473+LisAtUP@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:20:30 +0200 Subject: [PATCH 118/758] Update el.json5 --- src/assets/i18n/el.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/el.json5 b/src/assets/i18n/el.json5 index e8ae4ab2e9d..35cad25fd21 100644 --- a/src/assets/i18n/el.json5 +++ b/src/assets/i18n/el.json5 @@ -76,7 +76,7 @@ "admin.access-control.groups.form.delete-group.modal.info": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την ομάδα \"{{ dsoName }}\"", "admin.access-control.groups.form.groupCommunity": "Κοινότητα ή Συλλογή", "admin.access-control.groups.form.groupDescription": "Περιγραφή", - "admin.access-control.groups.form.groupName": "Ονομα ομάδας", + "admin.access-control.groups.form.groupName": "Όνομα ομάδας", "admin.access-control.groups.form.head.create": "Δημιουργία ομάδας", "admin.access-control.groups.form.head.edit": "Επεξεργασία ομάδας", "admin.access-control.groups.form.members-list.button.see-all": "Περιήγηση σε όλα", From f4e36aa261a4d0245cd3c304de118633de81baf5 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 11 Nov 2022 17:44:20 +0100 Subject: [PATCH 119/758] [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 e6065b7504867334690cc7748f1dacbb8c8ad769 Mon Sep 17 00:00:00 2001 From: Ying Jin Date: Wed, 16 Nov 2022 10:34:00 -0600 Subject: [PATCH 120/758] add captioning support for video/audio files --- .../core/shared/media-viewer-item.model.ts | 5 + .../media-viewer-video/caption-info.ts | 4 + .../media-viewer-video/language-helper.ts | 190 ++++++++++++++++++ .../media-viewer-video.component.html | 11 +- .../media-viewer-video.component.ts | 38 +++- .../media-viewer/media-viewer.component.ts | 1 + 6 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 src/app/item-page/media-viewer/media-viewer-video/caption-info.ts create mode 100644 src/app/item-page/media-viewer/media-viewer-video/language-helper.ts diff --git a/src/app/core/shared/media-viewer-item.model.ts b/src/app/core/shared/media-viewer-item.model.ts index cd3a31bd0b7..38c53944d1f 100644 --- a/src/app/core/shared/media-viewer-item.model.ts +++ b/src/app/core/shared/media-viewer-item.model.ts @@ -14,6 +14,11 @@ export class MediaViewerItem { */ format: string; + /** + * Incoming Bitsream format mime type + */ + minetype: string; + /** * Incoming Bitsream thumbnail */ diff --git a/src/app/item-page/media-viewer/media-viewer-video/caption-info.ts b/src/app/item-page/media-viewer/media-viewer-video/caption-info.ts new file mode 100644 index 00000000000..011249027fe --- /dev/null +++ b/src/app/item-page/media-viewer/media-viewer-video/caption-info.ts @@ -0,0 +1,4 @@ +export class CaptionInfo { + constructor(public src: string, public srclang: string, public langLabel: string ) { + } +} diff --git a/src/app/item-page/media-viewer/media-viewer-video/language-helper.ts b/src/app/item-page/media-viewer/media-viewer-video/language-helper.ts new file mode 100644 index 00000000000..b27ab9983f9 --- /dev/null +++ b/src/app/item-page/media-viewer/media-viewer-video/language-helper.ts @@ -0,0 +1,190 @@ +export const languageHelper = { + ab: 'Abkhazian', + aa: 'Afar', + af: 'Afrikaans', + ak: 'Akan', + sq: 'Albanian', + am: 'Amharic', + ar: 'Arabic', + an: 'Aragonese', + hy: 'Armenian', + as: 'Assamese', + av: 'Avaric', + ae: 'Avestan', + ay: 'Aymara', + az: 'Azerbaijani', + bm: 'Bambara', + ba: 'Bashkir', + eu: 'Basque', + be: 'Belarusian', + bn: 'Bengali (Bangla)', + bh: 'Bihari', + bi: 'Bislama', + bs: 'Bosnian', + br: 'Breton', + bg: 'Bulgarian', + my: 'Burmese', + ca: 'Catalan', + ch: 'Chamorro', + ce: 'Chechen', + ny: 'Chichewa, Chewa, Nyanja', + zh: 'Chinese', + cv: 'Chuvash', + kw: 'Cornish', + co: 'Corsican', + cr: 'Cree', + hr: 'Croatian', + cs: 'Czech', + da: 'Danish', + dv: 'Divehi, Dhivehi, Maldivian', + nl: 'Dutch', + dz: 'Dzongkha', + en: 'English', + eo: 'Esperanto', + et: 'Estonian', + ee: 'Ewe', + fo: 'Faroese', + fj: 'Fijian', + fi: 'Finnish', + fr: 'French', + ff: 'Fula, Fulah, Pulaar, Pular', + gl: 'Galician', + gd: 'Gaelic (Scottish)', + gv: 'Gaelic (Manx)', + ka: 'Georgian', + de: 'German', + el: 'Greek', + gn: 'Guarani', + gu: 'Gujarati', + ht: 'Haitian Creole', + ha: 'Hausa', + he: 'Hebrew', + hz: 'Herero', + hi: 'Hindi', + ho: 'Hiri Motu', + hu: 'Hungarian', + is: 'Icelandic', + io: 'Ido', + ig: 'Igbo', + in: 'Indonesian', + ia: 'Interlingua', + ie: 'Interlingue', + iu: 'Inuktitut', + ik: 'Inupiak', + ga: 'Irish', + it: 'Italian', + ja: 'Japanese', + jv: 'Javanese', + kl: 'Kalaallisut, Greenlandic', + kn: 'Kannada', + kr: 'Kanuri', + ks: 'Kashmiri', + kk: 'Kazakh', + km: 'Khmer', + ki: 'Kikuyu', + rw: 'Kinyarwanda (Rwanda)', + rn: 'Kirundi', + ky: 'Kyrgyz', + kv: 'Komi', + kg: 'Kongo', + ko: 'Korean', + ku: 'Kurdish', + kj: 'Kwanyama', + lo: 'Lao', + la: 'Latin', + lv: 'Latvian (Lettish)', + li: 'Limburgish ( Limburger)', + ln: 'Lingala', + lt: 'Lithuanian', + lu: 'Luga-Katanga', + lg: 'Luganda, Ganda', + lb: 'Luxembourgish', + mk: 'Macedonian', + mg: 'Malagasy', + ms: 'Malay', + ml: 'Malayalam', + mt: 'Maltese', + mi: 'Maori', + mr: 'Marathi', + mh: 'Marshallese', + mo: 'Moldavian', + mn: 'Mongolian', + na: 'Nauru', + nv: 'Navajo', + ng: 'Ndonga', + nd: 'Northern Ndebele', + ne: 'Nepali', + no: 'Norwegian', + nb: 'Norwegian bokmål', + nn: 'Norwegian nynorsk', + oc: 'Occitan', + oj: 'Ojibwe', + cu: 'Old Church Slavonic, Old Bulgarian', + or: 'Oriya', + om: 'Oromo (Afaan Oromo)', + os: 'Ossetian', + pi: 'Pāli', + ps: 'Pashto, Pushto', + fa: 'Persian (Farsi)', + pl: 'Polish', + pt: 'Portuguese', + pa: 'Punjabi (Eastern)', + qu: 'Quechua', + rm: 'Romansh', + ro: 'Romanian', + ru: 'Russian', + se: 'Sami', + sm: 'Samoan', + sg: 'Sango', + sa: 'Sanskrit', + sr: 'Serbian', + sh: 'Serbo-Croatian', + st: 'Sesotho', + tn: 'Setswana', + sn: 'Shona', + ii: 'Sichuan Yi, Nuosu', + sd: 'Sindhi', + si: 'Sinhalese', + ss: 'Siswati (Swati)', + sk: 'Slovak', + sl: 'Slovenian', + so: 'Somali', + nr: 'Southern Ndebele', + es: 'Spanish', + su: 'Sundanese', + sw: 'Swahili (Kiswahili)', + sv: 'Swedish', + tl: 'Tagalog', + ty: 'Tahitian', + tg: 'Tajik', + ta: 'Tamil', + tt: 'Tatar', + te: 'Telugu', + th: 'Thai', + bo: 'Tibetan', + ti: 'Tigrinya', + to: 'Tonga', + ts: 'Tsonga', + tr: 'Turkish', + tk: 'Turkmen', + tw: 'Twi', + ug: 'Uyghur', + uk: 'Ukrainian', + ur: 'Urdu', + uz: 'Uzbek', + ve: 'Venda', + vi: 'Vietnamese', + vo: 'Volapük', + wa: 'Wallon', + cy: 'Welsh', + wo: 'Wolof', + fy: 'Western Frisian', + xh: 'Xhosa', + yi: 'Yiddish', + yo: 'Yoruba', + za: 'Zhuang, Chuang', + zu: 'Zulu' +}; + + + diff --git a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html index a4493e36fc0..5e744373930 100644 --- a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html +++ b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html @@ -1,4 +1,5 @@ +> + + + + + + + +
    diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html index 0fc6d19667d..16fda1caa83 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html @@ -4,12 +4,12 @@ -
    -
    @@ -18,7 +18,7 @@
    - From 4b0b6983a0d001c7ad598c1b5061c9b6eefbc44f Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 17 Nov 2022 16:50:30 +0200 Subject: [PATCH 125/758] 94391: Remove unused import --- src/app/shared/dso-page/dso-edit-menu.resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 31e1baf76b3..749d5580a43 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -9,7 +9,7 @@ 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, mergeMap, switchMap, take } from 'rxjs/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'; From f16e18fa634c7b9d66b5c36c7b785a7c42a43b29 Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Fri, 18 Nov 2022 11:08:43 +0100 Subject: [PATCH 126/758] Fix breaking import --- .../dso-edit-menu-expandable-section.component.spec.ts | 2 +- .../dso-edit-menu-section.component.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 444e647ee89..79ab35bd28c 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 @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MenuServiceStub } from '../../../testing/menu-service.stub'; import { TranslateModule } from '@ngx-translate/core'; import { MenuService } from '../../../menu/menu.service'; -import { CSSVariableService } from '../../../sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../../sass-helper/css-variable.service'; import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub'; import { Router } from '@angular/router'; import { RouterStub } from '../../../testing/router.stub'; diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts index a0d5b7a88af..f0815c54150 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MenuServiceStub } from '../../../testing/menu-service.stub'; import { TranslateModule } from '@ngx-translate/core'; import { MenuService } from '../../../menu/menu.service'; -import { CSSVariableService } from '../../../sass-helper/sass-helper.service'; +import { CSSVariableService } from '../../../sass-helper/css-variable.service'; import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub'; import { Router } from '@angular/router'; import { RouterStub } from '../../../testing/router.stub'; From 7d58a1eb0a6c2bd3e432fb4c4963cd6659c8a0bd Mon Sep 17 00:00:00 2001 From: Nikunj Sharma Date: Fri, 18 Nov 2022 15:14:55 +0530 Subject: [PATCH 127/758] [CST-7604] fixed text highlight on search page --- .../item-search-result-list-element.component.html | 4 ++-- ...item-search-result-list-element.component.spec.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index 6b2951495d4..3a42eae1f50 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -19,9 +19,9 @@ + [innerHTML]="firstMetadataValue('dc.title') || 'Undefined'"> + [innerHTML]="firstMetadataValue('dc.title') || 'Undefined'"> diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts index d1e6c27ba43..f85e4c23b51 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts @@ -205,6 +205,18 @@ describe('ItemSearchResultListElementComponent', () => { }); }); + describe('When the item has title', () => { + beforeEach(() => { + publicationListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the title', () => { + const titleField = fixture.debugElement.query(By.css('.item-list-title')); + expect(titleField).not.toBeNull(); + }); + }); + describe('When the item has no title', () => { beforeEach(() => { publicationListElementComponent.object = mockItemWithoutMetadata; From 1ee3114432770db7a79562fd2199bb8fdb41051a Mon Sep 17 00:00:00 2001 From: Nikunj Sharma Date: Fri, 18 Nov 2022 18:09:26 +0530 Subject: [PATCH 128/758] [CST-7604] fixed text highlight on search page --- .../item/item-search-result-list-element.component.html | 4 ++-- .../item-search-result-list-element.component.spec.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index 3a42eae1f50..ad0206bd4ed 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -19,9 +19,9 @@ + [innerHTML]="firstMetadataValue('dc.title') ?? dsoTitle"> + [innerHTML]="firstMetadataValue('dc.title') ?? dsoTitle"> diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts index f85e4c23b51..660377746ab 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts @@ -13,7 +13,7 @@ import { APP_CONFIG } from '../../../../../../../config/app-config.interface'; let publicationListElementComponent: ItemSearchResultListElementComponent; let fixture: ComponentFixture; - +const dcTitle = 'This is just another title'; const mockItemWithMetadata: ItemSearchResult = Object.assign(new ItemSearchResult(), { indexableObject: Object.assign(new Item(), { @@ -22,7 +22,7 @@ const mockItemWithMetadata: ItemSearchResult = Object.assign(new ItemSearchResul 'dc.title': [ { language: 'en_US', - value: 'This is just another title' + value: dcTitle } ], 'dc.contributor.author': [ @@ -211,9 +211,9 @@ describe('ItemSearchResultListElementComponent', () => { fixture.detectChanges(); }); - it('should show the title', () => { + it('should show highlighted title', () => { const titleField = fixture.debugElement.query(By.css('.item-list-title')); - expect(titleField).not.toBeNull(); + expect(titleField.nativeNode.innerHTML).toEqual(dcTitle); }); }); From 9a695fedc7bb6992858cce5f2768ce2c9d08f902 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Wed, 23 Nov 2022 20:33:11 +0530 Subject: [PATCH 129/758] [CST-7693] Missing Translations added --- .../create-community-parent-selector.component.html | 2 +- .../scope-selector-modal.component.html | 2 +- src/assets/i18n/en.json5 | 10 ++++++++++ .../create-community-parent-selector.component.html | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html index 84fdd34c01e..4a226729888 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html +++ b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html @@ -9,7 +9,7 @@


    - or + {{'dso-selector.create.community.or-divider' | translate}}

    diff --git a/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html b/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html index bf5c15e9637..e7165a92136 100644 --- a/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html +++ b/src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.html @@ -9,7 +9,7 @@


    - or + {{'dso-selector.' + action + '.' + objectType.toString().toLowerCase() + '.or-divider' | translate}}

    diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index a806588701f..bf4b193ba1f 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1320,6 +1320,8 @@ "curation-task.task.vscan.label": "Virus Scan", + "curation-task.task.registerdoi.label": "Register DOI", + "curation.form.task-select.label": "Task:", @@ -1366,6 +1368,8 @@ "dso-selector.create.community.head": "New community", + "dso-selector.create.community.or-divider": "or", + "dso-selector.create.community.sub-level": "Create a new community in", "dso-selector.create.community.top-level": "Create a new top-level community", @@ -1400,6 +1404,8 @@ "dso-selector.set-scope.community.button": "Search all of DSpace", + "dso-selector.set-scope.community.or-divider": "or", + "dso-selector.set-scope.community.input-header": "Search for a community or collection", "dso-selector.claim.item.head": "Profile tips", @@ -4037,6 +4043,8 @@ "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfProject": "Funder of the Project", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isPublicationOfAuthor": "Publication of the Author", + "submission.sections.describe.relationship-lookup.selection-tab.title.openAIREFunding": "Funding OpenAIRE API", "submission.sections.describe.relationship-lookup.selection-tab.title.isProjectOfPublication": "Project", @@ -4081,6 +4089,8 @@ "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Parent Organizational Unit", + "submission.sections.describe.relationship-lookup.title.isPublicationOfAuthor": "Publication", + "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown", "submission.sections.describe.relationship-lookup.selection-tab.settings": "Settings", diff --git a/src/themes/custom/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html b/src/themes/custom/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html index 84fdd34c01e..4a226729888 100644 --- a/src/themes/custom/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html +++ b/src/themes/custom/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.html @@ -9,7 +9,7 @@


    - or + {{'dso-selector.create.community.or-divider' | translate}}

    From e7607da0a2f2e6344d482723558f850cac919eff Mon Sep 17 00:00:00 2001 From: Nikunj Sharma Date: Thu, 24 Nov 2022 14:17:50 +0530 Subject: [PATCH 130/758] [CST-7694] fixed group edit issue for non permanent group --- .../group-form/group-form.component.spec.ts | 37 +++++++++++++++++++ .../group-form/group-form.component.ts | 4 +- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/app/access-control/group-registry/group-form/group-form.component.spec.ts b/src/app/access-control/group-registry/group-form/group-form.component.spec.ts index 09487a7eaad..a7a7cb5be46 100644 --- a/src/app/access-control/group-registry/group-form/group-form.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/group-form.component.spec.ts @@ -266,6 +266,43 @@ describe('GroupFormComponent', () => { fixture.detectChanges(); }); + it('should edit with name and description operations', () => { + const operations = [{ + op: 'add', + path: '/metadata/dc.description', + value: 'testDescription' + }, { + op: 'replace', + path: '/name', + value: 'newGroupName' + }]; + expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); + }); + + it('should edit with description operations', () => { + component.groupName.value = null; + component.onSubmit(); + fixture.detectChanges(); + const operations = [{ + op: 'add', + path: '/metadata/dc.description', + value: 'testDescription' + }]; + expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); + }); + + it('should edit with name operations', () => { + component.groupDescription.value = null; + component.onSubmit(); + fixture.detectChanges(); + const operations = [{ + op: 'replace', + path: '/name', + value: 'newGroupName' + }]; + expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); + }); + it('should emit the existing group using the correct new values', waitForAsync(() => { fixture.whenStable().then(() => { expect(component.submitForm.emit).toHaveBeenCalledWith(expected2); diff --git a/src/app/access-control/group-registry/group-form/group-form.component.ts b/src/app/access-control/group-registry/group-form/group-form.component.ts index b0178f12948..d84667f04e0 100644 --- a/src/app/access-control/group-registry/group-form/group-form.component.ts +++ b/src/app/access-control/group-registry/group-form/group-form.component.ts @@ -344,8 +344,8 @@ export class GroupFormComponent implements OnInit, OnDestroy { if (hasValue(this.groupDescription.value)) { operations = [...operations, { - op: 'replace', - path: '/metadata/dc.description/0/value', + op: 'add', + path: '/metadata/dc.description', value: this.groupDescription.value }]; } From 6e2279d155975700b442839d59aee6cd89d8fb3a Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Mon, 28 Nov 2022 11:25:50 +0100 Subject: [PATCH 131/758] [1950][DURACOM-101] Fixed linting --- src/app/shared/form/builder/parsers/field-parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index e2a907056e2..d4468778976 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -1,6 +1,6 @@ import { Inject, InjectionToken } from '@angular/core'; -import { uniqueId } from 'lodash'; +import uniqueId from 'lodash/uniqueId'; import { DynamicFormControlLayout, DynamicFormControlRelation, MATCH_VISIBLE, OR_OPERATOR } from '@ng-dynamic-forms/core'; import { hasValue, isNotEmpty, isNotNull, isNotUndefined } from '../../../empty.util'; @@ -22,7 +22,7 @@ export const SUBMISSION_ID: InjectionToken = new InjectionToken( export const CONFIG_DATA: InjectionToken = new InjectionToken('configData'); export const INIT_FORM_VALUES: InjectionToken = new InjectionToken('initFormValues'); export const PARSER_OPTIONS: InjectionToken = new InjectionToken('parserOptions'); -export const REGEX_FIELD_VALIDATOR: RegExp = new RegExp('(\\/?)(.+)\\1([gimsuy]*)', 'i'); +export const REGEX_FIELD_VALIDATOR = new RegExp('(\\/?)(.+)\\1([gimsuy]*)', 'i'); export abstract class FieldParser { From 9eb4a7e22ea01258df60178e76c44c0205a4c32d Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 29 Nov 2022 17:20:35 +0100 Subject: [PATCH 132/758] 95335: Removed duplicate i18n key --- src/assets/i18n/en.json5 | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 826e871efa8..10a37f2b2dc 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4811,7 +4811,5 @@ "person.orcid.registry.auth": "ORCID Authorizations", "home.recent-submissions.head": "Recent Submissions", - "idle-modal.extend-session": "Extend session", - "listable-notification-object.default-message": "This object couldn't be retrieved", } From 92f94c39b2374b83ef7f1243f66401c5162c6fb2 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 29 Nov 2022 18:02:13 +0100 Subject: [PATCH 133/758] 97075: Edit metadata redesign --- .../dso-edit-metadata-form.ts | 133 ++++++++++++++++++ .../dso-edit-metadata.component.html | 126 +++++++++++++++++ .../dso-edit-metadata.component.scss | 73 ++++++++++ .../dso-edit-metadata.component.ts | 99 +++++++++++++ .../metadata-field-selector.component.html | 18 +++ .../metadata-field-selector.component.scss | 0 .../metadata-field-selector.component.ts | 68 +++++++++ src/app/dso-shared/dso-shared.module.ts | 20 +++ .../edit-item-page/edit-item-page.module.ts | 4 +- .../edit-item-page.routing.module.ts | 3 +- src/styles/_bootstrap_variables_mapping.scss | 6 + 11 files changed, 548 insertions(+), 2 deletions(-) create mode 100644 src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts create mode 100644 src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html create mode 100644 src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.scss create mode 100644 src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts create mode 100644 src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.html create mode 100644 src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.scss create mode 100644 src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts create mode 100644 src/app/dso-shared/dso-shared.module.ts diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts new file mode 100644 index 00000000000..c059f5064f4 --- /dev/null +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts @@ -0,0 +1,133 @@ +import { MetadataMap, MetadataValue } from '../../core/shared/metadata.models'; +import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; + +export enum DsoEditMetadataChangeType { + UPDATE = 1, + ADD = 2, + REMOVE = 3 +} + +export class DsoEditMetadataValue { + originalValue: MetadataValue; + newValue: MetadataValue; + reinstatableValue: MetadataValue; + + editing = false; + change: DsoEditMetadataChangeType; + reinstatableChange: DsoEditMetadataChangeType; + + constructor(value: MetadataValue, added = false) { + this.originalValue = value; + this.newValue = Object.assign(new MetadataValue(), value); + if (added) { + this.change = DsoEditMetadataChangeType.ADD; + this.editing = true; + } + } + + confirmChanges() { + if (hasNoValue(this.change) || this.change === DsoEditMetadataChangeType.UPDATE) { + if ((this.originalValue.value !== this.newValue.value || this.originalValue.language !== this.newValue.language)) { + this.change = DsoEditMetadataChangeType.UPDATE; + } else { + this.change = undefined; + } + } + this.editing = false; + } + + hasChanges(): boolean { + return hasValue(this.change); + } + + discardAndMarkReinstatable(): void { + if (this.change === DsoEditMetadataChangeType.UPDATE) { + this.reinstatableValue = this.newValue; + } + this.reinstatableChange = this.change; + this.discard(); + } + + discard(): void { + this.change = undefined; + this.newValue = Object.assign(new MetadataValue(), this.originalValue); + this.editing = false; + } + + reinstate(): void { + this.newValue = this.reinstatableValue; + this.reinstatableValue = undefined; + this.change = this.reinstatableChange; + this.reinstatableChange = undefined; + } + + isReinstatable(): boolean { + return hasValue(this.reinstatableValue) || hasValue(this.reinstatableChange); + } +} + +export class DsoEditMetadataForm { + fieldKeys: string[]; + fields: { + [mdField: string]: DsoEditMetadataValue[], + }; + newValue: DsoEditMetadataValue; + + constructor(metadata: MetadataMap) { + this.fieldKeys = []; + this.fields = {}; + Object.entries(metadata).forEach(([mdField, values]: [string, MetadataValue[]]) => { + this.fieldKeys.push(mdField); + this.fields[mdField] = values.map((value) => new DsoEditMetadataValue(value)); + }); + } + + add(): void { + if (hasNoValue(this.newValue)) { + this.newValue = new DsoEditMetadataValue(new MetadataValue(), true); + } + } + + setMetadataField(mdField: string) { + if (isEmpty(this.fields[mdField])) { + this.fieldKeys.push(mdField); + this.fields[mdField] = []; + } + this.fields[mdField].push(this.newValue); + this.newValue = undefined; + } + + remove(mdField: string, index: number) { + if (isNotEmpty(this.fields[mdField])) { + this.fields[mdField].splice(index, 1); + if (this.fields[mdField].length === 0) { + this.fieldKeys.splice(this.fieldKeys.indexOf(mdField), 1); + delete this.fields[mdField]; + } + } + } + + hasChanges(): boolean { + return Object.values(this.fields).some((values) => values.some((value) => value.hasChanges())); + } + + discard(): void { + Object.values(this.fields).forEach((values) => { + values.forEach((value) => { + value.discard(); + }); + }); + } + + reinstate(): void { + Object.values(this.fields).forEach((values) => { + values.forEach((value) => { + value.reinstate(); + }); + }); + } + + isReinstatable(): boolean { + return Object.values(this.fields).some((values) => values.some((value) => value.isReinstatable())); + } +} diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html new file mode 100644 index 00000000000..abe671e6f32 --- /dev/null +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html @@ -0,0 +1,126 @@ + + diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.scss b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.scss new file mode 100644 index 00000000000..a13afaf07e8 --- /dev/null +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.scss @@ -0,0 +1,73 @@ +.lbl-cell { + min-width: 210px; + background-color: var(--bs-gray-100); + font-weight: bold; + + &.ds-success { + background-color: var(--bs-success-bg); + border: 1px solid var(--bs-success); + } +} + +.lbl-cell, +.ds-flex-cell { + padding: 1rem; + border: 1px solid var(--bs-gray-200); +} + +.ds-lang-cell { + min-width: 72px; +} + +.ds-edit-cell { + min-width: 173px; +} + +.ds-value-row { + background-color: white; + + &:active { + cursor: grabbing; + } + + &.ds-warning { + background-color: var(--bs-warning-bg); + + .ds-flex-cell { + border: 1px solid var(--bs-warning); + } + } + + &.ds-danger { + background-color: var(--bs-danger-bg); + + .ds-flex-cell { + border: 1px solid var(--bs-danger); + } + } + + &.ds-success { + background-color: var(--bs-success-bg); + + .ds-flex-cell { + border: 1px solid var(--bs-success); + } + } +} + +.ds-drop-list { + background-color: var(--bs-gray-500); +} + +.ds-field-row { + border: 1px solid var(--bs-gray-400); +} + + +.ds-header-row { + background-color: var(--bs-gray-100); +} + +.ds-drag-handle { + cursor: grab; +} diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts new file mode 100644 index 00000000000..d961d9dce92 --- /dev/null +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts @@ -0,0 +1,99 @@ +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { AlertType } from '../../shared/alert/aletr-type'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { DsoEditMetadataChangeType, DsoEditMetadataForm } from './dso-edit-metadata-form'; +import { map, switchMap } from 'rxjs/operators'; +import { ActivatedRoute, Data } from '@angular/router'; +import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest'; +import { Subscription } from 'rxjs/internal/Subscription'; +import { RemoteData } from '../../core/data/remote-data'; +import { hasNoValue, hasValue } from '../../shared/empty.util'; +import { RegistryService } from '../../core/registry/registry.service'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { Observable } from 'rxjs/internal/Observable'; +import { followLink } from '../../shared/utils/follow-link-config.model'; +import { getFirstSucceededRemoteData, metadataFieldsToString } from '../../core/shared/operators'; + +@Component({ + selector: 'ds-dso-edit-metadata', + styleUrls: ['./dso-edit-metadata.component.scss'], + templateUrl: './dso-edit-metadata.component.html', +}) +export class DsoEditMetadataComponent implements OnInit, OnDestroy { + @Input() dso: DSpaceObject; + dsoType: string; + + form: DsoEditMetadataForm; + newMdField: string; + + isReinstatable: boolean; + hasChanges: boolean; + isEmpty: boolean; + + /** + * The AlertType enumeration for access in the component's template + * @type {AlertType} + */ + public AlertTypeEnum = AlertType; + + /** + * The DsoEditMetadataChangeType enumeration for access in the component's template + * @type {DsoEditMetadataChangeType} + */ + public DsoEditMetadataChangeTypeEnum = DsoEditMetadataChangeType; + + dsoUpdateSubscription: Subscription; + + constructor(protected route: ActivatedRoute) { + } + + ngOnInit(): void { + if (hasNoValue(this.dso)) { + this.dsoUpdateSubscription = observableCombineLatest([this.route.data, this.route.parent.data]).pipe( + map(([data, parentData]: [Data, Data]) => Object.assign({}, data, parentData)), + map((data: any) => data.dso) + ).subscribe((rd: RemoteData) => { + this.dso = rd.payload; + this.initForm(); + }); + } else { + this.initForm(); + } + } + + initForm(): void { + this.dsoType = typeof this.dso.type === 'string' ? this.dso.type as any : this.dso.type.value; + this.form = new DsoEditMetadataForm(this.dso.metadata); + this.onDebounce(); + } + + onDebounce(): void { + this.hasChanges = this.form.hasChanges(); + this.isReinstatable = this.form.isReinstatable(); + this.isEmpty = Object.keys(this.form.fields).length === 0; + } + + submit(): void { + + } + + add(): void { + this.newMdField = undefined; + this.form.add(); + } + + discard(): void { + this.form.discard(); + } + + reinstate(): void { + this.form.reinstate(); + } + + ngOnDestroy() { + if (hasValue(this.dsoUpdateSubscription)) { + this.dsoUpdateSubscription.unsubscribe(); + } + } + +} diff --git a/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.html b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.html new file mode 100644 index 00000000000..d2761e52a61 --- /dev/null +++ b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.html @@ -0,0 +1,18 @@ +
    + + +
    diff --git a/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.scss b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts new file mode 100644 index 00000000000..63c27ef0627 --- /dev/null +++ b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts @@ -0,0 +1,68 @@ +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { startWith, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { followLink } from '../../../shared/utils/follow-link-config.model'; +import { + getAllSucceededRemoteData, + getFirstSucceededRemoteData, + metadataFieldsToString +} from '../../../core/shared/operators'; +import { Observable } from 'rxjs/internal/Observable'; +import { RegistryService } from '../../../core/registry/registry.service'; +import { FormControl } from '@angular/forms'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { hasValue } from '../../../shared/empty.util'; +import { Subscription } from 'rxjs/internal/Subscription'; + +@Component({ + selector: 'ds-metadata-field-selector', + styleUrls: ['./metadata-field-selector.component.scss'], + templateUrl: './metadata-field-selector.component.html' +}) +export class MetadataFieldSelectorComponent implements OnInit, OnDestroy { + @Input() mdField: string; + @Output() mdFieldChange = new EventEmitter(); + mdFieldOptions$: Observable; + + public input: FormControl = new FormControl(); + + query$: BehaviorSubject = new BehaviorSubject(null); + debounceTime = 500; + + subs: Subscription[] = []; + + constructor(protected registryService: RegistryService) { + } + + ngOnInit(): void { + this.subs.push( + this.input.valueChanges.pipe( + debounceTime(this.debounceTime), + ).subscribe((valueChange) => { + this.query$.next(valueChange); + this.mdField = valueChange; + this.mdFieldChange.emit(this.mdField); + }), + ); + this.mdFieldOptions$ = this.query$.pipe( + distinctUntilChanged(), + switchMap((query) => { + if (query !== null) { + return this.registryService.queryMetadataFields(query, null, true, false, followLink('schema')).pipe( + getAllSucceededRemoteData(), + metadataFieldsToString(), + ); + } else { + return [[]]; + } + }), + ); + } + + select(mdFieldOption: string) { + this.mdField = mdFieldOption; + } + + ngOnDestroy(): void { + this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); + } +} diff --git a/src/app/dso-shared/dso-shared.module.ts b/src/app/dso-shared/dso-shared.module.ts new file mode 100644 index 00000000000..263d394906a --- /dev/null +++ b/src/app/dso-shared/dso-shared.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { DsoEditMetadataComponent } from './dso-edit-metadata/dso-edit-metadata.component'; +import { MetadataFieldSelectorComponent } from './dso-edit-metadata/metadata-field-selector/metadata-field-selector.component'; + +@NgModule({ + imports: [ + SharedModule, + ], + declarations: [ + DsoEditMetadataComponent, + MetadataFieldSelectorComponent, + ], + exports: [ + DsoEditMetadataComponent, + ], +}) +export class DsoSharedModule { + +} diff --git a/src/app/item-page/edit-item-page/edit-item-page.module.ts b/src/app/item-page/edit-item-page/edit-item-page.module.ts index 97901bd7c8e..71ab64fa33a 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.module.ts @@ -35,6 +35,7 @@ import { ItemVersionHistoryComponent } from './item-version-history/item-version import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component'; import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe'; import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-policies.module'; +import { DsoSharedModule } from '../../dso-shared/dso-shared.module'; /** @@ -48,7 +49,8 @@ import { ResourcePoliciesModule } from '../../shared/resource-policies/resource- EditItemPageRoutingModule, SearchPageModule, DragDropModule, - ResourcePoliciesModule + ResourcePoliciesModule, + DsoSharedModule, ], declarations: [ EditItemPageComponent, diff --git a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts index 2535e422168..bd8af3742e3 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts @@ -38,6 +38,7 @@ import { ItemPageBitstreamsGuard } from './item-page-bitstreams.guard'; import { ItemPageRelationshipsGuard } from './item-page-relationships.guard'; import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard'; import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; +import { DsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/dso-edit-metadata.component'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -75,7 +76,7 @@ import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.gua }, { path: 'metadata', - component: ItemMetadataComponent, + component: DsoEditMetadataComponent, data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }, canActivate: [ItemPageMetadataGuard] }, diff --git a/src/styles/_bootstrap_variables_mapping.scss b/src/styles/_bootstrap_variables_mapping.scss index 5a64be7e2a5..d352b24d38b 100644 --- a/src/styles/_bootstrap_variables_mapping.scss +++ b/src/styles/_bootstrap_variables_mapping.scss @@ -29,11 +29,17 @@ --bs-teal: #{$teal}; --bs-cyan: #{$cyan}; --bs-primary: #{$primary}; + --bs-primary-bg: #{lighten($primary, 30%)}; --bs-secondary: #{$secondary}; + --bs-secondary-bg: #{lighten($secondary, 30%)}; --bs-success: #{$success}; + --bs-success-bg: #{lighten($success, 30%)}; --bs-info: #{$info}; + --bs-info-bg: #{lighten($info, 30%)}; --bs-warning: #{$warning}; + --bs-warning-bg: #{lighten($warning, 30%)}; --bs-danger: #{$danger}; + --bs-danger-bg: #{lighten($danger, 30%)}; --bs-light: #{$light}; --bs-dark: #{$dark}; From 627da93e23ab242425fe0dfaefe7e7159a687d2c Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Wed, 30 Nov 2022 15:25:36 +0100 Subject: [PATCH 134/758] 97183 Workaround: reorder admin sidebar sections The issue described at https://github.com/DSpace/dspace-angular/issues/1643 was no longer reproducible: The menu component ultimately retrieves menu section information from the store, but in the `MenuComponent#ngOnInit` method, this information is piped through `distinctUntilChanged(compareArraysUsingIds())`, which discards an update that sets these menu elements to be visible. The behavior of this pipe is probably incorrect, but a proper fix is out of scope for the current task. For now, we work around the problem by adding top-level menu sections _after_ their children while initializing the menu section store, which side-steps this issue. --- src/app/menu.resolver.ts | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index 8630150c58f..60f2d1cca2a 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -171,17 +171,6 @@ export class MenuResolver implements Resolve { ]).subscribe(([isCollectionAdmin, isCommunityAdmin, isSiteAdmin]) => { const menuList = [ /* News */ - { - id: 'new', - active: false, - visible: true, - model: { - type: MenuItemType.TEXT, - text: 'menu.section.new' - } as TextMenuItemModel, - icon: 'plus', - index: 0 - }, { id: 'new_community', parentID: 'new', @@ -232,6 +221,17 @@ export class MenuResolver implements Resolve { link: '/processes/new' } as LinkMenuItemModel, }, + { + id: 'new', + active: false, + visible: true, + model: { + type: MenuItemType.TEXT, + text: 'menu.section.new' + } as TextMenuItemModel, + icon: 'plus', + index: 0 + }, // TODO: enable this menu item once the feature has been implemented // { // id: 'new_item_version', @@ -246,17 +246,6 @@ export class MenuResolver implements Resolve { // }, /* Edit */ - { - id: 'edit', - active: false, - visible: true, - model: { - type: MenuItemType.TEXT, - text: 'menu.section.edit' - } as TextMenuItemModel, - icon: 'pencil-alt', - index: 1 - }, { id: 'edit_community', parentID: 'edit', @@ -296,6 +285,17 @@ export class MenuResolver implements Resolve { } } as OnClickMenuItemModel, }, + { + id: 'edit', + active: false, + visible: true, + model: { + type: MenuItemType.TEXT, + text: 'menu.section.edit' + } as TextMenuItemModel, + icon: 'pencil-alt', + index: 1 + }, /* Statistics */ // TODO: enable this menu item once the feature has been implemented From 105f6cb9542926259c67aed08763b0330782ba8e Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 30 Nov 2022 17:49:19 +0100 Subject: [PATCH 135/758] 97075: Edit metadata redesign pt2 --- src/app/core/shared/operators.ts | 2 +- .../dso-edit-metadata-form.ts | 100 ++++++++++++++++-- .../dso-edit-metadata.component.html | 43 ++++---- .../dso-edit-metadata.component.scss | 6 +- .../dso-edit-metadata.component.ts | 59 +++++++++-- .../metadata-field-selector.component.ts | 14 ++- 6 files changed, 178 insertions(+), 46 deletions(-) diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index ea2a0283eb5..2a68e44af86 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -349,7 +349,7 @@ export const metadataFieldsToString = () => map((schema: MetadataSchema) => ({ field, schema })) ); }); - return observableCombineLatest(fieldSchemaArray); + return isNotEmpty(fieldSchemaArray) ? observableCombineLatest(fieldSchemaArray) : [[]]; }), map((fieldSchemaArray: { field: MetadataField, schema: MetadataSchema }[]): string[] => { return fieldSchemaArray.map((fieldSchema: { field: MetadataField, schema: MetadataSchema }) => fieldSchema.schema.prefix + '.' + fieldSchema.field.toString()); diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts index c059f5064f4..220a4561f6e 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts @@ -1,5 +1,10 @@ import { MetadataMap, MetadataValue } from '../../core/shared/metadata.models'; import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; +import { Operation } from 'fast-json-patch'; +import { MetadataPatchReplaceOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-replace-operation.model'; +import { MetadataPatchRemoveOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-remove-operation.model'; +import { MetadataPatchAddOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-add-operation.model'; +import { MetadataPatchOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-operation.model'; export enum DsoEditMetadataChangeType { UPDATE = 1, @@ -55,10 +60,14 @@ export class DsoEditMetadataValue { } reinstate(): void { - this.newValue = this.reinstatableValue; - this.reinstatableValue = undefined; - this.change = this.reinstatableChange; - this.reinstatableChange = undefined; + if (hasValue(this.reinstatableValue)) { + this.newValue = this.reinstatableValue; + this.reinstatableValue = undefined; + } + if (hasValue(this.reinstatableChange)) { + this.change = this.reinstatableChange; + this.reinstatableChange = undefined; + } } isReinstatable(): boolean { @@ -67,16 +76,23 @@ export class DsoEditMetadataValue { } export class DsoEditMetadataForm { + originalFieldKeys: string[]; fieldKeys: string[]; fields: { [mdField: string]: DsoEditMetadataValue[], }; + reinstatableNewValues: { + [mdField: string]: DsoEditMetadataValue[], + }; newValue: DsoEditMetadataValue; constructor(metadata: MetadataMap) { + this.originalFieldKeys = []; this.fieldKeys = []; this.fields = {}; + this.reinstatableNewValues = {}; Object.entries(metadata).forEach(([mdField, values]: [string, MetadataValue[]]) => { + this.originalFieldKeys.push(mdField); this.fieldKeys.push(mdField); this.fields[mdField] = values.map((value) => new DsoEditMetadataValue(value)); }); @@ -89,12 +105,17 @@ export class DsoEditMetadataForm { } setMetadataField(mdField: string) { + this.newValue.editing = false; + this.addValueToField(this.newValue, mdField); + this.newValue = undefined; + } + + private addValueToField(value: DsoEditMetadataValue, mdField: string) { if (isEmpty(this.fields[mdField])) { this.fieldKeys.push(mdField); this.fields[mdField] = []; } - this.fields[mdField].push(this.newValue); - this.newValue = undefined; + this.fields[mdField].push(value); } remove(mdField: string, index: number) { @@ -112,11 +133,31 @@ export class DsoEditMetadataForm { } discard(): void { - Object.values(this.fields).forEach((values) => { - values.forEach((value) => { - value.discard(); + Object.entries(this.fields).forEach(([field, values]) => { + let removeFromIndex = -1; + values.forEach((value, index) => { + if (value.change === DsoEditMetadataChangeType.ADD) { + if (isEmpty(this.reinstatableNewValues[field])) { + this.reinstatableNewValues[field] = []; + } + this.reinstatableNewValues[field].push(value); + if (removeFromIndex === -1) { + removeFromIndex = index; + } + } else { + value.discardAndMarkReinstatable(); + } }); + if (removeFromIndex > -1) { + this.fields[field].splice(removeFromIndex, this.fields[field].length - removeFromIndex); + } + }); + this.fieldKeys.forEach((field) => { + if (this.originalFieldKeys.indexOf(field) < 0) { + delete this.fields[field]; + } }); + this.fieldKeys = [...this.originalFieldKeys]; } reinstate(): void { @@ -125,9 +166,48 @@ export class DsoEditMetadataForm { value.reinstate(); }); }); + Object.entries(this.reinstatableNewValues).forEach(([field, values]) => { + values.forEach((value) => { + this.addValueToField(value, field); + }); + }); + this.reinstatableNewValues = {}; } isReinstatable(): boolean { - return Object.values(this.fields).some((values) => values.some((value) => value.isReinstatable())); + return isNotEmpty(this.reinstatableNewValues) || + Object.values(this.fields) + .some((values) => values + .some((value) => value.isReinstatable())); + } + + getOperations(): Operation[] { + const operations: Operation[] = []; + Object.entries(this.fields).forEach(([field, values]) => { + values.forEach((value, place) => { + if (value.hasChanges()) { + let operation: MetadataPatchOperation; + if (value.change === DsoEditMetadataChangeType.UPDATE) { + operation = new MetadataPatchReplaceOperation(field, place, { + value: value.newValue.value, + language: value.newValue.language, + }); + } else if (value.change === DsoEditMetadataChangeType.REMOVE) { + operation = new MetadataPatchRemoveOperation(field, place); + } else if (value.change === DsoEditMetadataChangeType.ADD) { + operation = new MetadataPatchAddOperation(field, { + value: value.newValue.value, + language: value.newValue.language, + }); + } else { + console.warn('Illegal metadata change state detected for', value); + } + if (hasValue(operation)) { + operations.push(operation.toOperation()); + } + } + }); + }); + return operations; } } diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html index abe671e6f32..27cf706a1a8 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html @@ -1,19 +1,19 @@
From c215e08720aa866c185de49eb41246a651423745 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 19 Sep 2023 14:47:42 +0200 Subject: [PATCH 666/758] [DSC-1250] revert code removed by mistake --- src/app/shared/form/chips/chips.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/form/chips/chips.component.html b/src/app/shared/form/chips/chips.component.html index b5d872a0bee..175c9b99c2b 100644 --- a/src/app/shared/form/chips/chips.component.html +++ b/src/app/shared/form/chips/chips.component.html @@ -65,7 +65,7 @@
-
+
From ec108b31a537e2513af9c09a813c6863f351dd3e Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 20 Sep 2023 15:48:38 +0200 Subject: [PATCH 667/758] [DSC-291] Add bitbucket pipeline configuration file --- bitbucket-pipelines.yml | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 bitbucket-pipelines.yml diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml new file mode 100644 index 00000000000..8353a902456 --- /dev/null +++ b/bitbucket-pipelines.yml @@ -0,0 +1,50 @@ +definitions: + steps: + - step: &test-code-checks + services: + - docker + 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: + 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 + + 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 From a3b47dcec80749487c7fa2d5ce51358b8715c68c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 20 Sep 2023 15:58:58 +0200 Subject: [PATCH 668/758] [DSC-291] try default option --- bitbucket-pipelines.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 8353a902456..c8190f1bbf6 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -16,6 +16,21 @@ definitions: - yarn run e2e pipelines: + default: + - parallel: + - stage: + name: + image: node:16 + caches: + - node + steps: + - step: *test-code-checks + - stage: + image: node:18 + caches: + - node + steps: + - step: *test-code-checks branches: 'dspace-cris-7': - parallel: From 636b5f5452022ab9ea1634aca557a5593cdf1d94 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 20 Sep 2023 16:03:34 +0200 Subject: [PATCH 669/758] [DSC-1207] Replaced deprecated API call --- src/app/shared/sidebar/sidebar.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/sidebar/sidebar.service.ts b/src/app/shared/sidebar/sidebar.service.ts index 0b77fce439a..bea1528e758 100644 --- a/src/app/shared/sidebar/sidebar.service.ts +++ b/src/app/shared/sidebar/sidebar.service.ts @@ -35,10 +35,10 @@ export class SidebarService { * @returns {Observable} Emits true if the user's screen size is mobile or when the state in the store is currently collapsed */ get isCollapsed(): Observable { - return observableCombineLatest( + return observableCombineLatest([ this.isXsOrSm$, this.isCollapsedInStore - ).pipe( + ]).pipe( map(([mobile, store]) => mobile && store) ); } From f36dd947c1a9b4f854542df2f82b391f3cae1492 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 20 Sep 2023 16:03:43 +0200 Subject: [PATCH 670/758] [DSC-291] try default option with only a stage --- bitbucket-pipelines.yml | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index c8190f1bbf6..60116030a8a 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -17,20 +17,13 @@ definitions: pipelines: default: - - parallel: - - stage: - name: - image: node:16 - caches: - - node - steps: - - step: *test-code-checks - - stage: - image: node:18 - caches: - - node - steps: - - step: *test-code-checks + - stage: + name: + image: node:16 + caches: + - node + steps: + - step: *test-code-checks branches: 'dspace-cris-7': - parallel: From 53abf51c20091aa0c9e9d39dd7450ad3626c831b Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 20 Sep 2023 16:26:12 +0200 Subject: [PATCH 671/758] [DSC-1251] consider empty value of configuration key request.item.type --- .../attachment-render.component.spec.ts | 14 ++++++++++++- .../file-download-button.component.html | 9 +++++--- .../file-download-button.component.spec.ts | 17 +++++++++++++++ .../file-download-link.component.spec.ts | 11 ++++++++++ .../file-download-link.component.ts | 21 +++++++++++++++++++ 5 files changed, 68 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/attachment-render/attachment-render.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/attachment-render.component.spec.ts index 541606ccdde..dbe7ec1e011 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-render.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/attachment-render.component.spec.ts @@ -6,16 +6,28 @@ import { AuthorizationDataService } from '../../../../../../../../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataServiceStub } from '../../../../../../../../../shared/testing/authorization-service.stub'; +import { ConfigurationDataService } from '../../../../../../../../../core/data/configuration-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../../../../../../../../../shared/remote-data.utils'; +import { ConfigurationProperty } from '../../../../../../../../../core/shared/configuration-property.model'; describe('AttachmentRenderComponent', () => { let component: AttachmentRenderComponent; let fixture: ComponentFixture; + let configurationDataService: ConfigurationDataService; beforeEach(async () => { + configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'request.item.type', + values: [] + })) + }); + await TestBed.configureTestingModule({ declarations: [ AttachmentRenderComponent ], providers: [ - {provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub} + {provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub}, + {provide: ConfigurationDataService, useValue: configurationDataService} ], schemas: [ NO_ERRORS_SCHEMA ] }) diff --git a/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 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 index a04507c87cb..d49b6fabcc8 100644 --- a/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 +++ 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 @@ -9,9 +9,12 @@ - {{ 'cris-layout.advanced-attachment.requestACopy' | - translate }} + diff --git a/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 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 index b6883e74233..d1d6d86eac9 100644 --- a/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 +++ 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 @@ -10,6 +10,9 @@ import { TranslateLoaderMock } from '../../../../../../../../../../../shared/moc import { SharedModule } from '../../../../../../../../../../../shared/shared.module'; import { FileDownloadButtonComponent } from './file-download-button.component'; +import { ConfigurationDataService } from '../../../../../../../../../../../core/data/configuration-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../../../../../../../../../../../shared/remote-data.utils'; +import { ConfigurationProperty } from '../../../../../../../../../../../core/shared/configuration-property.model'; describe('FileDownloadButtonComponent', () => { let component: FileDownloadButtonComponent; @@ -19,6 +22,7 @@ describe('FileDownloadButtonComponent', () => { let bitstream: Bitstream; let item: Item; + let configurationDataService: ConfigurationDataService; function init() { authorizationService = jasmine.createSpyObj('authorizationService', { @@ -36,6 +40,12 @@ describe('FileDownloadButtonComponent', () => { self: { href: 'obj-selflink' } } }); + configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'request.item.type', + values: [] + })) + }); } @@ -56,6 +66,7 @@ describe('FileDownloadButtonComponent', () => { declarations: [FileDownloadButtonComponent], providers: [ { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: ConfigurationDataService, useValue: configurationDataService } ] }) .compileComponents(); @@ -91,4 +102,10 @@ describe('FileDownloadButtonComponent', () => { expect(fixture.debugElement.query(By.css('[data-test="requestACopy"]'))).toBeTruthy(); }); + it('should show a disabled can request a copy button when request.item.type has no value', () => { + (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(of(false)); + component.ngOnInit(); + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('[data-test="requestACopy"]')).nativeElement.disabled).toBeTruthy(); + }); }); diff --git a/src/app/shared/file-download-link/file-download-link.component.spec.ts b/src/app/shared/file-download-link/file-download-link.component.spec.ts index 5633c94fe8c..8d22691bd5f 100644 --- a/src/app/shared/file-download-link/file-download-link.component.spec.ts +++ b/src/app/shared/file-download-link/file-download-link.component.spec.ts @@ -10,6 +10,9 @@ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { Item } from '../../core/shared/item.model'; import { getItemModuleRoute } from '../../item-page/item-page-routing-paths'; import { RouterLinkDirectiveStub } from '../testing/router-link-directive.stub'; +import { ConfigurationDataService } from '../../core/data/configuration-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; describe('FileDownloadLinkComponent', () => { let component: FileDownloadLinkComponent; @@ -20,6 +23,7 @@ describe('FileDownloadLinkComponent', () => { let bitstream: Bitstream; let item: Item; + let configurationDataService: ConfigurationDataService; function init() { authorizationService = jasmine.createSpyObj('authorizationService', { @@ -37,6 +41,12 @@ describe('FileDownloadLinkComponent', () => { self: {href: 'obj-selflink'} } }); + configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'request.item.type', + values: [] + })) + }); } function initTestbed() { @@ -44,6 +54,7 @@ describe('FileDownloadLinkComponent', () => { declarations: [FileDownloadLinkComponent, RouterLinkDirectiveStub], providers: [ {provide: AuthorizationDataService, useValue: authorizationService}, + {provide: ConfigurationDataService, useValue: configurationDataService} ] }) .compileComponents(); 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 22dcd120274..d2f1d215c0d 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 @@ -7,6 +7,9 @@ import { hasValue, isNotEmpty } from '../empty.util'; import { map } from 'rxjs/operators'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } 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'; +import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; @Component({ selector: 'ds-file-download-link', @@ -48,8 +51,16 @@ export class FileDownloadLinkComponent implements OnInit { canDownload$: Observable; + /** + * Whether or not the user can request a copy of the item + * based on the configuration property `request.item.type`. + */ + public canRequestItemCopy$: Observable; + + constructor( private authorizationService: AuthorizationDataService, + private configurationService: ConfigurationDataService, ) { } @@ -60,9 +71,19 @@ export class FileDownloadLinkComponent implements OnInit { this.bitstreamPath$ = observableCombineLatest([this.canDownload$, canRequestACopy$]).pipe( map(([canDownload, canRequestACopy]) => this.getBitstreamPath(canDownload, canRequestACopy)) ); + + this.canRequestItemCopy$ = this.configurationService.findByPropertyName('request.item.type').pipe( + getFirstCompletedRemoteData(), + getRemoteDataPayload(), + map((requestItemType: ConfigurationProperty) => + // in case requestItemType empty/commented out(undefined) - request-copy not allowed + hasValue(requestItemType) && requestItemType.values.length > 0 + ), + ); } else { this.bitstreamPath$ = observableOf(this.getBitstreamDownloadPath()); this.canDownload$ = observableOf(true); + this.canRequestItemCopy$ = observableOf(false); } } From 7f5f6e0658a2f4355d603574d6c02c72703648dc Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 20 Sep 2023 17:23:18 +0200 Subject: [PATCH 672/758] [DSC-291] try different node version --- bitbucket-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 60116030a8a..e458902a0d0 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -19,7 +19,7 @@ pipelines: default: - stage: name: - image: node:16 + image: node:18 caches: - node steps: From e82bfadaa7f95c44f412cba0bb015b51a67a79df Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 20 Sep 2023 17:26:40 +0200 Subject: [PATCH 673/758] [DSC-1252] display visibility switch for input groups --- src/app/shared/form/builder/parsers/field-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index fc83883b6c5..15ce3068374 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -344,7 +344,7 @@ export abstract class FieldParser { (controlModel as DsDynamicInputModel).typeBindRelations = this.getTypeBindRelations(this.configData.typeBind, this.parserOptions.typeField); } - controlModel.securityConfigLevel = this.mapBetweenMetadataRowAndSecurityMetadataLevels(this.fieldId); + controlModel.securityConfigLevel = this.mapBetweenMetadataRowAndSecurityMetadataLevels(this.getFieldId()); return controlModel; } From 9e1b1ab0815498c50eada5c7fea238518576f837 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 20 Sep 2023 17:56:47 +0200 Subject: [PATCH 674/758] [DSC-740] fix --- .../cris-layout-collection-box.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 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 e1126d358d7..94a641b3fe2 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 @@ -8,8 +8,8 @@
-
{{ '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 675/758] [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 676/758] [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 677/758] [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 678/758] [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 679/758] 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 680/758] [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 681/758] [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 682/758] [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 683/758] [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 684/758] =?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 685/758] [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 686/758] [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 687/758] [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 688/758] [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 689/758] [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 690/758] [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 691/758] [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 692/758] [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 693/758] 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 694/758] 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 695/758] [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 696/758] [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 697/758] [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 698/758] [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 699/758] [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 700/758] [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 701/758] [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 702/758] [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 703/758] [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 704/758] [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 705/758] [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 706/758] [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 707/758] [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 708/758] [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 709/758] 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 710/758] 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 711/758] [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 712/758] [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 713/758] [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 714/758] [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 715/758] [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 716/758] [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 717/758] [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 718/758] [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 719/758] [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 720/758] [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 721/758] [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 722/758] [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 723/758] [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 724/758] [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 725/758] [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 726/758] [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 727/758] [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 728/758] 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 729/758] 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 730/758] [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 731/758] [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 732/758] [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 733/758] [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 734/758] [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 735/758] [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 736/758] [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 737/758] [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 738/758] [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 739/758] [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 740/758] [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 741/758] [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 742/758] [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 743/758] [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 744/758] [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 745/758] [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 746/758] [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 747/758] [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 748/758] [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 749/758] [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 750/758] [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 754/758] [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 755/758] [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 756/758] [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 757/758] [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 758/758] [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); + })); + }); });