From 14b1fd463dd6930586d0fc5f5dad321afe0c8f49 Mon Sep 17 00:00:00 2001 From: Yura Date: Wed, 20 Oct 2021 11:27:55 +0200 Subject: [PATCH 001/570] 84367: SearchForm/SearchComponent: add option to hide scope selection dropdown --- .../search-page/configuration-search-page.component.ts | 6 ++++++ src/app/search-page/search.component.html | 1 + src/app/search-page/search.component.ts | 5 +++++ src/app/shared/search-form/search-form.component.html | 4 ++-- .../shared/search-form/search-form.component.spec.ts | 10 ++++++++++ src/app/shared/search-form/search-form.component.ts | 5 +++++ 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/app/search-page/configuration-search-page.component.ts b/src/app/search-page/configuration-search-page.component.ts index 1eefeeb569..5ba318b850 100644 --- a/src/app/search-page/configuration-search-page.component.ts +++ b/src/app/search-page/configuration-search-page.component.ts @@ -47,6 +47,12 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements */ @Input() fixedFilterQuery: string; + + /** + * Whether to show the scope selection dropdown + */ + @Input() scopeSelectable = true; + constructor(protected service: SearchService, protected sidebarService: SidebarService, protected windowService: HostWindowService, diff --git a/src/app/search-page/search.component.html b/src/app/search-page/search.component.html index d8aa25e4a3..499f652200 100644 --- a/src/app/search-page/search.component.html +++ b/src/app/search-page/search.component.html @@ -45,6 +45,7 @@ -
+
-
+
diff --git a/src/app/shared/search-form/search-form.component.spec.ts b/src/app/shared/search-form/search-form.component.spec.ts index 1469eac566..bbaee5709d 100644 --- a/src/app/shared/search-form/search-form.component.spec.ts +++ b/src/app/shared/search-form/search-form.component.spec.ts @@ -72,6 +72,16 @@ describe('SearchFormComponent', () => { expect(select).toBeNull(); }); + it('should not display scopes when scopeSelectable is false', () => { + comp.scopeSelectable = false; + comp.scopes = objects; + comp.scope = objects[1].id; + + fixture.detectChanges(); + const select = de.query(By.css('select')); + expect(select).toBeNull(); + }); + it('should display set query value in input field', fakeAsync(() => { const testString = 'This is a test query'; comp.query = testString; diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 2791aee378..1cf3c11913 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -66,6 +66,11 @@ export class SearchFormComponent { */ @Output() submitSearch = new EventEmitter(); + /** + * Whether to show the scope selection dropdown + */ + @Input() scopeSelectable = true; + constructor(private router: Router, private searchService: SearchService, private paginationService: PaginationService, private searchConfig: SearchConfigurationService From 931560ee2627c2b7d130ea22006251e53adf1224 Mon Sep 17 00:00:00 2001 From: Yura Date: Thu, 21 Oct 2021 10:22:23 +0200 Subject: [PATCH 002/570] 84367: Make ComcolPageBrowseByComponent themeable --- .../browse-by-metadata-page.component.html | 2 +- .../collection-page.component.html | 4 +-- .../community-page.component.html | 4 +-- .../themed-comcol-page-browse-by.component.ts | 33 +++++++++++++++++++ src/app/shared/shared.module.ts | 2 ++ 5 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/app/shared/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html index 2321da0204..324a581ae0 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html @@ -18,7 +18,7 @@ - +
diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index 9d598a3b69..6eceb696bd 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -40,10 +40,10 @@
- - +
diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index cf7282eb4b..27420e95b3 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -26,8 +26,8 @@
- - + + diff --git a/src/app/shared/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts b/src/app/shared/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts new file mode 100644 index 0000000000..410c1f53da --- /dev/null +++ b/src/app/shared/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts @@ -0,0 +1,33 @@ +import { Component, Input } from '@angular/core'; +import { ThemedComponent } from '../theme-support/themed.component'; +import { ComcolPageBrowseByComponent } from './comcol-page-browse-by.component'; + +/** + * Themed wrapper for ComcolPageBrowseByComponent + */ +@Component({ + selector: 'ds-themed-comcol-page-browse-by', + styleUrls: [], + templateUrl: '../theme-support/themed.component.html', +}) +export class ThemedComcolPageBrowseByComponent extends ThemedComponent { + /** + * The ID of the Community or Collection + */ + @Input() id: string; + @Input() contentType: string; + + inAndOutputNames: (keyof ComcolPageBrowseByComponent & keyof this)[] = ['id', 'contentType']; + + protected getComponentName(): string { + return 'ComcolPageBrowseByComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/shared/comcol-page-browse-by/comcol-page-browse-by.component`); + } + + protected importUnthemedComponent(): Promise { + return import('./comcol-page-browse-by.component'); + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 9b993e551f..9d5a0151c4 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -233,6 +233,7 @@ import { OnClickMenuItemComponent } from './menu/menu-item/onclick-menu-item.com import { TextMenuItemComponent } from './menu/menu-item/text-menu-item.component'; import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component'; import { SearchNavbarComponent } from '../search-navbar/search-navbar.component'; +import { ThemedComcolPageBrowseByComponent } from './comcol-page-browse-by/themed-comcol-page-browse-by.component'; /** * Declaration needed to make sure all decorator functions are called in time @@ -303,6 +304,7 @@ const COMPONENTS = [ EditComColPageComponent, DeleteComColPageComponent, ComcolPageBrowseByComponent, + ThemedComcolPageBrowseByComponent, ComcolRoleComponent, DsDynamicFormComponent, DsDynamicFormControlContainerComponent, From 859ff4a2f5e06c3c73fee0d8654fe4f391e76a4c Mon Sep 17 00:00:00 2001 From: Yura Date: Thu, 21 Oct 2021 10:33:17 +0200 Subject: [PATCH 003/570] 84367: Use DSONameService for ds-dso-selector names --- .../authorized-collection-selector.component.ts | 14 +++++++++----- .../dso-selector/dso-selector.component.html | 2 +- .../dso-selector/dso-selector.component.ts | 14 +++++++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts b/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts index bca1727542..08853307b0 100644 --- a/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts +++ b/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts @@ -14,6 +14,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { hasValue } from '../../../empty.util'; import { NotificationsService } from '../../../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-authorized-collection-selector', @@ -24,11 +25,14 @@ import { TranslateService } from '@ngx-translate/core'; * Component rendering a list of collections to select from */ export class AuthorizedCollectionSelectorComponent extends DSOSelectorComponent { - constructor(protected searchService: SearchService, - protected collectionDataService: CollectionDataService, - protected notifcationsService: NotificationsService, - protected translate: TranslateService) { - super(searchService, notifcationsService, translate); + constructor( + protected searchService: SearchService, + protected collectionDataService: CollectionDataService, + protected notifcationsService: NotificationsService, + protected translate: TranslateService, + protected dsoNameService: DSONameService, + ) { + super(searchService, notifcationsService, translate, dsoNameService); } /** diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index ab2ea6cd8b..a3b460fe90 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -22,7 +22,7 @@ \ No newline at end of file diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.scss b/src/app/shared/search/search-export-csv/search-export-csv.component.scss new file mode 100644 index 0000000000..4b0ab3c44a --- /dev/null +++ b/src/app/shared/search/search-export-csv/search-export-csv.component.scss @@ -0,0 +1,4 @@ +.export-button { + background: var(--ds-admin-sidebar-bg); + border-color: var(--ds-admin-sidebar-bg); +} \ No newline at end of file diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts b/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts new file mode 100644 index 0000000000..f8dc089c6a --- /dev/null +++ b/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts @@ -0,0 +1,179 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { of as observableOf } from 'rxjs'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { SearchExportCsvComponent } from './search-export-csv.component'; +import { ScriptDataService } from '../../../core/data/processes/script-data.service'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { Script } from '../../../process-page/scripts/script.model'; +import { Process } from '../../../process-page/processes/process.model'; +import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { Router } from '@angular/router'; +import { By } from '@angular/platform-browser'; +import { PaginatedSearchOptions } from '../paginated-search-options.model'; +import { SearchFilter } from '../search-filter.model'; +import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths'; + +describe('SearchExportCsvComponent', () => { + let component: SearchExportCsvComponent; + let fixture: ComponentFixture; + + let scriptDataService: ScriptDataService; + let authorizationDataService: AuthorizationDataService; + let notificationsService; + let router; + + const script = Object.assign(new Script(), {id: 'metadata-export-search', name: 'metadata-export-search'}); + const process = Object.assign(new Process(), {processId: 5, scriptName: 'metadata-export-search'}); + + const searchConfig = new PaginatedSearchOptions({ + configuration: 'test-configuration', + scope: 'test-scope', + query: 'test-query', + filters: [ + new SearchFilter('f.filter1', ['filter1value1,equals', 'filter1value2,equals']), + new SearchFilter('f.filter2', ['filter2value1,contains']) + ] + }); + + function initBeforeEachAsync() { + scriptDataService = jasmine.createSpyObj('scriptDataService', { + findById: createSuccessfulRemoteDataObject$(script), + invoke: createSuccessfulRemoteDataObject$(process) + }); + authorizationDataService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + + notificationsService = new NotificationsServiceStub(); + + router = jasmine.createSpyObj('authorizationService', ['navigateByUrl']); + TestBed.configureTestingModule({ + declarations: [SearchExportCsvComponent], + imports: [TranslateModule.forRoot(), NgbModule], + providers: [ + {provide: ScriptDataService, useValue: scriptDataService}, + {provide: AuthorizationDataService, useValue: authorizationDataService}, + {provide: NotificationsService, useValue: notificationsService}, + {provide: Router, useValue: router}, + ] + }).compileComponents(); + } + + function initBeforeEach() { + fixture = TestBed.createComponent(SearchExportCsvComponent); + component = fixture.componentInstance; + component.searchConfig = searchConfig; + fixture.detectChanges(); + } + + describe('init', () => { + describe('comp', () => { + beforeEach(waitForAsync(() => { + initBeforeEachAsync(); + })); + beforeEach(() => { + initBeforeEach(); + }); + it('should init the comp', () => { + expect(component).toBeTruthy(); + }); + }); + describe('when the user is an admin and the metadata-export-search script is present ', () => { + beforeEach(waitForAsync(() => { + initBeforeEachAsync(); + })); + beforeEach(() => { + initBeforeEach(); + }); + it('should add the button', () => { + const debugElement = fixture.debugElement.query(By.css('button.export-button')); + expect(debugElement).toBeDefined(); + }); + }); + describe('when the user is not an admin', () => { + beforeEach(waitForAsync(() => { + initBeforeEachAsync(); + (authorizationDataService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false)); + })); + beforeEach(() => { + initBeforeEach(); + }); + it('should not add the button', () => { + const debugElement = fixture.debugElement.query(By.css('button.export-button')); + expect(debugElement).toBeNull(); + }); + }); + describe('when the metadata-export-search script is not present', () => { + beforeEach(waitForAsync(() => { + initBeforeEachAsync(); + (scriptDataService.findById as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Not found', 404)); + })); + beforeEach(() => { + initBeforeEach(); + }); + it('should should not add the button', () => { + const debugElement = fixture.debugElement.query(By.css('button.export-button')); + expect(debugElement).toBeNull(); + }); + }); + }); + describe('export', () => { + beforeEach(waitForAsync(() => { + initBeforeEachAsync(); + })); + beforeEach(() => { + initBeforeEach(); + }); + it('should call the invoke script method with the correct parameters', () => { + component.export(); + expect(scriptDataService.invoke).toHaveBeenCalledWith('metadata-export-search', + [ + {name: '-q', value: searchConfig.query}, + {name: '-s', value: searchConfig.scope}, + {name: '-c', value: searchConfig.configuration}, + {name: '-f', value: 'filter1,equals=filter1value1,filter1value2'}, + {name: '-f', value: 'filter2,contains=filter2value1'}, + ], []); + + component.searchConfig = null; + fixture.detectChanges(); + + component.export(); + expect(scriptDataService.invoke).toHaveBeenCalledWith('metadata-export-search', [], []); + + }); + it('should show a success message when the script was invoked successfully and redirect to the corresponding process page', () => { + component.export(); + + expect(notificationsService.success).toHaveBeenCalled(); + expect(router.navigateByUrl).toHaveBeenCalledWith(getProcessDetailRoute(process.processId)); + }); + it('should show an error message when the script was not invoked successfully and stay on the current page', () => { + (scriptDataService.invoke as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Error', 500)); + + component.export(); + + expect(notificationsService.error).toHaveBeenCalled(); + expect(router.navigateByUrl).not.toHaveBeenCalled(); + }); + }); + describe('clicking the button', () => { + beforeEach(waitForAsync(() => { + initBeforeEachAsync(); + })); + beforeEach(() => { + initBeforeEach(); + }); + it('should trigger the export function', () => { + spyOn(component, 'export'); + + const debugElement = fixture.debugElement.query(By.css('button.export-button')); + debugElement.triggerEventHandler('click', null); + + expect(component.export).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.ts b/src/app/shared/search/search-export-csv/search-export-csv.component.ts new file mode 100644 index 0000000000..a1bdf1c7e1 --- /dev/null +++ b/src/app/shared/search/search-export-csv/search-export-csv.component.ts @@ -0,0 +1,102 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { PaginatedSearchOptions } from '../paginated-search-options.model'; +import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; +import { ScriptDataService } from '../../../core/data/processes/script-data.service'; +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { map, tap } from 'rxjs/operators'; +import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { hasValue, isNotEmpty } from '../../empty.util'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Process } from '../../../process-page/processes/process.model'; +import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'ds-search-export-csv', + styleUrls: ['./search-export-csv.component.scss'], + templateUrl: './search-export-csv.component.html', +}) +/** + * Display a button to export the current search results as csv + */ +export class SearchExportCsvComponent implements OnInit { + + /** + * The current configuration of the search + */ + @Input() searchConfig: PaginatedSearchOptions; + + /** + * Observable used to determine whether the button should be shown + */ + shouldShowButton$: Observable; + + /** + * The message key used for the tooltip of the button + */ + tooltipMsg = 'metadata-export-search.tooltip'; + + constructor(private scriptDataService: ScriptDataService, + private authorizationDataService: AuthorizationDataService, + private notificationsService: NotificationsService, + private translateService: TranslateService, + private router: Router + ) { + } + + ngOnInit(): void { + const scriptExists$ = this.scriptDataService.findById('metadata-export-search').pipe( + getFirstCompletedRemoteData(), + map((rd) => rd.isSuccess && hasValue(rd.payload)) + ); + + const isAuthorized$ = this.authorizationDataService.isAuthorized(FeatureID.AdministratorOf); + + this.shouldShowButton$ = observableCombineLatest([scriptExists$, isAuthorized$]).pipe( + tap((v) => console.log('showbutton', v)), + map(([scriptExists, isAuthorized]: [boolean, boolean]) => scriptExists && isAuthorized) + ); + } + + /** + * Start the export of the items based on the current search configuration + */ + export() { + const parameters = []; + if (hasValue(this.searchConfig)) { + if (isNotEmpty(this.searchConfig.query)) { + parameters.push({name: '-q', value: this.searchConfig.query}); + } + if (isNotEmpty(this.searchConfig.scope)) { + parameters.push({name: '-s', value: this.searchConfig.scope}); + } + if (isNotEmpty(this.searchConfig.configuration)) { + parameters.push({name: '-c', value: this.searchConfig.configuration}); + } + if (isNotEmpty(this.searchConfig.filters)) { + this.searchConfig.filters.forEach((filter) => { + let operator = 'equals'; + if (hasValue(filter.values)) { + operator = filter.values[0].substring(filter.values[0].indexOf(',') + 1); + } + const filterValue = `${filter.key.substring(2)},${operator}=${filter.values.map((v) => v.substring(0, v.indexOf(','))).join()}`; + parameters.push({name: '-f', value: filterValue}); + }); + } + } + + this.scriptDataService.invoke('metadata-export-search', parameters, []).pipe( + getFirstCompletedRemoteData() + ).subscribe((rd: RemoteData) => { + if (rd.hasSucceeded) { + this.notificationsService.success(this.translateService.get('metadata-export-search.submit.success')); + this.router.navigateByUrl(getProcessDetailRoute(rd.payload.processId)); + } else { + this.notificationsService.error(this.translateService.get('metadata-export-search.submit.error')); + } + }); + } +} diff --git a/src/app/shared/search/search-results/search-results.component.html b/src/app/shared/search/search-results/search-results.component.html index 4e6bca094e..01c63e9c9e 100644 --- a/src/app/shared/search/search-results/search-results.component.html +++ b/src/app/shared/search/search-results/search-results.component.html @@ -1,4 +1,7 @@ +

{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}

+ +
Date: Thu, 9 Dec 2021 15:09:46 +0100 Subject: [PATCH 013/570] 85451: Fixed filter syntax --- .../search-export-csv.component.spec.ts | 7 +++++-- .../search-export-csv.component.ts | 20 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts b/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts index f8dc089c6a..cb0d7e6266 100644 --- a/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts +++ b/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts @@ -34,7 +34,8 @@ describe('SearchExportCsvComponent', () => { query: 'test-query', filters: [ new SearchFilter('f.filter1', ['filter1value1,equals', 'filter1value2,equals']), - new SearchFilter('f.filter2', ['filter2value1,contains']) + new SearchFilter('f.filter2', ['filter2value1,contains']), + new SearchFilter('f.filter3', ['[2000 TO 2001]'], 'equals') ] }); @@ -134,8 +135,10 @@ describe('SearchExportCsvComponent', () => { {name: '-q', value: searchConfig.query}, {name: '-s', value: searchConfig.scope}, {name: '-c', value: searchConfig.configuration}, - {name: '-f', value: 'filter1,equals=filter1value1,filter1value2'}, + {name: '-f', value: 'filter1,equals=filter1value1'}, + {name: '-f', value: 'filter1,equals=filter1value2'}, {name: '-f', value: 'filter2,contains=filter2value1'}, + {name: '-f', value: 'filter3,equals=[2000 TO 2001]'}, ], []); component.searchConfig = null; diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.ts b/src/app/shared/search/search-export-csv/search-export-csv.component.ts index a1bdf1c7e1..d499097bd3 100644 --- a/src/app/shared/search/search-export-csv/search-export-csv.component.ts +++ b/src/app/shared/search/search-export-csv/search-export-csv.component.ts @@ -3,7 +3,7 @@ import { PaginatedSearchOptions } from '../paginated-search-options.model'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { ScriptDataService } from '../../../core/data/processes/script-data.service'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; -import { map, tap } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { hasValue, isNotEmpty } from '../../empty.util'; @@ -56,7 +56,6 @@ export class SearchExportCsvComponent implements OnInit { const isAuthorized$ = this.authorizationDataService.isAuthorized(FeatureID.AdministratorOf); this.shouldShowButton$ = observableCombineLatest([scriptExists$, isAuthorized$]).pipe( - tap((v) => console.log('showbutton', v)), map(([scriptExists, isAuthorized]: [boolean, boolean]) => scriptExists && isAuthorized) ); } @@ -78,12 +77,21 @@ export class SearchExportCsvComponent implements OnInit { } if (isNotEmpty(this.searchConfig.filters)) { this.searchConfig.filters.forEach((filter) => { - let operator = 'equals'; if (hasValue(filter.values)) { - operator = filter.values[0].substring(filter.values[0].indexOf(',') + 1); + filter.values.forEach((value) => { + let operator; + let filterValue; + if (hasValue(filter.operator)) { + operator = filter.operator; + filterValue = value; + } else { + operator = value.substring(value.indexOf(',') + 1); + filterValue = value.substring(0, value.indexOf(',')); + } + const valueToAdd = `${filter.key.substring(2)},${operator}=${filterValue}`; + parameters.push({name: '-f', value: valueToAdd}); + }); } - const filterValue = `${filter.key.substring(2)},${operator}=${filter.values.map((v) => v.substring(0, v.indexOf(','))).join()}`; - parameters.push({name: '-f', value: filterValue}); }); } } From 7a5381aa0f1893c5f9da331db78246cac83b38b7 Mon Sep 17 00:00:00 2001 From: WolfgangT71 <82097261+WolfgangT71@users.noreply.github.com> Date: Fri, 17 Dec 2021 17:53:47 +0100 Subject: [PATCH 014/570] Update de.json5 --- src/assets/i18n/de.json5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index bb719f202e..f3a6f7eba4 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -5323,7 +5323,7 @@ // "workflow-item.send-back.button.confirm": "Send back" // TODO New key - Add a translation - "workflow-item.send-back.button.confirm": "Send back" + "workflow-item.send-back.button.confirm": "Send back", - +"home.search-form.placeholder": "Durchsuche Repositorium" } From 565c4106f9cfa55d01cda77a43980d9ff3715dbf Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Fri, 31 Dec 2021 10:26:28 +0530 Subject: [PATCH 015/570] [DSC-389] add pagination. --- .../browse-by-metadata-page.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html index 2321da0204..1ec224e6b7 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html @@ -31,7 +31,7 @@ [sortConfig]="(currentSort$ |async)" [type]="startsWithType" [startsWithOptions]="startsWithOptions" - [enableArrows]="true" + [enableArrows]="false" (prev)="goPrev()" (next)="goNext()"> From e4d099df43189d9abb9e6c787918549dd5fb4132 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Wed, 2 Feb 2022 14:05:10 +0530 Subject: [PATCH 016/570] [DSC-389] pagination added. --- .../browse-by-metadata-page.component.html | 2 +- .../shared/browse-by/browse-by.component.html | 36 +++---------- .../browse-by/browse-by.component.spec.ts | 8 +-- .../shared/browse-by/browse-by.component.ts | 12 ++--- .../object-collection.component.html | 5 ++ .../object-collection.component.ts | 29 ++++++++++ .../object-detail.component.html | 7 ++- .../object-detail/object-detail.component.ts | 29 ++++++++++ .../object-grid/object-grid.component.html | 8 ++- .../object-grid/object-grid.component.ts | 30 +++++++++++ .../object-list/object-list.component.html | 7 ++- .../object-list/object-list.component.ts | 29 ++++++++++ .../pagination/pagination.component.html | 39 +++++++++----- .../shared/pagination/pagination.component.ts | 53 +++++++++++++++++++ 14 files changed, 239 insertions(+), 55 deletions(-) diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html index 1ec224e6b7..b38fcb0235 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html @@ -31,7 +31,7 @@ [sortConfig]="(currentSort$ |async)" [type]="startsWithType" [startsWithOptions]="startsWithOptions" - [enableArrows]="false" + [showPaginator]="true" (prev)="goPrev()" (next)="goNext()"> diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index 6d1422293d..cc126768e4 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -2,37 +2,14 @@

{{title | translate}}

-
+ [config]="paginationConfig" + [sortConfig]="sortConfig" + [showPaginator]="showPaginator" + [objects]="objects" + (prev)="goPrev()" + (next)="goNext()"> -
-
-
-
-
- -
- - - - -
-
-
-
-
    -
  • - -
  • -
-
- - -
-
@@ -40,3 +17,4 @@ {{'browse.empty' | translate}}
+ diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index 896537b63a..a39a207c77 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -129,9 +129,9 @@ describe('BrowseByComponent', () => { expect(fixture.debugElement.query(By.css('ds-viewable-collection'))).toBeDefined(); }); - describe('when enableArrows is true and objects are defined', () => { + describe('when showPaginator is true and objects are defined', () => { beforeEach(() => { - comp.enableArrows = true; + comp.showPaginator = true; comp.objects$ = mockItemsRD$; comp.paginationConfig = paginationConfig; @@ -188,7 +188,7 @@ describe('BrowseByComponent', () => { }); }); - describe('when enableArrows is true and browseEntries are provided', () => { + describe('when showPaginator is true and browseEntries are provided', () => { let browseEntries; beforeEach(() => { @@ -209,7 +209,7 @@ describe('BrowseByComponent', () => { }), ]; - comp.enableArrows = true; + comp.showPaginator = true; comp.objects$ = createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), browseEntries)); comp.paginationConfig = paginationConfig; comp.sortConfig = Object.assign(new SortOptions('dc.title', SortDirection.ASC)); diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts index 861e431635..8245897609 100644 --- a/src/app/shared/browse-by/browse-by.component.ts +++ b/src/app/shared/browse-by/browse-by.component.ts @@ -67,30 +67,30 @@ export class BrowseByComponent implements OnInit { /** * Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination */ - @Input() enableArrows = false; + @Input() showPaginator = true; /** - * If enableArrows is set to true, should it hide the options gear? + * If showPaginator is set to true, should it hide the options gear? */ @Input() hideGear = false; /** - * If enableArrows is set to true, emit when the previous button is clicked + * If showPaginator is set to true, emit when the previous button is clicked */ @Output() prev = new EventEmitter(); /** - * If enableArrows is set to true, emit when the next button is clicked + * If showPaginator is set to true, emit when the next button is clicked */ @Output() next = new EventEmitter(); /** - * If enableArrows is set to true, emit when the page size is changed + * If showPaginator is set to true, emit when the page size is changed */ @Output() pageSizeChange = new EventEmitter(); /** - * If enableArrows is set to true, emit when the sort direction is changed + * If showPaginator is set to true, emit when the sort direction is changed */ @Output() sortDirectionChange = new EventEmitter(); diff --git a/src/app/shared/object-collection/object-collection.component.html b/src/app/shared/object-collection/object-collection.component.html index f2778757ef..999ae9a120 100644 --- a/src/app/shared/object-collection/object-collection.component.html +++ b/src/app/shared/object-collection/object-collection.component.html @@ -6,6 +6,7 @@ [linkType]="linkType" [context]="context" [hidePaginationDetail]="hidePaginationDetail" + [showPaginator]="showPaginator" (paginationChange)="onPaginationChange($event)" (pageChange)="onPageChange($event)" (pageSizeChange)="onPageSizeChange($event)" @@ -19,6 +20,8 @@ [importConfig]="importConfig" (importObject)="importObject.emit($event)" (contentChange)="contentChange.emit()" + (prev)="goPrev()" + (next)="goNext()" *ngIf="(currentMode$ | async) === viewModeEnum.ListElement"> @@ -29,6 +32,7 @@ [linkType]="linkType" [context]="context" [hidePaginationDetail]="hidePaginationDetail" + [showPaginator]="showPaginator" (paginationChange)="onPaginationChange($event)" (pageChange)="onPageChange($event)" (pageSizeChange)="onPageSizeChange($event)" @@ -44,6 +48,7 @@ [linkType]="linkType" [context]="context" [hidePaginationDetail]="hidePaginationDetail" + [showPaginator]="showPaginator" *ngIf="(currentMode$ | async) === viewModeEnum.DetailedListElement"> diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts index 29fdb37ea1..f2706f1a4c 100644 --- a/src/app/shared/object-collection/object-collection.component.ts +++ b/src/app/shared/object-collection/object-collection.component.ts @@ -88,6 +88,11 @@ export class ObjectCollectionComponent implements OnInit { */ @Input() hidePaginationDetail = false; + /** + * Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination + */ + @Input() showPaginator = true; + /** * the page info of the list */ @@ -122,6 +127,16 @@ export class ObjectCollectionComponent implements OnInit { */ @Output() sortFieldChange: EventEmitter = new EventEmitter(); + /** + * If showPaginator is set to true, emit when the previous button is clicked + */ + @Output() prev = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the next button is clicked + */ + @Output() next = new EventEmitter(); + /** * Emits the current view mode */ @@ -192,4 +207,18 @@ export class ObjectCollectionComponent implements OnInit { this.paginationChange.emit(event); } + /** + * Go to the previous page + */ + goPrev() { + this.prev.emit(true); + } + + /** + * Go to the next page + */ + goNext() { + this.next.emit(true); + } + } diff --git a/src/app/shared/object-detail/object-detail.component.html b/src/app/shared/object-detail/object-detail.component.html index 7fef7d9689..32728d92e4 100644 --- a/src/app/shared/object-detail/object-detail.component.html +++ b/src/app/shared/object-detail/object-detail.component.html @@ -3,14 +3,19 @@ [pageInfoState]="objects?.payload" [collectionSize]="objects?.payload?.totalElements" [sortOptions]="sortConfig" + [objects]="objects" [hideGear]="hideGear" [hidePaginationDetail]="hidePaginationDetail" [hidePagerWhenSinglePage]="hidePagerWhenSinglePage" + [showPaginator]="showPaginator" (pageChange)="onPageChange($event)" (pageSizeChange)="onPageSizeChange($event)" (sortDirectionChange)="onSortDirectionChange($event)" (sortFieldChange)="onSortFieldChange($event)" - (paginationChange)="onPaginationChange($event)"> + (paginationChange)="onPaginationChange($event)" + (prev)="goPrev()" + (next)="goNext()" + >
diff --git a/src/app/shared/object-detail/object-detail.component.ts b/src/app/shared/object-detail/object-detail.component.ts index 8b94746fbd..05d2ca4b5b 100644 --- a/src/app/shared/object-detail/object-detail.component.ts +++ b/src/app/shared/object-detail/object-detail.component.ts @@ -66,6 +66,21 @@ export class ObjectDetailComponent { */ @Input() context: Context; + /** + * Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination + */ + @Input() showPaginator = true; + + /** + * If showPaginator is set to true, emit when the previous button is clicked + */ + @Output() prev = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the next button is clicked + */ + @Output() next = new EventEmitter(); + /** * The list of objects to paginate */ @@ -168,4 +183,18 @@ export class ObjectDetailComponent { this.paginationChange.emit(event); } + /** + * Go to the previous page + */ + goPrev() { + this.prev.emit(true); + } + + /** + * Go to the next page + */ + goNext() { + this.next.emit(true); + } + } diff --git a/src/app/shared/object-grid/object-grid.component.html b/src/app/shared/object-grid/object-grid.component.html index 0afd623d86..0961aa2989 100644 --- a/src/app/shared/object-grid/object-grid.component.html +++ b/src/app/shared/object-grid/object-grid.component.html @@ -4,13 +4,18 @@ [collectionSize]="objects?.payload?.totalElements" [sortOptions]="sortConfig" [hideGear]="hideGear" + [objects]="objects" [hidePagerWhenSinglePage]="hidePagerWhenSinglePage" [hidePaginationDetail]="hidePaginationDetail" + [showPaginator]="showPaginator" (pageChange)="onPageChange($event)" (pageSizeChange)="onPageSizeChange($event)" (sortDirectionChange)="onSortDirectionChange($event)" (sortFieldChange)="onSortFieldChange($event)" - (paginationChange)="onPaginationChange($event)"> + (paginationChange)="onPaginationChange($event)" + (prev)="goPrev()" + (next)="goNext()" + >
@@ -22,3 +27,4 @@ + diff --git a/src/app/shared/object-grid/object-grid.component.ts b/src/app/shared/object-grid/object-grid.component.ts index 39c3d56f7f..1b5ab075e2 100644 --- a/src/app/shared/object-grid/object-grid.component.ts +++ b/src/app/shared/object-grid/object-grid.component.ts @@ -49,6 +49,11 @@ export class ObjectGridComponent implements OnInit { */ @Input() sortConfig: SortOptions; + /** + * Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination + */ + @Input() showPaginator = true; + /** * The whether or not the gear is hidden */ @@ -134,6 +139,17 @@ export class ObjectGridComponent implements OnInit { * Event's payload equals to the newly selected sort field. */ @Output() sortFieldChange: EventEmitter = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the previous button is clicked + */ + @Output() prev = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the next button is clicked + */ + @Output() next = new EventEmitter(); + data: any = {}; columns$: Observable; @@ -225,4 +241,18 @@ export class ObjectGridComponent implements OnInit { this.paginationChange.emit(event); } + /** + * Go to the previous page + */ + goPrev() { + this.prev.emit(true); + } + + /** + * Go to the next page + */ + goNext() { + this.next.emit(true); + } + } diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html index 331ff1cb28..1c6d01ba11 100644 --- a/src/app/shared/object-list/object-list.component.html +++ b/src/app/shared/object-list/object-list.component.html @@ -2,15 +2,20 @@ [paginationOptions]="config" [pageInfoState]="objects?.payload" [collectionSize]="objects?.payload?.totalElements" + [objects]="objects" [sortOptions]="sortConfig" [hideGear]="hideGear" [hidePagerWhenSinglePage]="hidePagerWhenSinglePage" [hidePaginationDetail]="hidePaginationDetail" + [showPaginator]="showPaginator" (pageChange)="onPageChange($event)" (pageSizeChange)="onPageSizeChange($event)" (sortDirectionChange)="onSortDirectionChange($event)" (sortFieldChange)="onSortFieldChange($event)" - (paginationChange)="onPaginationChange($event)"> + (paginationChange)="onPaginationChange($event)" + (prev)="goPrev()" + (next)="goNext()" + >
  • (); + /** + * If showPaginator is set to true, emit when the previous button is clicked + */ + @Output() prev = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the next button is clicked + */ + @Output() next = new EventEmitter(); + /** * The current listable objects */ @@ -192,4 +207,18 @@ export class ObjectListComponent { onPaginationChange(event) { this.paginationChange.emit(event); } + + /** + * Go to the previous page + */ + goPrev() { + this.prev.emit(true); + } + + /** + * Go to the next page + */ + goNext() { + this.next.emit(true); + } } diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html index 2a9aa1a062..785d38af96 100644 --- a/src/app/shared/pagination/pagination.component.html +++ b/src/app/shared/pagination/pagination.component.html @@ -1,7 +1,7 @@
    -
    +
    {{ 'pagination.showing.label' | translate }} {{ 'pagination.showing.detail' | translate:(getShowingDetails(collectionSize)|async)}}
    @@ -20,16 +20,31 @@
    - diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts index 8f1c6bf26f..21f495956d 100644 --- a/src/app/shared/pagination/pagination.component.ts +++ b/src/app/shared/pagination/pagination.component.ts @@ -20,6 +20,10 @@ import { hasValue } from '../empty.util'; import { PageInfo } from '../../core/shared/page-info.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { map } from 'rxjs/operators'; +import { RemoteData } from 'src/app/core/data/remote-data'; +import { PaginatedList } from 'src/app/core/data/paginated-list.model'; +import { ListableObject } from '../object-collection/shared/listable-object.model'; +import { ViewMode } from 'src/app/core/shared/view-mode.model'; /** * The default pagination controls component. @@ -33,6 +37,11 @@ import { map } from 'rxjs/operators'; encapsulation: ViewEncapsulation.Emulated }) export class PaginationComponent implements OnDestroy, OnInit { + /** + * ViewMode that should be passed to {@link ListableObjectComponentLoaderComponent}. + */ + viewMode: ViewMode = ViewMode.ListElement; + /** * Number of items in collection. */ @@ -53,6 +62,26 @@ export class PaginationComponent implements OnDestroy, OnInit { */ @Input() sortOptions: SortOptions; + /** + * Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination + */ + @Input() showPaginator = true; + + /** + * The current pagination configuration + */ + @Input() config?: PaginationComponentOptions; + + /** + * The list of listable objects to render in this component + */ + @Input() objects: RemoteData>; + + /** + * The current sorting configuration + */ + @Input() sortConfig: SortOptions; + /** * An event fired when the page is changed. * Event's payload equals to the newly selected page. @@ -163,6 +192,15 @@ export class PaginationComponent implements OnDestroy, OnInit { */ private subs: Subscription[] = []; + /** + * If showPaginator is set to true, emit when the previous button is clicked + */ + @Output() prev = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the next button is clicked + */ + @Output() next = new EventEmitter(); /** * Method provided by Angular. Invoked after the constructor. */ @@ -347,4 +385,19 @@ export class PaginationComponent implements OnDestroy, OnInit { map((hasMultiplePages) => hasMultiplePages || !this.hidePagerWhenSinglePage) ); } + + /** + * Go to the previous page + */ + goPrev() { + this.prev.emit(true); + } + + /** + * Go to the next page + */ + goNext() { + this.next.emit(true); + } + } From 57ff37ec7f1b1caeb9860b9b3a87489b5402d542 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Wed, 2 Feb 2022 19:11:06 +0530 Subject: [PATCH 017/570] [DSC-389] merged with main branch --- config/config.example.yml | 3 + src/app/app-routing-paths.ts | 6 + .../edit-bitstream-page.component.html | 2 +- .../edit-bitstream-page.component.scss | 4 + .../edit-bitstream-page.component.spec.ts | 539 +- .../edit-bitstream-page.component.ts | 264 +- src/app/core/auth/models/auth.method-type.ts | 3 +- src/app/core/auth/models/auth.method.ts | 5 + ...onfig-accesses-conditions-options.model.ts | 45 + .../models/config-submission-access.model.ts | 42 + .../config-submission-accesses.model.ts | 10 + src/app/core/config/models/config-type.ts | 2 + .../submission-accesses-config.service.ts | 42 + src/app/core/core.module.ts | 14 +- .../data/feature-authorization/feature-id.ts | 1 + .../feedback/feedback-data.service.spec.ts | 88 + .../core/feedback/feedback-data.service.ts | 49 + src/app/core/feedback/feedback.guard.ts | 20 + .../core/feedback/models/feedback.model.ts | 34 + .../feedback/models/feedback.resource-type.ts | 9 + .../models/access-condition.model.ts | 25 + .../submission-accesses.resource-type.ts | 9 + .../submission-item-access-condition.model.ts | 8 + ...sion-upload-file-access-condition.model.ts | 23 +- .../workspaceitem-section-accesses.model.ts | 21 + .../models/workspaceitem-sections.model.ts | 2 + src/app/footer/footer.component.html | 4 + .../feedback-form.component.html | 45 + .../feedback-form.component.scss | 3 + .../feedback-form.component.spec.ts | 97 + .../feedback-form/feedback-form.component.ts | 83 + src/app/info/feedback/feedback.component.html | 3 + src/app/info/feedback/feedback.component.scss | 0 .../info/feedback/feedback.component.spec.ts | 27 + src/app/info/feedback/feedback.component.ts | 12 + .../feedback/themed-feedback.component.ts | 26 + src/app/info/info-routing-paths.ts | 5 + src/app/info/info-routing.module.ts | 14 +- src/app/info/info.module.ts | 15 +- .../item-delete/item-delete.component.spec.ts | 72 +- .../item-delete/item-delete.component.ts | 59 +- ...amic-form-control-container.component.html | 3 +- .../dynamic-form-array.component.html | 4 +- .../dynamic-form-array.component.scss | 10 +- .../methods/oidc/log-in-oidc.component.html | 3 + .../oidc/log-in-oidc.component.spec.ts | 155 + .../methods/oidc/log-in-oidc.component.ts | 110 + .../shared/mocks/mock-native-window-ref.ts | 3 +- .../section-accesses-config.service.mock.ts | 87 + .../mocks/section-accesses.service.mock.ts | 13 + src/app/shared/mocks/submission.mock.ts | 93 +- src/app/shared/shared.module.ts | 3 + src/app/shared/testing/form-event.stub.ts | 100 + src/app/shared/testing/route-service.stub.ts | 5 +- .../form/submission-form.component.ts | 2 +- .../objects/submission-objects.effects.ts | 6 +- .../accesses/section-accesses.component.html | 7 + .../accesses/section-accesses.component.scss | 5 + .../section-accesses.component.spec.ts | 204 + .../accesses/section-accesses.component.ts | 379 + .../accesses/section-accesses.model.ts | 123 + .../accesses/section-accesses.service.ts | 42 + .../container/section-container.component.ts | 6 +- .../form/section-form.component.spec.ts | 16 +- .../sections/form/section-form.component.ts | 2 +- .../sections/models/section.model.ts | 4 +- src/app/submission/sections/sections-type.ts | 3 +- .../edit/section-upload-file-edit.model.ts | 5 +- .../file/section-upload-file.component.ts | 12 +- src/app/submission/selectors.ts | 11 +- src/app/submission/submission.module.ts | 58 +- src/assets/i18n/en.json5 | 84 + src/assets/i18n/gd.json5 | 6076 +++++++++++++++++ src/config/default-app-config.ts | 1 + .../app/info/feedback/feedback.component.html | 0 .../app/info/feedback/feedback.component.scss | 0 .../app/info/feedback/feedback.component.ts | 15 + src/themes/custom/theme.module.ts | 4 +- 78 files changed, 9098 insertions(+), 291 deletions(-) create mode 100644 src/app/core/config/models/config-accesses-conditions-options.model.ts create mode 100644 src/app/core/config/models/config-submission-access.model.ts create mode 100644 src/app/core/config/models/config-submission-accesses.model.ts create mode 100644 src/app/core/config/submission-accesses-config.service.ts create mode 100644 src/app/core/feedback/feedback-data.service.spec.ts create mode 100644 src/app/core/feedback/feedback-data.service.ts create mode 100644 src/app/core/feedback/feedback.guard.ts create mode 100644 src/app/core/feedback/models/feedback.model.ts create mode 100644 src/app/core/feedback/models/feedback.resource-type.ts create mode 100644 src/app/core/submission/models/access-condition.model.ts create mode 100644 src/app/core/submission/models/submission-accesses.resource-type.ts create mode 100644 src/app/core/submission/models/submission-item-access-condition.model.ts create mode 100644 src/app/core/submission/models/workspaceitem-section-accesses.model.ts create mode 100644 src/app/info/feedback/feedback-form/feedback-form.component.html create mode 100644 src/app/info/feedback/feedback-form/feedback-form.component.scss create mode 100644 src/app/info/feedback/feedback-form/feedback-form.component.spec.ts create mode 100644 src/app/info/feedback/feedback-form/feedback-form.component.ts create mode 100644 src/app/info/feedback/feedback.component.html create mode 100644 src/app/info/feedback/feedback.component.scss create mode 100644 src/app/info/feedback/feedback.component.spec.ts create mode 100644 src/app/info/feedback/feedback.component.ts create mode 100644 src/app/info/feedback/themed-feedback.component.ts create mode 100644 src/app/shared/log-in/methods/oidc/log-in-oidc.component.html create mode 100644 src/app/shared/log-in/methods/oidc/log-in-oidc.component.spec.ts create mode 100644 src/app/shared/log-in/methods/oidc/log-in-oidc.component.ts create mode 100644 src/app/shared/mocks/section-accesses-config.service.mock.ts create mode 100644 src/app/shared/mocks/section-accesses.service.mock.ts create mode 100644 src/app/shared/testing/form-event.stub.ts create mode 100644 src/app/submission/sections/accesses/section-accesses.component.html create mode 100644 src/app/submission/sections/accesses/section-accesses.component.scss create mode 100644 src/app/submission/sections/accesses/section-accesses.component.spec.ts create mode 100644 src/app/submission/sections/accesses/section-accesses.component.ts create mode 100644 src/app/submission/sections/accesses/section-accesses.model.ts create mode 100644 src/app/submission/sections/accesses/section-accesses.service.ts create mode 100644 src/assets/i18n/gd.json5 create mode 100644 src/themes/custom/app/info/feedback/feedback.component.html create mode 100644 src/themes/custom/app/info/feedback/feedback.component.scss create mode 100644 src/themes/custom/app/info/feedback/feedback.component.ts diff --git a/config/config.example.yml b/config/config.example.yml index 1e035889a5..ecb2a3cfb9 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -127,6 +127,9 @@ languages: - code: fr label: Français active: true + - code: gd + label: Gàidhlig + active: true - code: lv label: Latviešu active: true diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index d9c3410931..57767b6f3e 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -32,6 +32,12 @@ export function getBitstreamRequestACopyRoute(item, bitstream): { routerLink: st }; } +export const HOME_PAGE_PATH = 'admin'; + +export function getHomePageRoute() { + return `/${HOME_PAGE_PATH}`; +} + export const ADMIN_MODULE_PATH = 'admin'; export function getAdminModuleRoute() { diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html index cbb587cca4..4d3b948a58 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html @@ -12,7 +12,7 @@
    - div > label { + margin-top: 1.75rem; +} + diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts index 67f8866e6d..44e48182fd 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts @@ -22,6 +22,8 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } f import { getEntityEditRoute } from '../../item-page/item-page-routing-paths'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { Item } from '../../core/shared/item.model'; +import { MetadataValueFilter } from '../../core/shared/metadata.models'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info'); const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning'); @@ -31,24 +33,27 @@ let notificationsService: NotificationsService; let formService: DynamicFormService; let bitstreamService: BitstreamDataService; let bitstreamFormatService: BitstreamFormatDataService; +let dsoNameService: DSONameService; let bitstream: Bitstream; let selectedFormat: BitstreamFormat; let allFormats: BitstreamFormat[]; let router: Router; -describe('EditBitstreamPageComponent', () => { - let comp: EditBitstreamPageComponent; - let fixture: ComponentFixture; +let comp: EditBitstreamPageComponent; +let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { +describe('EditBitstreamPageComponent', () => { + + beforeEach(() => { allFormats = [ Object.assign({ id: '1', shortDescription: 'Unknown', description: 'Unknown format', supportLevel: BitstreamFormatSupportLevel.Unknown, + mimetype: 'application/octet-stream', _links: { - self: { href: 'format-selflink-1' } + self: {href: 'format-selflink-1'} } }), Object.assign({ @@ -56,8 +61,9 @@ describe('EditBitstreamPageComponent', () => { shortDescription: 'PNG', description: 'Portable Network Graphics', supportLevel: BitstreamFormatSupportLevel.Known, + mimetype: 'image/png', _links: { - self: { href: 'format-selflink-2' } + self: {href: 'format-selflink-2'} } }), Object.assign({ @@ -65,19 +71,14 @@ describe('EditBitstreamPageComponent', () => { shortDescription: 'GIF', description: 'Graphics Interchange Format', supportLevel: BitstreamFormatSupportLevel.Known, + mimetype: 'image/gif', _links: { - self: { href: 'format-selflink-3' } + self: {href: 'format-selflink-3'} } }) ] as BitstreamFormat[]; selectedFormat = allFormats[1]; - notificationsService = jasmine.createSpyObj('notificationsService', - { - info: infoNotification, - warning: warningNotification, - success: successNotification - } - ); + formService = Object.assign({ createFormGroup: (fModel: DynamicFormControlModel[]) => { const controls = {}; @@ -90,156 +91,418 @@ describe('EditBitstreamPageComponent', () => { return undefined; } }); - bitstream = Object.assign(new Bitstream(), { - metadata: { - 'dc.description': [ - { - value: 'Bitstream description' - } - ], - 'dc.title': [ - { - value: 'Bitstream title' - } - ] - }, - format: createSuccessfulRemoteDataObject$(selectedFormat), - _links: { - self: 'bitstream-selflink' - }, - bundle: createSuccessfulRemoteDataObject$({ - item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), { - uuid: 'some-uuid' - })) - }) - }); - bitstreamService = jasmine.createSpyObj('bitstreamService', { - findById: createSuccessfulRemoteDataObject$(bitstream), - update: createSuccessfulRemoteDataObject$(bitstream), - updateFormat: createSuccessfulRemoteDataObject$(bitstream), - commitUpdates: {}, - patch: {} - }); + bitstreamFormatService = jasmine.createSpyObj('bitstreamFormatService', { findAll: createSuccessfulRemoteDataObject$(createPaginatedList(allFormats)) }); - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), RouterTestingModule], - declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective], - providers: [ - { provide: NotificationsService, useValue: notificationsService }, - { provide: DynamicFormService, useValue: formService }, - { provide: ActivatedRoute, useValue: { data: observableOf({ bitstream: createSuccessfulRemoteDataObject(bitstream) }), snapshot: { queryParams: {} } } }, - { provide: BitstreamDataService, useValue: bitstreamService }, - { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, - ChangeDetectorRef - ], - schemas: [NO_ERRORS_SCHEMA] - }).compileComponents(); - - })); - - beforeEach(() => { - fixture = TestBed.createComponent(EditBitstreamPageComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - router = TestBed.inject(Router); - spyOn(router, 'navigate'); + notificationsService = jasmine.createSpyObj('notificationsService', + { + info: infoNotification, + warning: warningNotification, + success: successNotification + } + ); }); - describe('on startup', () => { - let rawForm; + describe('EditBitstreamPageComponent no IIIF fields', () => { + + beforeEach(waitForAsync(() => { + + const bundleName = 'ORIGINAL'; + + bitstream = Object.assign(new Bitstream(), { + metadata: { + 'dc.description': [ + { + value: 'Bitstream description' + } + ], + 'dc.title': [ + { + value: 'Bitstream title' + } + ] + }, + format: createSuccessfulRemoteDataObject$(selectedFormat), + _links: { + self: 'bitstream-selflink' + }, + bundle: createSuccessfulRemoteDataObject$({ + item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), { + uuid: 'some-uuid', + firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string { + return undefined; + }, + })) + }) + }); + bitstreamService = jasmine.createSpyObj('bitstreamService', { + findById: createSuccessfulRemoteDataObject$(bitstream), + update: createSuccessfulRemoteDataObject$(bitstream), + updateFormat: createSuccessfulRemoteDataObject$(bitstream), + commitUpdates: {}, + patch: {} + }); + bitstreamFormatService = jasmine.createSpyObj('bitstreamFormatService', { + findAll: createSuccessfulRemoteDataObject$(createPaginatedList(allFormats)) + }); + dsoNameService = jasmine.createSpyObj('dsoNameService', { + getName: bundleName + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule], + declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective], + providers: [ + {provide: NotificationsService, useValue: notificationsService}, + {provide: DynamicFormService, useValue: formService}, + {provide: ActivatedRoute, + useValue: { + data: observableOf({bitstream: createSuccessfulRemoteDataObject(bitstream)}), + snapshot: {queryParams: {}} + } + }, + {provide: BitstreamDataService, useValue: bitstreamService}, + {provide: DSONameService, useValue: dsoNameService}, + {provide: BitstreamFormatDataService, useValue: bitstreamFormatService}, + ChangeDetectorRef + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + + })); beforeEach(() => { - rawForm = comp.formGroup.getRawValue(); + fixture = TestBed.createComponent(EditBitstreamPageComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + router = TestBed.inject(Router); + spyOn(router, 'navigate'); }); - it('should fill in the bitstream\'s title', () => { - expect(rawForm.fileNamePrimaryContainer.fileName).toEqual(bitstream.name); - }); + describe('on startup', () => { + let rawForm; - it('should fill in the bitstream\'s description', () => { - expect(rawForm.descriptionContainer.description).toEqual(bitstream.firstMetadataValue('dc.description')); - }); - - it('should select the correct format', () => { - expect(rawForm.formatContainer.selectedFormat).toEqual(selectedFormat.id); - }); - - it('should put the \"New Format\" input on invisible', () => { - expect(comp.formLayout.newFormat.grid.host).toContain('invisible'); - }); - }); - - describe('when an unknown format is selected', () => { - beforeEach(() => { - comp.updateNewFormatLayout(allFormats[0].id); - }); - - it('should remove the invisible class from the \"New Format\" input', () => { - expect(comp.formLayout.newFormat.grid.host).not.toContain('invisible'); - }); - }); - - describe('onSubmit', () => { - describe('when selected format hasn\'t changed', () => { beforeEach(() => { - comp.onSubmit(); + rawForm = comp.formGroup.getRawValue(); }); - it('should call update', () => { - expect(bitstreamService.update).toHaveBeenCalled(); + it('should fill in the bitstream\'s title', () => { + expect(rawForm.fileNamePrimaryContainer.fileName).toEqual(bitstream.name); }); - it('should commit the updates', () => { - expect(bitstreamService.commitUpdates).toHaveBeenCalled(); + it('should fill in the bitstream\'s description', () => { + expect(rawForm.descriptionContainer.description).toEqual(bitstream.firstMetadataValue('dc.description')); + }); + + it('should select the correct format', () => { + expect(rawForm.formatContainer.selectedFormat).toEqual(selectedFormat.id); + }); + + it('should put the \"New Format\" input on invisible', () => { + expect(comp.formLayout.newFormat.grid.host).toContain('invisible'); }); }); - describe('when selected format has changed', () => { + describe('when an unknown format is selected', () => { beforeEach(() => { - comp.formGroup.patchValue({ - formatContainer: { - selectedFormat: allFormats[2].id - } + comp.updateNewFormatLayout(allFormats[0].id); + }); + + it('should remove the invisible class from the \"New Format\" input', () => { + expect(comp.formLayout.newFormat.grid.host).not.toContain('invisible'); + }); + }); + + describe('onSubmit', () => { + describe('when selected format hasn\'t changed', () => { + beforeEach(() => { + comp.onSubmit(); }); + + it('should call update', () => { + expect(bitstreamService.update).toHaveBeenCalled(); + }); + + it('should commit the updates', () => { + expect(bitstreamService.commitUpdates).toHaveBeenCalled(); + }); + }); + + describe('when selected format has changed', () => { + beforeEach(() => { + comp.formGroup.patchValue({ + formatContainer: { + selectedFormat: allFormats[2].id + } + }); + fixture.detectChanges(); + comp.onSubmit(); + }); + + it('should call update', () => { + expect(bitstreamService.update).toHaveBeenCalled(); + }); + + it('should call updateFormat', () => { + expect(bitstreamService.updateFormat).toHaveBeenCalled(); + }); + + it('should commit the updates', () => { + expect(bitstreamService.commitUpdates).toHaveBeenCalled(); + }); + }); + }); + describe('when the cancel button is clicked', () => { + it('should call navigateToItemEditBitstreams method', () => { + spyOn(comp, 'navigateToItemEditBitstreams'); + comp.onCancel(); + expect(comp.navigateToItemEditBitstreams).toHaveBeenCalled(); + }); + }); + describe('when navigateToItemEditBitstreams is called, and the component has an itemId', () => { + it('should redirect to the item edit page on the bitstreams tab with the itemId from the component', () => { + comp.itemId = 'some-uuid1'; + comp.navigateToItemEditBitstreams(); + expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid1'), 'bitstreams']); + }); + }); + describe('when navigateToItemEditBitstreams is called, and the component does not have an itemId', () => { + it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => { + comp.itemId = undefined; + comp.navigateToItemEditBitstreams(); + expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid'), 'bitstreams']); + }); + }); + }); + + describe('EditBitstreamPageComponent with IIIF fields', () => { + + const bundleName = 'ORIGINAL'; + + beforeEach(waitForAsync(() => { + + bitstream = Object.assign(new Bitstream(), { + metadata: { + 'dc.description': [ + { + value: 'Bitstream description' + } + ], + 'dc.title': [ + { + value: 'Bitstream title' + } + ], + 'iiif.label': [ + { + value: 'chapter one' + } + ], + 'iiif.toc': [ + { + value: 'chapter one' + } + ], + 'iiif.image.width': [ + { + value: '2400' + } + ], + 'iiif.image.height': [ + { + value: '2800' + } + ], + }, + format: createSuccessfulRemoteDataObject$(allFormats[1]), + _links: { + self: 'bitstream-selflink' + }, + bundle: createSuccessfulRemoteDataObject$({ + item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), { + uuid: 'some-uuid', + firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string { + return 'True'; + } + })) + }) + }); + bitstreamService = jasmine.createSpyObj('bitstreamService', { + findById: createSuccessfulRemoteDataObject$(bitstream), + update: createSuccessfulRemoteDataObject$(bitstream), + updateFormat: createSuccessfulRemoteDataObject$(bitstream), + commitUpdates: {}, + patch: {} + }); + + dsoNameService = jasmine.createSpyObj('dsoNameService', { + getName: bundleName + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule], + declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective], + providers: [ + {provide: NotificationsService, useValue: notificationsService}, + {provide: DynamicFormService, useValue: formService}, + { + provide: ActivatedRoute, + useValue: { + data: observableOf({bitstream: createSuccessfulRemoteDataObject(bitstream)}), + snapshot: {queryParams: {}} + } + }, + {provide: BitstreamDataService, useValue: bitstreamService}, + {provide: DSONameService, useValue: dsoNameService}, + {provide: BitstreamFormatDataService, useValue: bitstreamFormatService}, + ChangeDetectorRef + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditBitstreamPageComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + router = TestBed.inject(Router); + spyOn(router, 'navigate'); + }); + + + describe('on startup', () => { + let rawForm; + + beforeEach(() => { + rawForm = comp.formGroup.getRawValue(); + }); + it('should set isIIIF to true', () => { + expect(comp.isIIIF).toBeTrue(); + }); + it('should fill in the iiif label', () => { + expect(rawForm.iiifLabelContainer.iiifLabel).toEqual('chapter one'); + }); + it('should fill in the iiif toc', () => { + expect(rawForm.iiifTocContainer.iiifToc).toEqual('chapter one'); + }); + it('should fill in the iiif width', () => { + expect(rawForm.iiifWidthContainer.iiifWidth).toEqual('2400'); + }); + it('should fill in the iiif height', () => { + expect(rawForm.iiifHeightContainer.iiifHeight).toEqual('2800'); + }); + }); + }); + + describe('ignore OTHERCONTENT bundle', () => { + + const bundleName = 'OTHERCONTENT'; + + beforeEach(waitForAsync(() => { + + bitstream = Object.assign(new Bitstream(), { + metadata: { + 'dc.description': [ + { + value: 'Bitstream description' + } + ], + 'dc.title': [ + { + value: 'Bitstream title' + } + ], + 'iiif.label': [ + { + value: 'chapter one' + } + ], + 'iiif.toc': [ + { + value: 'chapter one' + } + ], + 'iiif.image.width': [ + { + value: '2400' + } + ], + 'iiif.image.height': [ + { + value: '2800' + } + ], + }, + format: createSuccessfulRemoteDataObject$(allFormats[2]), + _links: { + self: 'bitstream-selflink' + }, + bundle: createSuccessfulRemoteDataObject$({ + item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), { + uuid: 'some-uuid', + firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string { + return 'True'; + } + })) + }) + }); + bitstreamService = jasmine.createSpyObj('bitstreamService', { + findById: createSuccessfulRemoteDataObject$(bitstream), + update: createSuccessfulRemoteDataObject$(bitstream), + updateFormat: createSuccessfulRemoteDataObject$(bitstream), + commitUpdates: {}, + patch: {} + }); + + dsoNameService = jasmine.createSpyObj('dsoNameService', { + getName: bundleName + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule], + declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective], + providers: [ + {provide: NotificationsService, useValue: notificationsService}, + {provide: DynamicFormService, useValue: formService}, + {provide: ActivatedRoute, + useValue: { + data: observableOf({bitstream: createSuccessfulRemoteDataObject(bitstream)}), + snapshot: {queryParams: {}} + } + }, + {provide: BitstreamDataService, useValue: bitstreamService}, + {provide: DSONameService, useValue: dsoNameService}, + {provide: BitstreamFormatDataService, useValue: bitstreamFormatService}, + ChangeDetectorRef + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditBitstreamPageComponent); + comp = fixture.componentInstance; fixture.detectChanges(); - comp.onSubmit(); + router = TestBed.inject(Router); + spyOn(router, 'navigate'); }); - it('should call update', () => { - expect(bitstreamService.update).toHaveBeenCalled(); - }); + describe('EditBitstreamPageComponent with IIIF fields', () => { + let rawForm; - it('should call updateFormat', () => { - expect(bitstreamService.updateFormat).toHaveBeenCalled(); - }); + beforeEach(() => { + rawForm = comp.formGroup.getRawValue(); + }); - it('should commit the updates', () => { - expect(bitstreamService.commitUpdates).toHaveBeenCalled(); + it('should NOT set isIIIF to true', () => { + expect(comp.isIIIF).toBeFalse(); + }); + it('should put the \"IIIF Label\" input not to be shown', () => { + expect(rawForm.iiifLabelContainer).toBeFalsy(); + }); }); - }); - }); - describe('when the cancel button is clicked', () => { - it('should call navigateToItemEditBitstreams method', () => { - spyOn(comp, 'navigateToItemEditBitstreams'); - comp.onCancel(); - expect(comp.navigateToItemEditBitstreams).toHaveBeenCalled(); - }); - }); - describe('when navigateToItemEditBitstreams is called, and the component has an itemId', () => { - it('should redirect to the item edit page on the bitstreams tab with the itemId from the component', () => { - comp.itemId = 'some-uuid1'; - comp.navigateToItemEditBitstreams(); - expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid1'), 'bitstreams']); - }); - }); - describe('when navigateToItemEditBitstreams is called, and the component does not have an itemId', () => { - it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => { - comp.itemId = undefined; - comp.navigateToItemEditBitstreams(); - expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid'), 'bitstreams']); - }); }); + }); diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts index f6ece7f4fa..9a59df4b95 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts @@ -1,16 +1,27 @@ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + OnDestroy, + OnInit +} from '@angular/core'; import { Bitstream } from '../../core/shared/bitstream.model'; import { ActivatedRoute, Router } from '@angular/router'; import { map, mergeMap, switchMap } from 'rxjs/operators'; -import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; +import { + combineLatest, + combineLatest as observableCombineLatest, + Observable, + of as observableOf, + Subscription +} from 'rxjs'; import { DynamicFormControlModel, DynamicFormGroupModel, DynamicFormLayout, DynamicFormService, DynamicInputModel, - DynamicSelectModel, - DynamicTextAreaModel + DynamicSelectModel } from '@ng-dynamic-forms/core'; import { FormGroup } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -28,13 +39,19 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { BitstreamFormatDataService } from '../../core/data/bitstream-format-data.service'; import { BitstreamFormat } from '../../core/shared/bitstream-format.model'; import { BitstreamFormatSupportLevel } from '../../core/shared/bitstream-format-support-level'; -import { hasValue, isNotEmpty } from '../../shared/empty.util'; +import { hasValue, isNotEmpty, isEmpty } from '../../shared/empty.util'; import { Metadata } from '../../core/shared/metadata.utils'; import { Location } from '@angular/common'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { getEntityEditRoute, getItemEditRoute } from '../../item-page/item-page-routing-paths'; import { Bundle } from '../../core/shared/bundle.model'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { Item } from '../../core/shared/item.model'; +import { + DsDynamicInputModel +} from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model'; +import { DsDynamicTextAreaModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-textarea.model'; @Component({ selector: 'ds-edit-bitstream-page', @@ -94,6 +111,26 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { */ NOTIFICATIONS_PREFIX = 'bitstream.edit.notifications.'; + /** + * IIIF image width metadata key + */ + IMAGE_WIDTH_METADATA = 'iiif.image.width'; + + /** + * IIIF image height metadata key + */ + IMAGE_HEIGHT_METADATA = 'iiif.image.height'; + + /** + * IIIF table of contents metadata key + */ + IIIF_TOC_METADATA = 'iiif.toc'; + + /** + * IIIF label metadata key + */ + IIIF_LABEL_METADATA = 'iiif.label'; + /** * Options for fetching all bitstream formats */ @@ -102,7 +139,8 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { /** * The Dynamic Input Model for the file's name */ - fileNameModel = new DynamicInputModel({ + fileNameModel = new DsDynamicInputModel({ + hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '', id: 'fileName', name: 'fileName', required: true, @@ -118,14 +156,16 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { * The Dynamic Switch Model for the file's name */ primaryBitstreamModel = new DynamicCustomSwitchModel({ - id: 'primaryBitstream', - name: 'primaryBitstream' - }); + id: 'primaryBitstream', + name: 'primaryBitstream' + } + ); /** * The Dynamic TextArea Model for the file's description */ - descriptionModel = new DynamicTextAreaModel({ + descriptionModel = new DsDynamicTextAreaModel({ + hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '', id: 'description', name: 'description', rows: 10 @@ -147,10 +187,87 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { name: 'newFormat' }); + /** + * The Dynamic Input Model for the iiif label + */ + iiifLabelModel = new DsDynamicInputModel({ + hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '', + id: 'iiifLabel', + name: 'iiifLabel' + }, + { + grid: { + host: 'col col-lg-6 d-inline-block' + } + }); + iiifLabelContainer = new DynamicFormGroupModel({ + id: 'iiifLabelContainer', + group: [this.iiifLabelModel] + },{ + grid: { + host: 'form-row' + } + }); + + iiifTocModel = new DsDynamicInputModel({ + hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '', + id: 'iiifToc', + name: 'iiifToc', + },{ + grid: { + host: 'col col-lg-6 d-inline-block' + } + }); + iiifTocContainer = new DynamicFormGroupModel({ + id: 'iiifTocContainer', + group: [this.iiifTocModel] + },{ + grid: { + host: 'form-row' + } + }); + + iiifWidthModel = new DsDynamicInputModel({ + hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '', + id: 'iiifWidth', + name: 'iiifWidth', + },{ + grid: { + host: 'col col-lg-6 d-inline-block' + } + }); + iiifWidthContainer = new DynamicFormGroupModel({ + id: 'iiifWidthContainer', + group: [this.iiifWidthModel] + },{ + grid: { + host: 'form-row' + } + }); + + iiifHeightModel = new DsDynamicInputModel({ + hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '', + id: 'iiifHeight', + name: 'iiifHeight' + },{ + grid: { + host: 'col col-lg-6 d-inline-block' + } + }); + iiifHeightContainer = new DynamicFormGroupModel({ + id: 'iiifHeightContainer', + group: [this.iiifHeightModel] + },{ + grid: { + host: 'form-row' + } + }); + /** * All input models in a simple array for easier iterations */ - inputModels = [this.fileNameModel, this.primaryBitstreamModel, this.descriptionModel, this.selectedFormatModel, this.newFormatModel]; + inputModels = [this.fileNameModel, this.primaryBitstreamModel, this.descriptionModel, this.selectedFormatModel, + this.newFormatModel]; /** * The dynamic form fields used for editing the information of a bitstream @@ -163,7 +280,11 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { this.fileNameModel, this.primaryBitstreamModel ] - }), + },{ + grid: { + host: 'form-row' + } + }), new DynamicFormGroupModel({ id: 'descriptionContainer', group: [ @@ -254,18 +375,27 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { */ entityType: string; + /** + * Set to true when the parent item supports IIIF. + */ + isIIIF = false; + + /** * Array to track all subscriptions and unsubscribe them onDestroy * @type {Array} */ protected subs: Subscription[] = []; + constructor(private route: ActivatedRoute, private router: Router, + private changeDetectorRef: ChangeDetectorRef, private location: Location, private formService: DynamicFormService, private translate: TranslateService, private bitstreamService: BitstreamDataService, + private dsoNameService: DSONameService, private notificationsService: NotificationsService, private bitstreamFormatService: BitstreamFormatDataService) { } @@ -277,7 +407,6 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { * - Translate the form labels and hints */ ngOnInit(): void { - this.formGroup = this.formService.createFormGroup(this.formModel); this.itemId = this.route.snapshot.queryParams.itemId; this.entityType = this.route.snapshot.queryParams.entityType; @@ -301,13 +430,10 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { ).subscribe(([bitstream, allFormats]) => { this.bitstream = bitstream as Bitstream; this.formats = allFormats.page; - this.updateFormatModel(); - this.updateForm(this.bitstream); + this.setIiifStatus(this.bitstream); }) ); - this.updateFieldTranslations(); - this.subs.push( this.translate.onLangChange .subscribe(() => { @@ -316,6 +442,16 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { ); } + /** + * Initializes the form. + */ + setForm() { + this.formGroup = this.formService.createFormGroup(this.formModel); + this.updateFormatModel(); + this.updateForm(this.bitstream); + this.updateFieldTranslations(); + } + /** * Update the current form values with bitstream properties * @param bitstream @@ -333,6 +469,22 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { newFormat: hasValue(bitstream.firstMetadata('dc.format')) ? bitstream.firstMetadata('dc.format').value : undefined } }); + if (this.isIIIF) { + this.formGroup.patchValue({ + iiifLabelContainer: { + iiifLabel: bitstream.firstMetadataValue(this.IIIF_LABEL_METADATA) + }, + iiifTocContainer: { + iiifToc: bitstream.firstMetadataValue(this.IIIF_TOC_METADATA) + }, + iiifWidthContainer: { + iiifWidth: bitstream.firstMetadataValue(this.IMAGE_WIDTH_METADATA) + }, + iiifHeightContainer: { + iiifHeight: bitstream.firstMetadataValue(this.IMAGE_HEIGHT_METADATA) + } + }); + } this.bitstream.format.pipe( getAllSucceededRemoteDataPayload() ).subscribe((format: BitstreamFormat) => { @@ -467,6 +619,32 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { const primary = rawForm.fileNamePrimaryContainer.primaryBitstream; Metadata.setFirstValue(newMetadata, 'dc.title', rawForm.fileNamePrimaryContainer.fileName); Metadata.setFirstValue(newMetadata, 'dc.description', rawForm.descriptionContainer.description); + if (this.isIIIF) { + // It's helpful to remove these metadata elements entirely when the form value is empty. + // This avoids potential issues on the REST side and makes it possible to do things like + // remove an existing "table of contents" entry. + if (isEmpty(rawForm.iiifLabelContainer.iiifLabel)) { + + delete newMetadata[this.IIIF_LABEL_METADATA]; + } else { + Metadata.setFirstValue(newMetadata, this.IIIF_LABEL_METADATA, rawForm.iiifLabelContainer.iiifLabel); + } + if (isEmpty(rawForm.iiifTocContainer.iiifToc)) { + delete newMetadata[this.IIIF_TOC_METADATA]; + } else { + Metadata.setFirstValue(newMetadata, this.IIIF_TOC_METADATA, rawForm.iiifTocContainer.iiifToc); + } + if (isEmpty(rawForm.iiifWidthContainer.iiifWidth)) { + delete newMetadata[this.IMAGE_WIDTH_METADATA]; + } else { + Metadata.setFirstValue(newMetadata, this.IMAGE_WIDTH_METADATA, rawForm.iiifWidthContainer.iiifWidth); + } + if (isEmpty(rawForm.iiifHeightContainer.iiifHeight)) { + delete newMetadata[this.IMAGE_HEIGHT_METADATA]; + } else { + Metadata.setFirstValue(newMetadata, this.IMAGE_HEIGHT_METADATA, rawForm.iiifHeightContainer.iiifHeight); + } + } if (isNotEmpty(rawForm.formatContainer.newFormat)) { Metadata.setFirstValue(newMetadata, 'dc.format', rawForm.formatContainer.newFormat); } @@ -497,6 +675,58 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { } } + /** + * Verifies that the parent item is iiif-enabled. Checks bitstream mimetype to be + * sure it's an image, excluding bitstreams in the THUMBNAIL or OTHERCONTENT bundles. + * @param bitstream + */ + setIiifStatus(bitstream: Bitstream) { + + const regexExcludeBundles = /OTHERCONTENT|THUMBNAIL|LICENSE/; + const regexIIIFItem = /true|yes/i; + + const isImage$ = this.bitstream.format.pipe( + getFirstSucceededRemoteData(), + map((format: RemoteData) => format.payload.mimetype.includes('image/'))); + + const isIIIFBundle$ = this.bitstream.bundle.pipe( + getFirstSucceededRemoteData(), + map((bundle: RemoteData) => + this.dsoNameService.getName(bundle.payload).match(regexExcludeBundles) == null)); + + const isEnabled$ = this.bitstream.bundle.pipe( + getFirstSucceededRemoteData(), + map((bundle: RemoteData) => bundle.payload.item.pipe( + getFirstSucceededRemoteData(), + map((item: RemoteData) => + (item.payload.firstMetadataValue('dspace.iiif.enabled') && + item.payload.firstMetadataValue('dspace.iiif.enabled').match(regexIIIFItem) !== null) + )))); + + const iiifSub = combineLatest( + isImage$, + isIIIFBundle$, + isEnabled$ + ).subscribe(([isImage, isIIIFBundle, isEnabled]) => { + if (isImage && isIIIFBundle && isEnabled) { + this.isIIIF = true; + this.inputModels.push(this.iiifLabelModel); + this.formModel.push(this.iiifLabelContainer); + this.inputModels.push(this.iiifTocModel); + this.formModel.push(this.iiifTocContainer); + this.inputModels.push(this.iiifWidthModel); + this.formModel.push(this.iiifWidthContainer); + this.inputModels.push(this.iiifHeightModel); + this.formModel.push(this.iiifHeightContainer); + } + this.setForm(); + this.changeDetectorRef.detectChanges(); + }); + + this.subs.push(iiifSub); + + } + /** * Unsubscribe from open subscriptions */ diff --git a/src/app/core/auth/models/auth.method-type.ts b/src/app/core/auth/models/auth.method-type.ts index f053515065..9d999c4c3f 100644 --- a/src/app/core/auth/models/auth.method-type.ts +++ b/src/app/core/auth/models/auth.method-type.ts @@ -3,5 +3,6 @@ export enum AuthMethodType { Shibboleth = 'shibboleth', Ldap = 'ldap', Ip = 'ip', - X509 = 'x509' + X509 = 'x509', + Oidc = 'oidc' } diff --git a/src/app/core/auth/models/auth.method.ts b/src/app/core/auth/models/auth.method.ts index 617154080b..5a362e8606 100644 --- a/src/app/core/auth/models/auth.method.ts +++ b/src/app/core/auth/models/auth.method.ts @@ -29,6 +29,11 @@ export class AuthMethod { this.authMethodType = AuthMethodType.Password; break; } + case 'oidc': { + this.authMethodType = AuthMethodType.Oidc; + this.location = location; + break; + } default: { break; diff --git a/src/app/core/config/models/config-accesses-conditions-options.model.ts b/src/app/core/config/models/config-accesses-conditions-options.model.ts new file mode 100644 index 0000000000..244b501908 --- /dev/null +++ b/src/app/core/config/models/config-accesses-conditions-options.model.ts @@ -0,0 +1,45 @@ +/** + * Model class for an Item Access Condition + */ +export class AccessesConditionOption { + + /** + * The name for this Access Condition + */ + name: string; + + /** + * The groupName for this Access Condition + */ + groupName: string; + + /** + * A boolean representing if this Access Condition has a start date + */ + hasStartDate: boolean; + + /** + * A boolean representing if this Access Condition has an end date + */ + hasEndDate: boolean; + + /** + * Maximum value of the start date + */ + endDateLimit?: string; + + /** + * Maximum value of the end date + */ + startDateLimit?: string; + + /** + * Maximum value of the start date + */ + maxStartDate?: string; + + /** + * Maximum value of the end date + */ + maxEndDate?: string; +} diff --git a/src/app/core/config/models/config-submission-access.model.ts b/src/app/core/config/models/config-submission-access.model.ts new file mode 100644 index 0000000000..7db96acf2b --- /dev/null +++ b/src/app/core/config/models/config-submission-access.model.ts @@ -0,0 +1,42 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; +import { typedObject } from '../../cache/builders/build-decorators'; +import { ConfigObject } from './config.model'; +import { AccessesConditionOption } from './config-accesses-conditions-options.model'; +import { SUBMISSION_ACCESSES_TYPE } from './config-type'; +import { HALLink } from '../../shared/hal-link.model'; + +/** + * Class for the configuration describing the item accesses condition + */ +@typedObject +@inheritSerialization(ConfigObject) +export class SubmissionAccessModel extends ConfigObject { + static type = SUBMISSION_ACCESSES_TYPE; + + /** + * A list of available item access conditions + */ + @autoserialize + accessConditionOptions: AccessesConditionOption[]; + + /** + * Boolean that indicates whether the current item must be findable via search or browse. + */ + @autoserialize + discoverable: boolean; + + /** + * Boolean that indicates whether or not the user can change the discoverable flag. + */ + @autoserialize + canChangeDiscoverable: boolean; + + /** + * The links to all related resources returned by the rest api. + */ + @deserialize + _links: { + self: HALLink + }; + +} diff --git a/src/app/core/config/models/config-submission-accesses.model.ts b/src/app/core/config/models/config-submission-accesses.model.ts new file mode 100644 index 0000000000..3f8004928d --- /dev/null +++ b/src/app/core/config/models/config-submission-accesses.model.ts @@ -0,0 +1,10 @@ +import { inheritSerialization } from 'cerialize'; +import { typedObject } from '../../cache/builders/build-decorators'; +import { SUBMISSION_ACCESSES_TYPE } from './config-type'; +import { SubmissionAccessModel } from './config-submission-access.model'; + +@typedObject +@inheritSerialization(SubmissionAccessModel) +export class SubmissionAccessesModel extends SubmissionAccessModel { + static type = SUBMISSION_ACCESSES_TYPE; +} diff --git a/src/app/core/config/models/config-type.ts b/src/app/core/config/models/config-type.ts index 858ff19c91..19864536f0 100644 --- a/src/app/core/config/models/config-type.ts +++ b/src/app/core/config/models/config-type.ts @@ -15,3 +15,5 @@ export const SUBMISSION_SECTION_TYPE = new ResourceType('submissionsection'); export const SUBMISSION_UPLOADS_TYPE = new ResourceType('submissionuploads'); export const SUBMISSION_UPLOAD_TYPE = new ResourceType('submissionupload'); + +export const SUBMISSION_ACCESSES_TYPE = new ResourceType('submissionaccessoption'); diff --git a/src/app/core/config/submission-accesses-config.service.ts b/src/app/core/config/submission-accesses-config.service.ts new file mode 100644 index 0000000000..de9afc66ea --- /dev/null +++ b/src/app/core/config/submission-accesses-config.service.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@angular/core'; +import { ConfigService } from './config.service'; +import { RequestService } from '../data/request.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { dataService } from '../cache/builders/build-decorators'; +import { SUBMISSION_ACCESSES_TYPE } from './models/config-type'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; +import { ConfigObject } from './models/config.model'; +import { SubmissionAccessesModel } from './models/config-submission-accesses.model'; +import { RemoteData } from '../data/remote-data'; +import { Observable } from 'rxjs'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; + +/** + * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. + */ +@Injectable() +@dataService(SUBMISSION_ACCESSES_TYPE) +export class SubmissionAccessesConfigService extends ConfigService { + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer + ) { + super(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator, 'submissionaccessoptions'); + } + + findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow): Observable> { + return super.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow as FollowLinkConfig[]) as Observable>; + } +} diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 2c9a24cb80..8d8a614a89 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -2,11 +2,7 @@ import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; -import { - DynamicFormLayoutService, - DynamicFormService, - DynamicFormValidationService -} from '@ng-dynamic-forms/core'; +import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { EffectsModule } from '@ngrx/effects'; import { Action, StoreConfig, StoreModule } from '@ngrx/store'; @@ -77,6 +73,7 @@ import { MetadataSchema } from './metadata/metadata-schema.model'; import { MetadataService } from './metadata/metadata.service'; import { RegistryService } from './registry/registry.service'; import { RoleService } from './roles/role.service'; +import { FeedbackDataService } from './feedback/feedback-data.service'; import { ApiService } from './services/api.service'; import { ServerResponseService } from './services/server-response.service'; @@ -164,6 +161,7 @@ import { Root } from './data/root.model'; import { SearchConfig } from './shared/search/search-filters/search-config.model'; import { SequenceService } from './shared/sequence.service'; import { GroupDataService } from './eperson/group-data.service'; +import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -286,7 +284,8 @@ const PROVIDERS = [ VocabularyService, VocabularyTreeviewService, SequenceService, - GroupDataService + GroupDataService, + FeedbackDataService, ]; /** @@ -345,7 +344,8 @@ export const models = Registration, UsageReport, Root, - SearchConfig + SearchConfig, + SubmissionAccessesModel ]; @NgModule({ diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 01a482dc79..029c75d9cb 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -27,4 +27,5 @@ export enum FeatureID { CanDeleteVersion = 'canDeleteVersion', CanCreateVersion = 'canCreateVersion', CanViewUsageStatistics = 'canViewUsageStatistics', + CanSendFeedback = 'canSendFeedback', } diff --git a/src/app/core/feedback/feedback-data.service.spec.ts b/src/app/core/feedback/feedback-data.service.spec.ts new file mode 100644 index 0000000000..4bb5e642c2 --- /dev/null +++ b/src/app/core/feedback/feedback-data.service.spec.ts @@ -0,0 +1,88 @@ +import { FeedbackDataService } from './feedback-data.service'; +import { HALLink } from '../shared/hal-link.model'; +import { Item } from '../shared/item.model'; +import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { getMockRequestService } from '../../shared/mocks/request.service.mock'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; +import { Feedback } from './models/feedback.model'; + +describe('FeedbackDataService', () => { + let service: FeedbackDataService; + let requestService; + let halService; + let rdbService; + let notificationsService; + let http; + let comparator; + let objectCache; + let store; + let item; + let bundleLink; + let bundleHALLink; + + const feedbackPayload = Object.assign(new Feedback(), { + email: 'test@email.com', + message: 'message', + page: '/home' + }); + + + function initTestService(): FeedbackDataService { + bundleLink = '/items/0fdc0cd7-ff8c-433d-b33c-9b56108abc07/bundles'; + bundleHALLink = new HALLink(); + bundleHALLink.href = bundleLink; + item = new Item(); + item._links = { + bundles: bundleHALLink + }; + requestService = getMockRequestService(); + halService = new HALEndpointServiceStub('url') as any; + rdbService = {} as RemoteDataBuildService; + notificationsService = {} as NotificationsService; + http = {} as HttpClient; + comparator = new DSOChangeAnalyzer() as any; + objectCache = { + + addPatch: () => { + /* empty */ + }, + getObjectBySelfLink: () => { + /* empty */ + } + } as any; + store = {} as Store; + return new FeedbackDataService( + requestService, + rdbService, + store, + objectCache, + halService, + notificationsService, + http, + comparator, + ); + } + + + beforeEach(() => { + service = initTestService(); + }); + + + describe('getFeedback', () => { + beforeEach(() => { + spyOn(service, 'getFeedback'); + service.getFeedback('3'); + }); + + it('should call getFeedback with the feedback link', () => { + expect(service.getFeedback).toHaveBeenCalledWith('3'); + }); + }); + +}); diff --git a/src/app/core/feedback/feedback-data.service.ts b/src/app/core/feedback/feedback-data.service.ts new file mode 100644 index 0000000000..8f6d4e3ecf --- /dev/null +++ b/src/app/core/feedback/feedback-data.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { DataService } from '../data/data.service'; +import { Feedback } from './models/feedback.model'; +import { FEEDBACK } from './models/feedback.resource-type'; +import { dataService } from '../cache/builders/build-decorators'; +import { RequestService } from '../data/request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { Store } from '@ngrx/store'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; +import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators'; + +/** + * Service for checking and managing the feedback + */ +@Injectable() +@dataService(FEEDBACK) +export class FeedbackDataService extends DataService { + protected linkPath = 'feedbacks'; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DSOChangeAnalyzer, + ) { + super(); + } + + /** + * Get feedback from its id + * @param uuid string the id of the feedback + */ + getFeedback(uuid: string): Observable { + return this.findById(uuid).pipe( + getFirstSucceededRemoteData(), + getRemoteDataPayload(), + ); + } + +} diff --git a/src/app/core/feedback/feedback.guard.ts b/src/app/core/feedback/feedback.guard.ts new file mode 100644 index 0000000000..63353a60ff --- /dev/null +++ b/src/app/core/feedback/feedback.guard.ts @@ -0,0 +1,20 @@ +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { Observable } from 'rxjs'; +import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; +import { FeatureID } from '../data/feature-authorization/feature-id'; +import { Injectable } from '@angular/core'; + +/** + * An guard for redirecting users to the feedback page if user is authorized + */ +@Injectable() +export class FeedbackGuard implements CanActivate { + + constructor(private authorizationService: AuthorizationDataService) { + } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.authorizationService.isAuthorized(FeatureID.CanSendFeedback); + } + +} diff --git a/src/app/core/feedback/models/feedback.model.ts b/src/app/core/feedback/models/feedback.model.ts new file mode 100644 index 0000000000..fd98affba9 --- /dev/null +++ b/src/app/core/feedback/models/feedback.model.ts @@ -0,0 +1,34 @@ +import { autoserialize, inheritSerialization } from 'cerialize'; +import { typedObject } from '../../cache/builders/build-decorators'; + +import { DSpaceObject } from '../../shared/dspace-object.model'; +import { HALLink } from '../../shared/hal-link.model'; +import { FEEDBACK } from './feedback.resource-type'; + +@typedObject +@inheritSerialization(DSpaceObject) +export class Feedback extends DSpaceObject { + static type = FEEDBACK; + + /** + * The email address + */ + @autoserialize + public email: string; + + /** + * A string representing message the user inserted + */ + @autoserialize + public message: string; + /** + * A string representing the page from which the user came from + */ + @autoserialize + public page: string; + + _links: { + self: HALLink; + }; + +} diff --git a/src/app/core/feedback/models/feedback.resource-type.ts b/src/app/core/feedback/models/feedback.resource-type.ts new file mode 100644 index 0000000000..89b28c6cb0 --- /dev/null +++ b/src/app/core/feedback/models/feedback.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from '../../shared/resource-type'; + +/** + * The resource type for Feedback + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const FEEDBACK = new ResourceType('feedback'); diff --git a/src/app/core/submission/models/access-condition.model.ts b/src/app/core/submission/models/access-condition.model.ts new file mode 100644 index 0000000000..5ec5c2c434 --- /dev/null +++ b/src/app/core/submission/models/access-condition.model.ts @@ -0,0 +1,25 @@ +/** + * An interface to represent an access condition. + */ +export class AccessConditionObject { + + /** + * The access condition id + */ + id: string; + + /** + * The access condition name + */ + name: string; + + /** + * Possible start date of the access condition + */ + startDate: string; + + /** + * Possible end date of the access condition + */ + endDate: string; +} diff --git a/src/app/core/submission/models/submission-accesses.resource-type.ts b/src/app/core/submission/models/submission-accesses.resource-type.ts new file mode 100644 index 0000000000..0634a63337 --- /dev/null +++ b/src/app/core/submission/models/submission-accesses.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from '../../shared/resource-type'; + +/** + * The resource type for Accesses section + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SUBMISSION_ACCESSES = new ResourceType('submissionaccesses'); diff --git a/src/app/core/submission/models/submission-item-access-condition.model.ts b/src/app/core/submission/models/submission-item-access-condition.model.ts new file mode 100644 index 0000000000..60e749b786 --- /dev/null +++ b/src/app/core/submission/models/submission-item-access-condition.model.ts @@ -0,0 +1,8 @@ +import { AccessConditionObject } from './access-condition.model'; + +/** + * An interface to represent item's access condition. + */ +export class SubmissionItemAccessConditionObject extends AccessConditionObject { + +} diff --git a/src/app/core/submission/models/submission-upload-file-access-condition.model.ts b/src/app/core/submission/models/submission-upload-file-access-condition.model.ts index fa4d9b9062..7be9d6f1f7 100644 --- a/src/app/core/submission/models/submission-upload-file-access-condition.model.ts +++ b/src/app/core/submission/models/submission-upload-file-access-condition.model.ts @@ -1,25 +1,8 @@ +import { AccessConditionObject } from './access-condition.model'; + /** * An interface to represent bitstream's access condition. */ -export class SubmissionUploadFileAccessConditionObject { +export class SubmissionUploadFileAccessConditionObject extends AccessConditionObject { - /** - * The access condition id - */ - id: string; - - /** - * The access condition name - */ - name: string; - - /** - * Possible start date of the access condition - */ - startDate: string; - - /** - * Possible end date of the access condition - */ - endDate: string; } diff --git a/src/app/core/submission/models/workspaceitem-section-accesses.model.ts b/src/app/core/submission/models/workspaceitem-section-accesses.model.ts new file mode 100644 index 0000000000..2d571f67f8 --- /dev/null +++ b/src/app/core/submission/models/workspaceitem-section-accesses.model.ts @@ -0,0 +1,21 @@ +import { SubmissionItemAccessConditionObject } from './submission-item-access-condition.model'; + +/** + * An interface to represent the submission's item accesses condition. + */ +export interface WorkspaceitemSectionAccessesObject { + /** + * The access condition id + */ + id: string; + + /** + * Boolean that indicates whether the current item must be findable via search or browse. + */ + discoverable: boolean; + + /** + * A list of available item access conditions + */ + accessConditions: SubmissionItemAccessConditionObject[]; +} diff --git a/src/app/core/submission/models/workspaceitem-sections.model.ts b/src/app/core/submission/models/workspaceitem-sections.model.ts index 6ff756a323..084da3f088 100644 --- a/src/app/core/submission/models/workspaceitem-sections.model.ts +++ b/src/app/core/submission/models/workspaceitem-sections.model.ts @@ -1,3 +1,4 @@ +import { WorkspaceitemSectionAccessesObject } from './workspaceitem-section-accesses.model'; import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model'; import { WorkspaceitemSectionLicenseObject } from './workspaceitem-section-license.model'; import { WorkspaceitemSectionUploadObject } from './workspaceitem-section-upload.model'; @@ -19,4 +20,5 @@ export type WorkspaceitemSectionDataType | WorkspaceitemSectionFormObject | WorkspaceitemSectionLicenseObject | WorkspaceitemSectionCcLicenseObject + | WorkspaceitemSectionAccessesObject | string; diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html index c8ee8a2f5b..2c1a34ccae 100644 --- a/src/app/footer/footer.component.html +++ b/src/app/footer/footer.component.html @@ -75,6 +75,10 @@ {{ 'footer.link.end-user-agreement' | translate}}
  • +
  • + {{ 'footer.link.feedback' | translate}} +
diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.html b/src/app/info/feedback/feedback-form/feedback-form.component.html new file mode 100644 index 0000000000..02745f2580 --- /dev/null +++ b/src/app/info/feedback/feedback-form/feedback-form.component.html @@ -0,0 +1,45 @@ +
+
+
+

{{ 'info.feedback.head' | translate }}

+

{{ 'info.feedback.info' | translate }}

+
+
+
+ + + {{ 'info.feedback.email_help' | translate }} +
+
+ + + + + +
+
+ + +
+
+ + + +
+
+ + + {{ 'info.feedback.page_help' | translate }} +
+
+
+
+ +
+
+
+
+
+
diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.scss b/src/app/info/feedback/feedback-form/feedback-form.component.scss new file mode 100644 index 0000000000..41d439e591 --- /dev/null +++ b/src/app/info/feedback/feedback-form/feedback-form.component.scss @@ -0,0 +1,3 @@ +ds-error{ + color:red; +} \ No newline at end of file diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts b/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts new file mode 100644 index 0000000000..d6bedc46cf --- /dev/null +++ b/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts @@ -0,0 +1,97 @@ +import { EPersonMock } from '../../../shared/testing/eperson.mock'; +import { FeedbackDataService } from '../../../core/feedback/feedback-data.service'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { FeedbackFormComponent } from './feedback-form.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { RouteService } from '../../../core/services/route.service'; +import { routeServiceStub } from '../../../shared/testing/route-service.stub'; +import { FormBuilder } from '@angular/forms'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; +import { AuthService } from '../../../core/auth/auth.service'; +import { AuthServiceStub } from '../../../shared/testing/auth-service.stub'; +import { of } from 'rxjs'; +import { Feedback } from '../../../core/feedback/models/feedback.model'; +import { Router } from '@angular/router'; +import { RouterMock } from '../../../shared/mocks/router.mock'; +import { NativeWindowService } from '../../../core/services/window.service'; +import { NativeWindowMockFactory } from '../../../shared/mocks/mock-native-window-ref'; + + +describe('FeedbackFormComponent', () => { + let component: FeedbackFormComponent; + let fixture: ComponentFixture; + let de: DebugElement; + const notificationService = new NotificationsServiceStub(); + const feedbackDataServiceStub = jasmine.createSpyObj('feedbackDataService', { + create: of(new Feedback()) + }); + const authService: AuthServiceStub = Object.assign(new AuthServiceStub(), { + getAuthenticatedUserFromStore: () => { + return of(EPersonMock); + } + }); + const routerStub = new RouterMock(); + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [FeedbackFormComponent], + providers: [ + { provide: RouteService, useValue: routeServiceStub }, + { provide: FormBuilder, useValue: new FormBuilder() }, + { provide: NotificationsService, useValue: notificationService }, + { provide: FeedbackDataService, useValue: feedbackDataServiceStub }, + { provide: AuthService, useValue: authService }, + { provide: NativeWindowService, useFactory: NativeWindowMockFactory }, + { provide: Router, useValue: routerStub }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FeedbackFormComponent); + component = fixture.componentInstance; + de = fixture.debugElement; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have page value', () => { + expect(component.feedbackForm.controls.page.value).toEqual('http://localhost/home'); + }); + + it('should have email if ePerson', () => { + expect(component.feedbackForm.controls.email.value).toEqual('test@test.com'); + }); + + it('should have disabled button', () => { + expect(de.query(By.css('button')).nativeElement.disabled).toBeTrue(); + }); + + describe('when message is inserted', () => { + + beforeEach(() => { + component.feedbackForm.patchValue({ message: 'new feedback' }); + fixture.detectChanges(); + }); + + it('should not have disabled button', () => { + expect(de.query(By.css('button')).nativeElement.disabled).toBeFalse(); + }); + + it('on submit should call createFeedback of feedbackDataServiceStub service', () => { + component.createFeedback(); + fixture.detectChanges(); + expect(feedbackDataServiceStub.create).toHaveBeenCalled(); + }); + }); + + +}); diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.ts b/src/app/info/feedback/feedback-form/feedback-form.component.ts new file mode 100644 index 0000000000..35cdd69cfc --- /dev/null +++ b/src/app/info/feedback/feedback-form/feedback-form.component.ts @@ -0,0 +1,83 @@ +import { RemoteData } from '../../../core/data/remote-data'; +import { NoContent } from '../../../core/shared/NoContent.model'; +import { FeedbackDataService } from '../../../core/feedback/feedback-data.service'; +import { Component, Inject, OnInit } from '@angular/core'; +import { RouteService } from '../../../core/services/route.service'; +import { FormBuilder, Validators } from '@angular/forms'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { AuthService } from '../../../core/auth/auth.service'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { Router } from '@angular/router'; +import { getHomePageRoute } from '../../../app-routing-paths'; +import { take } from 'rxjs/operators'; +import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service'; +import { URLCombiner } from '../../../core/url-combiner/url-combiner'; + +@Component({ + selector: 'ds-feedback-form', + templateUrl: './feedback-form.component.html', + styleUrls: ['./feedback-form.component.scss'] +}) +/** + * Component displaying the contents of the Feedback Statement + */ +export class FeedbackFormComponent implements OnInit { + + /** + * Form builder created used from the feedback from + */ + feedbackForm = this.fb.group({ + email: ['', [Validators.required, Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$')]], + message: ['', Validators.required], + page: [''], + }); + + constructor( + @Inject(NativeWindowService) protected _window: NativeWindowRef, + public routeService: RouteService, + private fb: FormBuilder, + protected notificationsService: NotificationsService, + protected translate: TranslateService, + private feedbackDataService: FeedbackDataService, + private authService: AuthService, + private router: Router) { + } + + /** + * On init check if user is logged in and use its email if so + */ + ngOnInit() { + + this.authService.getAuthenticatedUserFromStore().pipe(take(1)).subscribe((user: EPerson) => { + if (!!user) { + this.feedbackForm.patchValue({ email: user.email }); + } + }); + + this.routeService.getPreviousUrl().pipe(take(1)).subscribe((url: string) => { + if (!url) { + url = getHomePageRoute(); + } + const relatedUrl = new URLCombiner(this._window.nativeWindow.origin, url).toString(); + this.feedbackForm.patchValue({ page: relatedUrl }); + }); + + } + + /** + * Function to create the feedback from form values + */ + createFeedback(): void { + const url = this.feedbackForm.value.page.replace(this._window.nativeWindow.origin, ''); + this.feedbackDataService.create(this.feedbackForm.value).pipe(getFirstCompletedRemoteData()).subscribe((response: RemoteData) => { + if (response.isSuccess) { + this.notificationsService.success(this.translate.instant('info.feedback.create.success')); + this.feedbackForm.reset(); + this.router.navigateByUrl(url); + } + }); + } + +} diff --git a/src/app/info/feedback/feedback.component.html b/src/app/info/feedback/feedback.component.html new file mode 100644 index 0000000000..210bdcf1d7 --- /dev/null +++ b/src/app/info/feedback/feedback.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/src/app/info/feedback/feedback.component.scss b/src/app/info/feedback/feedback.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/info/feedback/feedback.component.spec.ts b/src/app/info/feedback/feedback.component.spec.ts new file mode 100644 index 0000000000..810c3b703f --- /dev/null +++ b/src/app/info/feedback/feedback.component.spec.ts @@ -0,0 +1,27 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { FeedbackComponent } from './feedback.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; + +describe('FeedbackComponent', () => { + let component: FeedbackComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [FeedbackComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FeedbackComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/info/feedback/feedback.component.ts b/src/app/info/feedback/feedback.component.ts new file mode 100644 index 0000000000..3c0924b4de --- /dev/null +++ b/src/app/info/feedback/feedback.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-feedback', + templateUrl: './feedback.component.html', + styleUrls: ['./feedback.component.scss'] +}) +/** + * Component displaying the Feedback Statement + */ +export class FeedbackComponent { +} diff --git a/src/app/info/feedback/themed-feedback.component.ts b/src/app/info/feedback/themed-feedback.component.ts new file mode 100644 index 0000000000..68581c32fd --- /dev/null +++ b/src/app/info/feedback/themed-feedback.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { ThemedComponent } from '../../shared/theme-support/themed.component'; +import { FeedbackComponent } from './feedback.component'; + +/** + * Themed wrapper for FeedbackComponent + */ +@Component({ + selector: 'ds-themed-feedback', + styleUrls: [], + templateUrl: '../../shared/theme-support/themed.component.html', +}) +export class ThemedFeedbackComponent extends ThemedComponent { + protected getComponentName(): string { + return 'FeedbackComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/info/feedback/feedback.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./feedback.component`); + } + +} diff --git a/src/app/info/info-routing-paths.ts b/src/app/info/info-routing-paths.ts index 8ec6dbcb8d..a18de2c611 100644 --- a/src/app/info/info-routing-paths.ts +++ b/src/app/info/info-routing-paths.ts @@ -2,6 +2,7 @@ import { getInfoModulePath } from '../app-routing-paths'; export const END_USER_AGREEMENT_PATH = 'end-user-agreement'; export const PRIVACY_PATH = 'privacy'; +export const FEEDBACK_PATH = 'feedback'; export function getEndUserAgreementPath() { return getSubPath(END_USER_AGREEMENT_PATH); @@ -11,6 +12,10 @@ export function getPrivacyPath() { return getSubPath(PRIVACY_PATH); } +export function getFeedbackPath() { + return getSubPath(FEEDBACK_PATH); +} + function getSubPath(path: string) { return `${getInfoModulePath()}/${path}`; } diff --git a/src/app/info/info-routing.module.ts b/src/app/info/info-routing.module.ts index f76fb47ff0..c746bebaaa 100644 --- a/src/app/info/info-routing.module.ts +++ b/src/app/info/info-routing.module.ts @@ -1,9 +1,12 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { PRIVACY_PATH, END_USER_AGREEMENT_PATH } from './info-routing-paths'; +import { PRIVACY_PATH, END_USER_AGREEMENT_PATH, FEEDBACK_PATH } from './info-routing-paths'; import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end-user-agreement.component'; import { ThemedPrivacyComponent } from './privacy/themed-privacy.component'; +import { ThemedFeedbackComponent } from './feedback/themed-feedback.component'; +import { FeedbackGuard } from '../core/feedback/feedback.guard'; + @NgModule({ imports: [ @@ -22,6 +25,15 @@ import { ThemedPrivacyComponent } from './privacy/themed-privacy.component'; resolve: { breadcrumb: I18nBreadcrumbResolver }, data: { title: 'info.privacy.title', breadcrumbKey: 'info.privacy' } } + ]), + RouterModule.forChild([ + { + path: FEEDBACK_PATH, + component: ThemedFeedbackComponent, + resolve: { breadcrumb: I18nBreadcrumbResolver }, + data: { title: 'info.feedback.title', breadcrumbKey: 'info.feedback' }, + canActivate: [FeedbackGuard] + } ]) ] }) diff --git a/src/app/info/info.module.ts b/src/app/info/info.module.ts index 84ba5a62db..61eee71f3a 100644 --- a/src/app/info/info.module.ts +++ b/src/app/info/info.module.ts @@ -8,6 +8,11 @@ import { PrivacyComponent } from './privacy/privacy.component'; import { PrivacyContentComponent } from './privacy/privacy-content/privacy-content.component'; import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end-user-agreement.component'; import { ThemedPrivacyComponent } from './privacy/themed-privacy.component'; +import { FeedbackComponent } from './feedback/feedback.component'; +import { FeedbackFormComponent } from './feedback/feedback-form/feedback-form.component'; +import { ThemedFeedbackComponent } from './feedback/themed-feedback.component'; +import { FeedbackGuard } from '../core/feedback/feedback.guard'; + const DECLARATIONS = [ EndUserAgreementComponent, @@ -15,21 +20,25 @@ const DECLARATIONS = [ EndUserAgreementContentComponent, PrivacyComponent, PrivacyContentComponent, - ThemedPrivacyComponent + ThemedPrivacyComponent, + FeedbackComponent, + FeedbackFormComponent, + ThemedFeedbackComponent ]; @NgModule({ imports: [ CommonModule, SharedModule, - InfoRoutingModule + InfoRoutingModule, ], declarations: [ ...DECLARATIONS ], exports: [ ...DECLARATIONS - ] + ], + providers: [FeedbackGuard] }) export class InfoModule { } diff --git a/src/app/item-page/edit-item-page/item-delete/item-delete.component.spec.ts b/src/app/item-page/edit-item-page/item-delete/item-delete.component.spec.ts index ea78767df5..2533de32b2 100644 --- a/src/app/item-page/edit-item-page/item-delete/item-delete.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-delete/item-delete.component.spec.ts @@ -3,7 +3,7 @@ import { ItemType } from '../../../core/shared/item-relationships/item-type.mode import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; import { Item } from '../../../core/shared/item.model'; import { RouterStub } from '../../../shared/testing/router.stub'; -import { of as observableOf } from 'rxjs'; +import { of as observableOf, EMPTY } from 'rxjs'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; @@ -24,6 +24,8 @@ import { RelationshipType } from '../../../core/shared/item-relationships/relati import { EntityTypeService } from '../../../core/data/entity-type.service'; import { getItemEditRoute } from '../../item-page-routing-paths'; import { createPaginatedList } from '../../../shared/testing/utils.test'; +import { RelationshipTypeService } from '../../../core/data/relationship-type.service'; +import { LinkService } from '../../../core/cache/builders/link.service'; let comp: ItemDeleteComponent; let fixture: ComponentFixture; @@ -40,6 +42,7 @@ let mockItemDataService: ItemDataService; let routeStub; let objectUpdatesServiceStub; let relationshipService; +let linkService; let entityTypeService; let notificationsServiceStub; let typesSelection; @@ -52,7 +55,12 @@ describe('ItemDeleteComponent', () => { uuid: 'fake-uuid', handle: 'fake/handle', lastModified: '2018', - isWithdrawn: true + isWithdrawn: true, + metadata: { + 'dspace.entity.type': [ + { value: 'Person' } + ] + } }); itemType = Object.assign(new ItemType(), { @@ -129,6 +137,12 @@ describe('ItemDeleteComponent', () => { } ); + linkService = jasmine.createSpyObj('linkService', + { + resolveLinks: relationships[0], + } + ); + notificationsServiceStub = new NotificationsServiceStub(); TestBed.configureTestingModule({ @@ -142,6 +156,8 @@ describe('ItemDeleteComponent', () => { { provide: ObjectUpdatesService, useValue: objectUpdatesServiceStub }, { provide: RelationshipService, useValue: relationshipService }, { provide: EntityTypeService, useValue: entityTypeService }, + { provide: RelationshipTypeService, useValue: {} }, + { provide: LinkService, useValue: linkService }, ], schemas: [ CUSTOM_ELEMENTS_SCHEMA ] @@ -166,25 +182,45 @@ describe('ItemDeleteComponent', () => { }); describe('performAction', () => { - it('should call delete function from the ItemDataService', () => { - spyOn(comp, 'notify'); - comp.performAction(); - expect(mockItemDataService.delete) - .toHaveBeenCalledWith(mockItem.id, types.filter((type) => typesSelection[type]).map((type) => type.id)); - expect(comp.notify).toHaveBeenCalled(); + describe(`when there are entitytypes`, () => { + it('should call delete function from the ItemDataService', () => { + spyOn(comp, 'notify'); + comp.performAction(); + expect(mockItemDataService.delete) + .toHaveBeenCalledWith(mockItem.id, types.filter((type) => typesSelection[type]).map((type) => type.id)); + expect(comp.notify).toHaveBeenCalled(); + }); + + it('should call delete function from the ItemDataService with empty types', () => { + + spyOn(comp, 'notify'); + jasmine.getEnv().allowRespy(true); + spyOn(entityTypeService, 'getEntityTypeRelationships').and.returnValue([]); + comp.ngOnInit(); + + comp.performAction(); + + expect(mockItemDataService.delete).toHaveBeenCalledWith(mockItem.id, []); + expect(comp.notify).toHaveBeenCalled(); + }); }); - it('should call delete function from the ItemDataService with empty types', () => { + describe(`when there are no entity types`, () => { + beforeEach(() => { + (comp as any).entityTypeService = jasmine.createSpyObj('entityTypeService', + { + getEntityTypeByLabel: EMPTY, + } + ); + }); - spyOn(comp, 'notify'); - jasmine.getEnv().allowRespy(true); - spyOn(entityTypeService, 'getEntityTypeRelationships').and.returnValue([]); - comp.ngOnInit(); - - comp.performAction(); - - expect(mockItemDataService.delete).toHaveBeenCalledWith(mockItem.id, []); - expect(comp.notify).toHaveBeenCalled(); + it('should call delete function from the ItemDataService', () => { + spyOn(comp, 'notify'); + comp.performAction(); + expect(mockItemDataService.delete) + .toHaveBeenCalledWith(mockItem.id, types.filter((type) => typesSelection[type]).map((type) => type.id)); + expect(comp.notify).toHaveBeenCalled(); + }); }); }); describe('notify', () => { diff --git a/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts b/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts index 0249422c8e..7735fae0ea 100644 --- a/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts +++ b/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts @@ -1,12 +1,14 @@ -import { Component, Input, OnInit } from '@angular/core'; -import {defaultIfEmpty, filter, map, switchMap, take} from 'rxjs/operators'; -import { AbstractSimpleItemActionComponent } from '../simple-item-action/abstract-simple-item-action.component'; +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { defaultIfEmpty, filter, map, switchMap, take } from 'rxjs/operators'; +import { + AbstractSimpleItemActionComponent +} from '../simple-item-action/abstract-simple-item-action.component'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { combineLatest as observableCombineLatest, combineLatest, Observable, - of as observableOf + of as observableOf, Subscription } from 'rxjs'; import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model'; import { VirtualMetadata } from '../virtual-metadata/virtual-metadata.component'; @@ -32,6 +34,7 @@ import { followLink } from '../../../shared/utils/follow-link-config.model'; import { getItemEditRoute } from '../../item-page-routing-paths'; import { RemoteData } from '../../../core/data/remote-data'; import { NoContent } from '../../../core/shared/NoContent.model'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; @Component({ selector: 'ds-item-delete', @@ -42,7 +45,7 @@ import { NoContent } from '../../../core/shared/NoContent.model'; */ export class ItemDeleteComponent extends AbstractSimpleItemActionComponent - implements OnInit { + implements OnInit, OnDestroy { /** * The current url of this page @@ -60,7 +63,7 @@ export class ItemDeleteComponent * A list of the relationship types for which this item has relations as an observable. * The list doesn't contain duplicates. */ - types$: Observable; + types$: BehaviorSubject = new BehaviorSubject([]); /** * A map which stores the relationships of this item for each type as observable lists @@ -84,6 +87,11 @@ export class ItemDeleteComponent */ public modalRef: NgbModalRef; + /** + * Array to track all subscriptions and unsubscribe them onDestroy + */ + private subs: Subscription[] = []; + constructor(protected route: ActivatedRoute, protected router: Router, protected notificationsService: NotificationsService, @@ -113,8 +121,8 @@ export class ItemDeleteComponent this.url = this.router.url; const label = this.item.firstMetadataValue('dspace.entity.type'); - if (label !== undefined) { - this.types$ = this.entityTypeService.getEntityTypeByLabel(label).pipe( + if (isNotEmpty(label)) { + this.subs.push(this.entityTypeService.getEntityTypeByLabel(label).pipe( getFirstSucceededRemoteData(), getRemoteDataPayload(), switchMap((entityType) => this.entityTypeService.getEntityTypeRelationships(entityType.id)), @@ -138,16 +146,14 @@ export class ItemDeleteComponent ), ); }) - ); - } else { - this.types$ = observableOf([]); + ).subscribe((types: RelationshipType[]) => this.types$.next(types))); } - this.types$.pipe( + this.subs.push(this.types$.pipe( take(1), ).subscribe((types) => this.objectUpdatesService.initialize(this.url, types, this.item.lastModified) - ); + )); } /** @@ -327,7 +333,7 @@ export class ItemDeleteComponent */ performAction() { - this.types$.pipe( + this.subs.push(this.types$.pipe( switchMap((types) => combineLatest( types.map((type) => this.isSelected(type)) @@ -339,13 +345,14 @@ export class ItemDeleteComponent map((selectedTypes) => selectedTypes.map((type) => type.id)), ) ), - ).subscribe((types) => { - this.itemDataService.delete(this.item.id, types).pipe(getFirstCompletedRemoteData()).subscribe( - (rd: RemoteData) => { - this.notify(rd.hasSucceeded); - } - ); - }); + switchMap((types) => + this.itemDataService.delete(this.item.id, types).pipe(getFirstCompletedRemoteData()) + ) + ).subscribe( + (rd: RemoteData) => { + this.notify(rd.hasSucceeded); + } + )); } /** @@ -361,4 +368,14 @@ export class ItemDeleteComponent this.router.navigate([getItemEditRoute(this.item)]); } } + + /** + * Unsubscribe from all subscriptions + */ + ngOnDestroy(): void { + this.subs + .filter((sub) => hasValue(sub)) + .forEach((sub) => sub.unsubscribe()); + } + } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html index fc115e043a..55e354ea7a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html @@ -14,7 +14,8 @@
-
-
- +
+
@@ -24,7 +26,7 @@

{{'submission.general.discard.confirm.info' | translate}}

diff --git a/src/app/shared/number-picker/number-picker.component.html b/src/app/shared/number-picker/number-picker.component.html index 58b6def50b..9b2ef98925 100644 --- a/src/app/shared/number-picker/number-picker.component.html +++ b/src/app/shared/number-picker/number-picker.component.html @@ -9,6 +9,7 @@ Increment
From 8c865b758eba2847056154a3bb20fb38a5bb2523 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 9 Feb 2022 15:07:08 -0600 Subject: [PATCH 065/570] Fix bugs / stability issues with search e2e tests --- cypress/integration/search-navbar.spec.ts | 6 +++--- cypress/integration/search-page.spec.ts | 18 +++++++----------- .../object-grid/object-grid.component.html | 2 +- .../object-list/object-list.component.html | 2 +- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/cypress/integration/search-navbar.spec.ts b/cypress/integration/search-navbar.spec.ts index 3aa8e7248d..211b4836b1 100644 --- a/cypress/integration/search-navbar.spec.ts +++ b/cypress/integration/search-navbar.spec.ts @@ -31,7 +31,7 @@ describe('Search from Navigation Bar', () => { // Wait for search results to come back from the above GET command cy.wait('@search-results'); // At least one search result should be displayed - cy.get('ds-item-search-result-list-element').should('be.visible'); + cy.get('[data-e2e="list-object"]').should('be.visible'); }); it('should go to search page with correct query if submitted (from search)', () => { @@ -46,7 +46,7 @@ describe('Search from Navigation Bar', () => { // Wait for search results to come back from the above GET command cy.wait('@search-results'); // At least one search result should be displayed - cy.get('ds-item-search-result-list-element').should('be.visible'); + cy.get('[data-e2e="list-object"]').should('be.visible'); }); it('should allow user to also submit query by clicking icon', () => { @@ -61,6 +61,6 @@ describe('Search from Navigation Bar', () => { // Wait for search results to come back from the above GET command cy.wait('@search-results'); // At least one search result should be displayed - cy.get('ds-item-search-result-list-element').should('be.visible'); + cy.get('[data-e2e="list-object"]').should('be.visible'); }); }); diff --git a/cypress/integration/search-page.spec.ts b/cypress/integration/search-page.spec.ts index cbd518b97c..981c9365aa 100644 --- a/cypress/integration/search-page.spec.ts +++ b/cypress/integration/search-page.spec.ts @@ -1,13 +1,8 @@ import { Options } from 'cypress-axe'; +import { TEST_SEARCH_TERM } from 'cypress/support'; import { testA11y } from 'cypress/support/utils'; describe('Search Page', () => { - it('should contain query value when navigating to page with query parameter', () => { - const queryString = 'test query'; - cy.visit('/search?query=' + queryString); - cy.get('[data-e2e="search-box"]').should('have.value', queryString); - }); - it('should redirect to the correct url when query was set and submit button was triggered', () => { const queryString = 'Another interesting query string'; cy.visit('/search'); @@ -18,13 +13,14 @@ describe('Search Page', () => { }); it('should load results and pass accessibility tests', () => { - cy.visit('/search'); + cy.visit('/search?query=' + TEST_SEARCH_TERM); + cy.get('[data-e2e="search-box"]').should('have.value', TEST_SEARCH_TERM); // tag must be loaded cy.get('ds-search-page').should('exist'); // At least one search result should be displayed - cy.get('ds-item-search-result-list-element').should('be.visible'); + cy.get('[data-e2e="list-object"]').should('be.visible'); // Click each filter toggle to open *every* filter // (As we want to scan filter section for accessibility issues as well) @@ -49,7 +45,7 @@ describe('Search Page', () => { }); it('should have a working grid view that passes accessibility tests', () => { - cy.visit('/search'); + cy.visit('/search?query=' + TEST_SEARCH_TERM); // Click button in sidebar to display grid view cy.get('ds-search-sidebar [data-e2e="grid-view"]').click(); @@ -57,8 +53,8 @@ describe('Search Page', () => { // tag must be loaded cy.get('ds-search-page').should('exist'); - // At least one grid element (card) should be displayed - cy.get('ds-item-search-result-grid-element').should('be.visible'); + // At least one grid object (card) should be displayed + cy.get('[data-e2e="grid-object"]').should('be.visible'); // Analyze for accessibility issues testA11y('ds-search-page', diff --git a/src/app/shared/object-grid/object-grid.component.html b/src/app/shared/object-grid/object-grid.component.html index 0afd623d86..296f190d2d 100644 --- a/src/app/shared/object-grid/object-grid.component.html +++ b/src/app/shared/object-grid/object-grid.component.html @@ -13,7 +13,7 @@ (paginationChange)="onPaginationChange($event)">
-
+
diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html index 331ff1cb28..53a62ca1f5 100644 --- a/src/app/shared/object-list/object-list.component.html +++ b/src/app/shared/object-list/object-list.component.html @@ -12,7 +12,7 @@ (sortFieldChange)="onSortFieldChange($event)" (paginationChange)="onPaginationChange($event)">
    -
  • +
  • Date: Mon, 14 Feb 2022 11:28:08 -0600 Subject: [PATCH 066/570] Update submission tests to use Demo Submitter. Switch Docker scripts to use updated `dspace7-entities-data.sql` which now includes demo in-progress submissions --- cypress.json | 4 +++- cypress/integration/my-dspace.spec.ts | 22 ++++++++++++++-------- cypress/integration/submission.spec.ts | 10 +++++----- cypress/support/index.ts | 2 ++ docker/cli.assetstore.yml | 2 +- docker/db.entities.yml | 2 +- docker/docker-compose-ci.yml | 2 +- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/cypress.json b/cypress.json index fe74ce3ff9..c0b5a56a15 100644 --- a/cypress.json +++ b/cypress.json @@ -19,6 +19,8 @@ "DSPACE_TEST_ENTITY_PUBLICATION": "e98b0f27-5c19-49a0-960d-eb6ad5287067", "DSPACE_TEST_SEARCH_TERM": "test", "DSPACE_TEST_SUBMIT_COLLECTION_NAME": "Sample Collection", - "DSPACE_TEST_SUBMIT_COLLECTION_UUID": "9d8334e9-25d3-4a67-9cea-3dffdef80144" + "DSPACE_TEST_SUBMIT_COLLECTION_UUID": "9d8334e9-25d3-4a67-9cea-3dffdef80144", + "DSPACE_TEST_SUBMIT_USER": "dspacedemo+submit@gmail.com", + "DSPACE_TEST_SUBMIT_USER_PASSWORD": "dspace" } } \ No newline at end of file diff --git a/cypress/integration/my-dspace.spec.ts b/cypress/integration/my-dspace.spec.ts index 6222d30cfb..da78ff8da6 100644 --- a/cypress/integration/my-dspace.spec.ts +++ b/cypress/integration/my-dspace.spec.ts @@ -1,17 +1,17 @@ import { Options } from 'cypress-axe'; -import { TEST_ADMIN_USER, TEST_ADMIN_PASSWORD, TEST_SUBMIT_COLLECTION_NAME } from 'cypress/support'; +import { TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD, TEST_SUBMIT_COLLECTION_NAME } from 'cypress/support'; import { testA11y } from 'cypress/support/utils'; describe('My DSpace page', () => { it('should display recent submissions and pass accessibility tests', () => { - cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); + cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.visit('/mydspace'); cy.get('ds-my-dspace-page').should('exist'); // At least one recent submission should be displayed - cy.get('ds-item-search-result-list-element-submission').should('be.visible'); + cy.get('[data-e2e="list-object"]').should('be.visible'); // Click each filter toggle to open *every* filter // (As we want to scan filter section for accessibility issues as well) @@ -36,7 +36,7 @@ describe('My DSpace page', () => { }); it('should have a working detailed view that passes accessibility tests', () => { - cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); + cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.visit('/mydspace'); @@ -61,7 +61,7 @@ describe('My DSpace page', () => { // NOTE: Deleting existing submissions is exercised by submission.spec.ts it('should let you start a new submission & edit in-progress submissions', () => { - cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); + cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.visit('/mydspace'); // Open the New Submission dropdown @@ -97,8 +97,14 @@ describe('My DSpace page', () => { // Part 2 will be the [id] of the submission const id = subpaths[2]; - // Go back to the MyDSpace page - cy.visit('/mydspace'); + // Click the "Save for Later" button to save this submission + cy.get('button#saveForLater').click(); + + // "Save for Later" should send us to MyDSpace + cy.url().should('include', '/mydspace'); + + // Close any open notifications, to make sure they don't get in the way of next steps + cy.get('[data-dismiss="alert"]').click({multiple: true}); // This is the GET command that will actually run the search cy.intercept('GET', '/server/api/discover/search/objects*').as('search-results'); @@ -125,7 +131,7 @@ describe('My DSpace page', () => { }); it('should let you import from external sources', () => { - cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); + cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.visit('/mydspace'); // Open the New Import dropdown diff --git a/cypress/integration/submission.spec.ts b/cypress/integration/submission.spec.ts index 9469ee35e1..4c7758f8d6 100644 --- a/cypress/integration/submission.spec.ts +++ b/cypress/integration/submission.spec.ts @@ -1,12 +1,12 @@ import { Options } from 'cypress-axe'; -import { TEST_ADMIN_USER, TEST_ADMIN_PASSWORD, TEST_SUBMIT_COLLECTION_NAME, TEST_SUBMIT_COLLECTION_UUID } from 'cypress/support'; +import { TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD, TEST_SUBMIT_COLLECTION_NAME, TEST_SUBMIT_COLLECTION_UUID } from 'cypress/support'; import { testA11y } from 'cypress/support/utils'; describe('New Submission page', () => { // NOTE: We already test that new submissions can be started from MyDSpace in my-dspace.spec.ts - it('should create a new submission when using /submit path', () => { - cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); + it('should create a new submission when using /submit path & pass accessibility', () => { + cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); // Test that calling /submit with collection & entityType will create a new submission cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none'); @@ -33,7 +33,7 @@ describe('New Submission page', () => { }); it('should block submission & show errors if required fields are missing', () => { - cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); + cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); // Create a new submission cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none'); @@ -91,7 +91,7 @@ describe('New Submission page', () => { }); it('should allow for deposit if all required fields completed & file uploaded', () => { - cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); + cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); // Create a new submission cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none'); diff --git a/cypress/support/index.ts b/cypress/support/index.ts index a56ace7d38..8edfdedde3 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -54,3 +54,5 @@ export const TEST_SEARCH_TERM = Cypress.env('DSPACE_TEST_SEARCH_TERM') || 'test' // Collection used for submission tests export const TEST_SUBMIT_COLLECTION_NAME = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME') || 'Sample Collection'; export const TEST_SUBMIT_COLLECTION_UUID = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_UUID') || '9d8334e9-25d3-4a67-9cea-3dffdef80144'; +export const TEST_SUBMIT_USER = Cypress.env('DSPACE_TEST_SUBMIT_USER') || 'dspacedemo+submit@gmail.com'; +export const TEST_SUBMIT_USER_PASSWORD = Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD') || 'dspace'; diff --git a/docker/cli.assetstore.yml b/docker/cli.assetstore.yml index c2846286d7..40e4974c7c 100644 --- a/docker/cli.assetstore.yml +++ b/docker/cli.assetstore.yml @@ -35,6 +35,6 @@ services: tar xvfz /tmp/assetstore.tar.gz fi - /dspace/bin/dspace index-discovery + /dspace/bin/dspace index-discovery -b /dspace/bin/dspace oai import /dspace/bin/dspace oai clean-cache diff --git a/docker/db.entities.yml b/docker/db.entities.yml index 818d14877c..d1dfdf4a26 100644 --- a/docker/db.entities.yml +++ b/docker/db.entities.yml @@ -20,7 +20,7 @@ services: environment: # This LOADSQL should be kept in sync with the URL in DSpace/DSpace # This SQL is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data - - LOADSQL=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-2021-04-14.sql + - LOADSQL=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-data.sql dspace: ### OVERRIDE default 'entrypoint' in 'docker-compose-rest.yml' #### # Ensure that the database is ready BEFORE starting tomcat diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml index a895314a17..3bd8f52630 100644 --- a/docker/docker-compose-ci.yml +++ b/docker/docker-compose-ci.yml @@ -63,7 +63,7 @@ services: # This LOADSQL should be kept in sync with the LOADSQL in # https://github.com/DSpace/DSpace/blob/main/dspace/src/main/docker-compose/db.entities.yml # This SQL is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data - LOADSQL: https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-2021-04-14.sql + LOADSQL: https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-data.sql PGDATA: /pgdata image: dspace/dspace-postgres-pgcrypto:loadsql networks: From 852da27c2c9e0ac8bebe0a4310d54e55bd934020 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 4 Mar 2022 12:49:55 -0600 Subject: [PATCH 067/570] Update Cypress to read REST API URL dynamically from config.json --- cypress.json | 1 - cypress/support/commands.ts | 69 +++++++++++++++++++++++-------------- cypress/support/index.ts | 7 +++- 3 files changed, 50 insertions(+), 27 deletions(-) diff --git a/cypress.json b/cypress.json index c0b5a56a15..80358eb6dd 100644 --- a/cypress.json +++ b/cypress.json @@ -11,7 +11,6 @@ "openMode": 0 }, "env": { - "DSPACE_TEST_REST_BASE_URL": "http://localhost:8080", "DSPACE_TEST_ADMIN_USER": "dspacedemo+admin@gmail.com", "DSPACE_TEST_ADMIN_PASSWORD": "dspace", "DSPACE_TEST_COMMUNITY": "0958c910-2037-42a9-81c7-dca80e3892b4", diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index dcac5aaaac..d66e1ef0bd 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -4,7 +4,7 @@ // *********************************************** import { AuthTokenInfo, TOKENITEM } from 'src/app/core/auth/models/auth-token-info.model'; -import { TEST_REST_BASE_URL } from '.'; +import { FALLBACK_TEST_REST_BASE_URL } from '.'; // Declare Cypress namespace to help with Intellisense & code completion in IDEs // ALL custom commands MUST be listed here for code completion to work @@ -30,34 +30,53 @@ declare global { * @param password password to login as */ function login(email: string, password: string): void { - // To login via REST, first we have to do a GET to obtain a valid CSRF token - cy.request( TEST_REST_BASE_URL + '/server/api/authn/status' ) - .then((response) => { - // We should receive a CSRF token returned in a response header - expect(response.headers).to.have.property('dspace-xsrf-token'); - const csrfToken = response.headers['dspace-xsrf-token']; + // Cypress doesn't have access to the running application in Node.js. + // So, it's not possible to inject or load the AppConfig or environment of the Angular UI. + // Instead, we'll read our running application's config.json, which contains the configs & + // is regenerated at runtime each time the Angular UI application starts up. + cy.readFile('./src/assets/config.json').then((str) => { + // Parse JSON file into a JSON object + const config = JSON.parse(JSON.stringify(str)); - // Now, send login POST request including that CSRF token - cy.request({ - method: 'POST', - url: TEST_REST_BASE_URL + '/server/api/authn/login', - headers: { 'X-XSRF-TOKEN' : csrfToken}, - form: true, // indicates the body should be form urlencoded - body: { user: email, password: password } - }).then((resp) => { - // We expect a successful login - expect(resp.status).to.eq(200); - // We expect to have a valid authorization header returned (with our auth token) - expect(resp.headers).to.have.property('authorization'); + // Find the URL of our REST API. Have a fallback ready, just in case 'rest.baseUrl' cannot be found. + let baseRestUrl = FALLBACK_TEST_REST_BASE_URL; + if (!config.rest.baseUrl) { + console.warn("Could not load 'rest.baseUrl' from config.json. Falling back to " + FALLBACK_TEST_REST_BASE_URL); + } else { + console.log("Found 'rest.baseUrl' in config.json. Using this REST API for login: " + config.rest.baseUrl); + baseRestUrl = config.rest.baseUrl; + } - // Initialize our AuthTokenInfo object from the authorization header. - const authheader = resp.headers.authorization as string; - const authinfo: AuthTokenInfo = new AuthTokenInfo(authheader); + // To login via REST, first we have to do a GET to obtain a valid CSRF token + cy.request( baseRestUrl + '/api/authn/status' ) + .then((response) => { + // We should receive a CSRF token returned in a response header + expect(response.headers).to.have.property('dspace-xsrf-token'); + const csrfToken = response.headers['dspace-xsrf-token']; - // Save our AuthTokenInfo object to our dsAuthInfo UI cookie - // This ensures the UI will recognize we are logged in on next "visit()" - cy.setCookie(TOKENITEM, JSON.stringify(authinfo)); + // Now, send login POST request including that CSRF token + cy.request({ + method: 'POST', + url: baseRestUrl + '/api/authn/login', + headers: { 'X-XSRF-TOKEN' : csrfToken}, + form: true, // indicates the body should be form urlencoded + body: { user: email, password: password } + }).then((resp) => { + // We expect a successful login + expect(resp.status).to.eq(200); + // We expect to have a valid authorization header returned (with our auth token) + expect(resp.headers).to.have.property('authorization'); + + // Initialize our AuthTokenInfo object from the authorization header. + const authheader = resp.headers.authorization as string; + const authinfo: AuthTokenInfo = new AuthTokenInfo(authheader); + + // Save our AuthTokenInfo object to our dsAuthInfo UI cookie + // This ensures the UI will recognize we are logged in on next "visit()" + cy.setCookie(TOKENITEM, JSON.stringify(authinfo)); + }); }); + }); } // Add as a Cypress command (i.e. assign to 'cy.login') diff --git a/cypress/support/index.ts b/cypress/support/index.ts index 8edfdedde3..d9b6409a0d 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -42,7 +42,12 @@ afterEach(() => { // Default values listed here are all valid for the Demo Entities Data set available at // https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data // (This is the data set used in our CI environment) -export const TEST_REST_BASE_URL = Cypress.env('DSPACE_TEST_REST_BASE_URL') || 'http://localhost:8080'; + +// NOTE: FALLBACK_TEST_REST_BASE_URL is only used if Cypress cannot read the REST API BaseURL +// from the Angular UI's config.json. See 'getBaseRESTUrl()' in commands.ts +export const FALLBACK_TEST_REST_BASE_URL = 'http://localhost:8080/server'; + +// Admin account used for administrative tests export const TEST_ADMIN_USER = Cypress.env('DSPACE_TEST_ADMIN_USER') || 'dspacedemo+admin@gmail.com'; export const TEST_ADMIN_PASSWORD = Cypress.env('DSPACE_TEST_ADMIN_PASSWORD') || 'dspace'; // Community/collection/publication used for view/edit tests From a0e2ac3d122bab4bb3dd092aa73195db154ca930 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 4 Mar 2022 12:50:21 -0600 Subject: [PATCH 068/570] Update README with clearer instructions on running 'ng e2e' --- README.md | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 74010f3c5c..d9c911d30a 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ After you have installed all dependencies you can now run the app. Run `yarn run When building for production we're using Ahead of Time (AoT) compilation. With AoT, the browser downloads a pre-compiled version of the application, so it can render the application immediately, without waiting to compile the app first. The compiler is roughly half the size of Angular itself, so omitting it dramatically reduces the application payload. -To build the app for production and start the server run: +To build the app for production and start the server (in one command) run: ```bash yarn start @@ -207,6 +207,10 @@ yarn run build:prod ``` This will build the application and put the result in the `dist` folder. You can copy this folder to wherever you need it for your application server. If you will be using the built-in Express server, you'll also need a copy of the `node_modules` folder tucked inside your copy of `dist`. +After building the app for production, it can be started by running: +```bash +yarn run serve:ssr +``` ### Running the application with Docker NOTE: At this time, we do not have production-ready Docker images for DSpace. @@ -268,11 +272,29 @@ E2E tests (aka integration tests) use [Cypress.io](https://www.cypress.io/). Con The test files can be found in the `./cypress/integration/` folder. -Before you can run e2e tests, two things are required: -1. You MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `config.prod.yml` or `config.yml`. You may override this using env variables, see [Configuring](#configuring). -2. Your backend MUST include our Entities Test Data set. Some tests run against a (currently hardcoded) Community/Collection/Item UUID. These UUIDs are all valid for our Entities Test Data set. The Entities Test Data set may be installed easily via Docker, see https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker-compose#ingest-option-2-ingest-entities-test-data +Before you can run e2e tests, two things are REQUIRED: +1. You MUST be running the DSpace backend (i.e. REST API) locally. The e2e tests will *NOT* succeed if run against our demo REST API (https://api7.dspace.org/server/), as that server is uncontrolled and may have content added/removed at any time. + * After starting up your backend on localhost, make sure either your `config.prod.yml` or `config.dev.yml` has its `rest` settings defined to use that localhost backend. + * If you'd prefer, you may instead use environment variables as described at [Configuring](#configuring). For example: + ``` + DSPACE_REST_SSL = false + DSPACE_REST_HOST = localhost + DSPACE_REST_PORT = 8080 + ``` +2. Your backend MUST include our [Entities Test Data set](https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data). Some tests run against a specific Community/Collection/Item UUID. These UUIDs are all valid for our Entities Test Data set. + * (Recommended) The Entities Test Data set may be installed easily via Docker, see https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker-compose#ingest-option-2-ingest-entities-test-data + * Alternatively, the Entities Test Data set may be installed via a simple SQL import (e. g. `psql -U dspace < dspace7-entities-data.sql`). See instructions in link above. -Run `ng e2e` to kick off the tests. This will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results. +After performing the above setup, you can run the e2e tests using +``` +ng e2e +```` +NOTE: By default these tests will run against the REST API backend configured via environment variables or in `config.prod.yml`. If you'd rather it use `config.dev.yml`, just set the NODE_ENV environment variable like this: +``` +NODE_ENV=development ng e2e +``` + +The `ng e2e` command will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results. #### Writing E2E Tests From bc705df1444b7d5c8de548b089bd55de06536ab1 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 4 Mar 2022 17:02:42 -0600 Subject: [PATCH 069/570] Check two locations for config.json, as it's in a different location in CI --- cypress/plugins/index.ts | 23 +++++++++++++++++++++-- cypress/support/commands.ts | 6 +++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts index c6eb874232..ead38afb92 100644 --- a/cypress/plugins/index.ts +++ b/cypress/plugins/index.ts @@ -1,15 +1,34 @@ +const fs = require('fs'); + // Plugins enable you to tap into, modify, or extend the internal behavior of Cypress // For more info, visit https://on.cypress.io/plugins-api module.exports = (on, config) => { - // Define "log" and "table" tasks, used for logging accessibility errors during CI - // Borrowed from https://github.com/component-driven/cypress-axe#in-cypress-plugins-file on('task', { + // Define "log" and "table" tasks, used for logging accessibility errors during CI + // Borrowed from https://github.com/component-driven/cypress-axe#in-cypress-plugins-file log(message: string) { console.log(message); return null; }, table(message: string) { console.table(message); + return null; + }, + // Cypress doesn't have access to the running application in Node.js. + // So, it's not possible to inject or load the AppConfig or environment of the Angular UI. + // Instead, we'll read our running application's config.json, which contains the configs & + // is regenerated at runtime each time the Angular UI application starts up. + readUIConfig() { + // Check if we have a config.json in the src/assets. If so, use that. + // This is where it's written when running "ng e2e" or "yarn serve" + if (fs.existsSync('./src/assets/config.json')) { + return fs.readFileSync('./src/assets/config.json', 'utf8'); + // Otherwise, check the dist/browser/assets + // This is where it's written when running "serve:ssr", which is what CI uses to start the frontend + } else if (fs.existsSync('./dist/browser/assets/config.json')) { + return fs.readFileSync('./dist/browser/assets/config.json', 'utf8'); + } + return null; } }); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index d66e1ef0bd..30951d46f1 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -34,9 +34,9 @@ function login(email: string, password: string): void { // So, it's not possible to inject or load the AppConfig or environment of the Angular UI. // Instead, we'll read our running application's config.json, which contains the configs & // is regenerated at runtime each time the Angular UI application starts up. - cy.readFile('./src/assets/config.json').then((str) => { - // Parse JSON file into a JSON object - const config = JSON.parse(JSON.stringify(str)); + cy.task('readUIConfig').then((str: string) => { + // Parse config into a JSON object + const config = JSON.parse(str); // Find the URL of our REST API. Have a fallback ready, just in case 'rest.baseUrl' cannot be found. let baseRestUrl = FALLBACK_TEST_REST_BASE_URL; From 59f03817b36d51a5be77df0508ec097926a3e9f8 Mon Sep 17 00:00:00 2001 From: YPaulsen-TLC Date: Mon, 20 Dec 2021 12:58:51 +0100 Subject: [PATCH 070/570] DSpace translation German translations of DSpace 7 message keys. --- src/assets/i18n/de.json5 | 100 +++++++++++++-------------------------- 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index b414852bcd..7edfd27993 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -768,31 +768,28 @@ // "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.", // TODO New key - Add a translation "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.", + // Der erste Tag, ab wann der Zugriff möglich ist. Dieses Datum kann in dieser Form nicht verändert werden. Um eine Embargo-Frist für eine Datei festzulegen, zu dem ()-Tab gehen, auf () klicken, die ()-Richtline für die Datei erstellen oder verändern und das Startdatum wie gewünscht einstellen. // "bitstream.edit.form.embargo.label": "Embargo until specific date", "bitstream.edit.form.embargo.label": "Embargo bis zu einem bestimmten Datum", // "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.", - // TODO New key - Add a translation - "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": "Ändern Sie den Dateinamen für die Datei. Beachten Sie, dass sich dadurch die angezeigte Datei-URL ändert, aber alte Links weiterhin funktionieren, solange sich die Sequenz-ID nicht ändert.", // "bitstream.edit.form.fileName.label": "Filename", "bitstream.edit.form.fileName.label": "Dateiname", // "bitstream.edit.form.newFormat.label": "Describe new format", - // TODO New key - Add a translation - "bitstream.edit.form.newFormat.label": "Describe new format", + "bitstream.edit.form.newFormat.label": "Neues Format beschreiben", // "bitstream.edit.form.newFormat.hint": "The application you used to create the file, and the version number (for example, \"ACMESoft SuperApp version 1.5\").", - // TODO New key - Add a translation - "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": "Die Anwendung, mit der Sie die Datei erstellt haben, und die Versionsnummer (zum Beispiel, \"ACMESoft SuperApp version 1.5\").", // "bitstream.edit.form.primaryBitstream.label": "Primary bitstream", "bitstream.edit.form.primaryBitstream.label": "Primäre Datei", // "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\".", - // TODO New key - Add a translation - "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": "Wenn das Format nicht in der obigen Liste enthalten ist, wählen Sie \"Format in der Liste\" und beschreiben Sie es unter \"Neues Format beschreiben\".", // "bitstream.edit.form.selectedFormat.label": "Selected Format", "bitstream.edit.form.selectedFormat.label": "Ausgewähltes Format", @@ -1153,23 +1150,19 @@ // "collection.edit.template.head": "Edit Template Item for Collection \"{{ collection }}\"", // TODO New key - Add a translation - "collection.edit.template.head": "Edit Template Item for Collection \"{{ collection }}\"", + "collection.edit.template.head": "Itemvorlage für Collection \"{{ collection }}\" bearbeiten", // "collection.edit.template.label": "Template item", - // TODO New key - Add a translation - "collection.edit.template.label": "Template item", + "collection.edit.template.label": "Itemvorlage", // "collection.edit.template.notifications.delete.error": "Failed to delete the item template", - // TODO New key - Add a translation - "collection.edit.template.notifications.delete.error": "Failed to delete the item template", + "collection.edit.template.notifications.delete.error": "Löschen der Itemvorlage fehlgeschlagen", // "collection.edit.template.notifications.delete.success": "Successfully deleted the item template", - // TODO New key - Add a translation - "collection.edit.template.notifications.delete.success": "Successfully deleted the item template", + "collection.edit.template.notifications.delete.success": "Die Itemvorlage wurde erfolgreich gelöscht", // "collection.edit.template.title": "Edit Template Item", - // TODO New key - Add a translation - "collection.edit.template.title": "Edit Template Item", + "collection.edit.template.title": "Itemvorlage bearbeiten", @@ -1393,8 +1386,7 @@ // "comcol-role.edit.submitters.name": "Submitters", - // TODO New key - Add a translation - "comcol-role.edit.submitters.name": "Submitters", + "comcol-role.edit.submitters.name": "Einreichende", // "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 @@ -1410,21 +1402,18 @@ "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.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": "Die Standardeinstellung für das Lesen eingehender Items ist derzeit auf Anonym eingestellt.", // "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": "Standard-Lesezugriff Datei", // "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.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": "Die Standardeinstellung für das Lesen eingehender Dateien ist derzeit auf Anonym eingestellt.", // "comcol-role.edit.editor.name": "Editors", @@ -1435,21 +1424,17 @@ // "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": "Endredakteure", // "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": "Die Endredakteure können die Metadaten der eingehenden Beiträge bearbeiten, sie können sie jedoch nicht ablehnen.", // "comcol-role.edit.reviewer.name": "Reviewers", - // TODO New key - Add a translation - "comcol-role.edit.reviewer.name": "Reviewers", + "comcol-role.edit.reviewer.name": "Prüfer", // "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": "Die Prüfer können eingehende Beiträge annehmen oder ablehnen. Sie können jedoch nicht die Metadaten der Einreichung bearbeiten.", @@ -1509,16 +1494,13 @@ "cookies.consent.app.opt-out.title": "(opt-out)", // "cookies.consent.app.purpose": "purpose", - // TODO New key - Add a translation - "cookies.consent.app.purpose": "purpose", + "cookies.consent.app.purpose": "Verwendungszweck", // "cookies.consent.app.required.description": "This application is always required", - // TODO New key - Add a translation - "cookies.consent.app.required.description": "This application is always required", + "cookies.consent.app.required.description": "Dieser Eintrag ist immer erforderlich.", // "cookies.consent.app.required.title": "(always required)", - // TODO New key - Add a translation - "cookies.consent.app.required.title": "(always required)", + "cookies.consent.app.required.title": "(immer erforderlich)", // "cookies.consent.update": "There were changes since your last visit, please update your consent.", "cookies.consent.update": "Seit Ihrem letzten Besuch haben sich Änderungen ergeben, bitte aktualisieren Sie Ihr Einverständnis.", @@ -1569,33 +1551,27 @@ "cookies.consent.app.title.acknowledgement": "Bestätigung", // "cookies.consent.app.description.acknowledgement": "Required for saving your acknowledgements and consents", - // TODO New key - Add a translation "cookies.consent.app.description.acknowledgement": "Erforderlich für die Speicherung Ihrer Bestätigungen und Einwilligungen", // "cookies.consent.app.title.google-analytics": "Google Analytics", - // TODO New key - Add a translation "cookies.consent.app.title.google-analytics": "Google Analytics", // "cookies.consent.app.description.google-analytics": "Allows us to track statistical data", - // TODO New key - Add a translation - "cookies.consent.app.description.google-analytics": "Allows us to track statistical data", + "cookies.consent.app.description.google-analytics": "Erlaubt uns, statistische Daten zu erfassen", // "cookies.consent.purpose.functional": "Functional", - // TODO New key - Add a translation - "cookies.consent.purpose.functional": "Functional", + "cookies.consent.purpose.functional": "Funktionell", // "cookies.consent.purpose.statistical": "Statistical", - // TODO New key - Add a translation - "cookies.consent.purpose.statistical": "Statistical", + "cookies.consent.purpose.statistical": "Statistisch", // "curation-task.task.checklinks.label": "Check Links in Metadata", - // TODO New key - Add a translation - "curation-task.task.checklinks.label": "Check Links in Metadata", + "curation-task.task.checklinks.label": "Links in Metadaten prüfen", // "curation-task.task.noop.label": "NOOP", // TODO New key - Add a translation @@ -1606,45 +1582,35 @@ "curation-task.task.profileformats.label": "Profile Bitstream Formats", // "curation-task.task.requiredmetadata.label": "Check for Required Metadata", - // TODO New key - Add a translation - "curation-task.task.requiredmetadata.label": "Check for Required Metadata", + "curation-task.task.requiredmetadata.label": "Prüfung auf erforderliche Metadaten", // "curation-task.task.translate.label": "Microsoft Translator", - // TODO New key - Add a translation - "curation-task.task.translate.label": "Microsoft Translator", + "curation-task.task.translate.label": "Microsoft Übersetzer", // "curation-task.task.vscan.label": "Virus Scan", - // TODO New key - Add a translation - "curation-task.task.vscan.label": "Virus Scan", + "curation-task.task.vscan.label": "Viren-Scan", // "curation.form.task-select.label": "Task:", - // TODO New key - Add a translation - "curation.form.task-select.label": "Task:", + "curation.form.task-select.label": "Aufgabe:", // "curation.form.submit": "Start", - // TODO New key - Add a translation "curation.form.submit": "Start", // "curation.form.submit.success.head": "The curation task has been started successfully", - // TODO New key - Add a translation - "curation.form.submit.success.head": "The curation task has been started successfully", + "curation.form.submit.success.head": "Die Datenpflegeroutine wurde erfolgreich gestartet", // "curation.form.submit.success.content": "You will be redirected to the corresponding process page.", - // TODO New key - Add a translation - "curation.form.submit.success.content": "You will be redirected to the corresponding process page.", + "curation.form.submit.success.content": "Sie werden auf die entsprechende Prozessseite weitergeleitet.", // "curation.form.submit.error.head": "Running the curation task failed", - // TODO New key - Add a translation - "curation.form.submit.error.head": "Running the curation task failed", + "curation.form.submit.error.head": "Die Ausführung der Datenpflegeroutine ist fehlgeschlagen", // "curation.form.submit.error.content": "An error occured when trying to start the curation task.", - // TODO New key - Add a translation - "curation.form.submit.error.content": "An error occured when trying to start the curation task.", + "curation.form.submit.error.content": "Beim Versuch, die Datenpflegeroutine zu starten, ist ein Fehler aufgetreten.", // "curation.form.handle.label": "Handle:", - // TODO New key - Add a translation "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)", From 8b0c0348b2d582bb106ccc00ab67171017763b74 Mon Sep 17 00:00:00 2001 From: YPaulsen-TLC Date: Fri, 7 Jan 2022 14:34:18 +0100 Subject: [PATCH 071/570] New message key translations German translations of DSpace 7 message keys. --- src/assets/i18n/de.json5 | 400 +++++++++++++++------------------------ 1 file changed, 152 insertions(+), 248 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 7edfd27993..77105cc7aa 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -107,7 +107,7 @@ "admin.registries.bitstream-formats.edit.head": "Dateiformat: {{ 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": "Dateiformate, die als intern gekennzeichnet sind, dienen administrativen Zwecken und bleiben dem Endnutzer verborgen.", + "admin.registries.bitstream-formats.edit.internal.hint": "Dateiformate, die als intern gekennzeichnet sind, dienen administrativen Zwecken und bleiben dem/der Endnutzer:in verborgen.", // "admin.registries.bitstream-formats.edit.internal.label": "Internal", "admin.registries.bitstream-formats.edit.internal.label": "Intern", @@ -812,7 +812,7 @@ // "browse.comcol.by.author": "By Author", - "browse.comcol.by.author": "Nach Autor/in", + "browse.comcol.by.author": "Nach Autor:in", // "browse.comcol.by.dateissued": "By Issue Date", "browse.comcol.by.dateissued": "Nach Erscheinungsjahr", @@ -830,7 +830,7 @@ "browse.empty": "Es gibt keine Dokumente, die angezeigt werden können.", // "browse.metadata.author": "Author", - "browse.metadata.author": "Autor/in", + "browse.metadata.author": "Autor:in", // "browse.metadata.dateissued": "Issue Date", "browse.metadata.dateissued": "Erscheinungsdatum", @@ -842,7 +842,7 @@ "browse.metadata.title": "Titel", // "browse.metadata.author.breadcrumbs": "Browse by Author", - "browse.metadata.author.breadcrumbs": "Auflistung nach Autor(in)", + "browse.metadata.author.breadcrumbs": "Auflistung nach Autor:in", // "browse.metadata.dateissued.breadcrumbs": "Browse by Date", "browse.metadata.dateissued.breadcrumbs": "Auflistung nach Datum", @@ -978,7 +978,7 @@ "collection.edit.item-mapper.confirm": "Ausgewählte Ressourcen spiegeln", // "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": "Sammlungsadministratoren haben die Möglichkeit Ressourcen von einer Sammlung in eine andere zu spiegeln. Man kann nach Ressourcen in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Ressourcen anzeigen lassen.", + "collection.edit.item-mapper.description": "Sammlungsadministrator:innen haben die Möglichkeit Ressourcen von einer Sammlung in eine andere zu spiegeln. Man kann nach Ressourcen in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Ressourcen anzeigen lassen.", // "collection.edit.item-mapper.head": "Item Mapper - Map Items from Other Collections", "collection.edit.item-mapper.head": "Ressourcen spiegeln - Spiegelt Ressourcen aus anderen Sammlungen", @@ -1370,10 +1370,10 @@ // "comcol-role.edit.community-admin.name": "Administrators", - "comcol-role.edit.community-admin.name": "Administratoren", + "comcol-role.edit.community-admin.name": "Administrator:innen", // "comcol-role.edit.collection-admin.name": "Administrators", - "comcol-role.edit.collection-admin.name": "Administratoren", + "comcol-role.edit.collection-admin.name": "Administrator:innen", // "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).", @@ -1417,24 +1417,24 @@ // "comcol-role.edit.editor.name": "Editors", - "comcol-role.edit.editor.name": "Redakteure", + "comcol-role.edit.editor.name": "Redakteur:innen", // "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": "Die Redakteure können die Metadaten der eingehenden Beiträge bearbeiten und sie dann annehmen oder ablehnen.", // "comcol-role.edit.finaleditor.name": "Final editors", - "comcol-role.edit.finaleditor.name": "Endredakteure", + "comcol-role.edit.finaleditor.name": "Endredakteur:innen", // "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": "Die Endredakteure können die Metadaten der eingehenden Beiträge bearbeiten, sie können sie jedoch nicht ablehnen.", + "comcol-role.edit.finaleditor.description": "Die Endredakteur:innen können die Metadaten der eingehenden Beiträge bearbeiten, sie können sie jedoch nicht ablehnen.", // "comcol-role.edit.reviewer.name": "Reviewers", - "comcol-role.edit.reviewer.name": "Prüfer", + "comcol-role.edit.reviewer.name": "Prüfer:innen", // "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": "Die Prüfer können eingehende Beiträge annehmen oder ablehnen. Sie können jedoch nicht die Metadaten der Einreichung bearbeiten.", + "comcol-role.edit.reviewer.description": "Die Prüfer:innen können eingehende Beiträge annehmen oder ablehnen. Sie können jedoch nicht die Metadaten der Einreichung bearbeiten.", @@ -1623,8 +1623,7 @@ "dso-selector.create.collection.head": "Neue Sammlung", // "dso-selector.create.collection.sub-level": "Create a new collection in", - // TODO New key - Add a translation - "dso-selector.create.collection.sub-level": "Create a new collection in", + "dso-selector.create.collection.sub-level": "Eine neue Sammlung erstellen in", // "dso-selector.create.community.head": "New community", "dso-selector.create.community.head": "Neuer Bereich", @@ -1639,12 +1638,10 @@ "dso-selector.create.item.head": "Neue Ressource", // "dso-selector.create.item.sub-level": "Create a new item in", - // TODO New key - Add a translation - "dso-selector.create.item.sub-level": "Create a new item in", + "dso-selector.create.item.sub-level": "Ein neues Item erstellen in", // "dso-selector.create.submission.head": "New submission", - // TODO New key - Add a translation - "dso-selector.create.submission.head": "New submission", + "dso-selector.create.submission.head": "Neue Veröffentlichung", // "dso-selector.edit.collection.head": "Edit collection", "dso-selector.edit.collection.head": "Sammlung bearbeiten", @@ -1656,8 +1653,7 @@ "dso-selector.edit.item.head": "Ressource bearbeiten", // "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", - // TODO New key - Add a translation - "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", + "dso-selector.export-metadata.dspaceobject.head": "Exportieren der Metadaten aus", // "dso-selector.no-results": "No {{ type }} found", "dso-selector.no-results": "Kein(e) {{ type }} gefunden", @@ -1668,36 +1664,28 @@ // "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}", - // TODO New key - Add a translation - "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}", + "confirmation-modal.export-metadata.header": "Exportieren der Metadaten für {{ dsoName }}", // "confirmation-modal.export-metadata.info": "Are you sure you want to export metadata for {{ dsoName }}", - // TODO New key - Add a translation - "confirmation-modal.export-metadata.info": "Are you sure you want to export metadata for {{ dsoName }}", + "confirmation-modal.export-metadata.info": "Sind Sie sicher, dass Sie die Metadaten für {{ dsoName }} exportieren wollen", // "confirmation-modal.export-metadata.cancel": "Cancel", - // TODO New key - Add a translation - "confirmation-modal.export-metadata.cancel": "Cancel", + "confirmation-modal.export-metadata.cancel": "Abbrechen", // "confirmation-modal.export-metadata.confirm": "Export", - // TODO New key - Add a translation - "confirmation-modal.export-metadata.confirm": "Export", + "confirmation-modal.export-metadata.confirm": "Exportieren", // "confirmation-modal.delete-eperson.header": "Delete EPerson \"{{ dsoName }}\"", - // TODO New key - Add a translation - "confirmation-modal.delete-eperson.header": "Delete EPerson \"{{ dsoName }}\"", + "confirmation-modal.delete-eperson.header": "Person \"{{ dsoName }}\" löschen", // "confirmation-modal.delete-eperson.info": "Are you sure you want to delete EPerson \"{{ dsoName }}\"", - // TODO New key - Add a translation - "confirmation-modal.delete-eperson.info": "Are you sure you want to delete EPerson \"{{ dsoName }}\"", + "confirmation-modal.delete-eperson.info": "Sind Sie sicher, dass Sie die Person \"{{ dsoName }}\" löschen möchten", // "confirmation-modal.delete-eperson.cancel": "Cancel", - // TODO New key - Add a translation - "confirmation-modal.delete-eperson.cancel": "Cancel", + "confirmation-modal.delete-eperson.cancel": "Abbrechen", // "confirmation-modal.delete-eperson.confirm": "Delete", - // TODO New key - Add a translation - "confirmation-modal.delete-eperson.confirm": "Delete", + "confirmation-modal.delete-eperson.confirm": "Löschen", // "error.bitstream": "Error fetching bitstream", @@ -1726,7 +1714,7 @@ "error.item": "Fehler beim Laden der Ressource", // "error.items": "Error fetching items", - "error.items": "Fejöer beim Laden der Ressourcen", + "error.items": "Fehler beim Laden der Ressourcen", // "error.objects": "Error fetching objects", "error.objects": "Fehler beim Laden der Objekte", @@ -1761,8 +1749,7 @@ // "file-section.error.header": "Error obtaining files for this item", - // TODO New key - Add a translation - "file-section.error.header": "Error obtaining files for this item", + "file-section.error.header": "Fehler beim Abrufen der Dateien für dieses Item", @@ -1776,124 +1763,98 @@ "footer.link.lyrasis": "LYRASIS", // "footer.link.cookies": "Cookie settings", - // TODO New key - Add a translation - "footer.link.cookies": "Cookie settings", + "footer.link.cookies": "Cookie Einstellungen", // "footer.link.privacy-policy": "Privacy policy", - // TODO New key - Add a translation - "footer.link.privacy-policy": "Privacy policy", + "footer.link.privacy-policy": "Datenschutzbestimmungen", // "footer.link.end-user-agreement":"End User Agreement", - // TODO New key - Add a translation - "footer.link.end-user-agreement":"End User Agreement", - + "footer.link.end-user-agreement":"Endnutzervereinbarung", + // "forgot-email.form.header": "Forgot Password", - // TODO New key - Add a translation - "forgot-email.form.header": "Forgot Password", + "forgot-email.form.header": "Passwort vergessen", // "forgot-email.form.info": "Enter Register an account to subscribe to collections for email updates, and submit new items to DSpace.", // TODO New key - Add a translation "forgot-email.form.info": "Enter Register an account to subscribe to collections for email updates, and submit new items to DSpace.", // "forgot-email.form.email": "Email Address *", - // TODO New key - Add a translation - "forgot-email.form.email": "Email Address *", + "forgot-email.form.email": "E-Mail-Addresse *", // "forgot-email.form.email.error.required": "Please fill in an email address", - // TODO New key - Add a translation - "forgot-email.form.email.error.required": "Please fill in an email address", + "forgot-email.form.email.error.required": "Bitte geben Sie eine E-Mail-Adresse ein", // "forgot-email.form.email.error.pattern": "Please fill in a valid email address", - // TODO New key - Add a translation - "forgot-email.form.email.error.pattern": "Please fill in a valid email address", + "forgot-email.form.email.error.pattern": "Bitte geben Sie eine gültige E-Mail-Adresse ein", // "forgot-email.form.email.hint": "This address will be verified and used as your login name.", - // TODO New key - Add a translation - "forgot-email.form.email.hint": "This address will be verified and used as your login name.", + "forgot-email.form.email.hint": "Diese Adresse wird überprüft und als Ihr Anmeldename verwendet.", // "forgot-email.form.submit": "Submit", - // TODO New key - Add a translation - "forgot-email.form.submit": "Submit", + "forgot-email.form.submit": "Einreichen", // "forgot-email.form.success.head": "Verification email sent", - // TODO New key - Add a translation - "forgot-email.form.success.head": "Verification email sent", + "forgot-email.form.success.head": "Verifizierungs-E-Mail gesendet", // "forgot-email.form.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", // TODO New key - Add a translation "forgot-email.form.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", // "forgot-email.form.error.head": "Error when trying to register email", - // TODO New key - Add a translation - "forgot-email.form.error.head": "Error when trying to register email", + "forgot-email.form.error.head": "Fehler beim Versuch, die E-Mail zu registrieren", // "forgot-email.form.error.content": "An error occured when registering the following email address: {{ email }}", // TODO New key - Add a translation - "forgot-email.form.error.content": "An error occured when registering the following email address: {{ email }}", + "forgot-email.form.error.content": "Bei der Registrierung der folgenden E-Mail-Adresse ist ein Fehler aufgetreten: {{ email }}", // "forgot-password.title": "Forgot Password", - // TODO New key - Add a translation - "forgot-password.title": "Forgot Password", + "forgot-password.title": "Passwort vergessen", // "forgot-password.form.head": "Forgot Password", - // TODO New key - Add a translation - "forgot-password.form.head": "Forgot Password", + "forgot-password.form.head": "Passwort vergessen", // "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.", - // TODO New key - Add a translation - "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": "Geben Sie ein neues Kennwort in das unten stehende Feld ein und bestätigen Sie es, indem Sie es erneut in das zweite Feld eingeben. Es sollte mindestens sechs Zeichen lang sein.", // "forgot-password.form.card.security": "Security", - // TODO New key - Add a translation - "forgot-password.form.card.security": "Security", + "forgot-password.form.card.security": "Sicherheit", // "forgot-password.form.identification.header": "Identify", - // TODO New key - Add a translation - "forgot-password.form.identification.header": "Identify", + "forgot-password.form.identification.header": "Identifizieren", // "forgot-password.form.identification.email": "Email address: ", - // TODO New key - Add a translation - "forgot-password.form.identification.email": "Email address: ", + "forgot-password.form.identification.email": "E-Mail-Addresse: ", // "forgot-password.form.label.password": "Password", - // TODO New key - Add a translation - "forgot-password.form.label.password": "Password", + "forgot-password.form.label.password": "Passwort", // "forgot-password.form.label.passwordrepeat": "Retype to confirm", - // TODO New key - Add a translation - "forgot-password.form.label.passwordrepeat": "Retype to confirm", + "forgot-password.form.label.passwordrepeat": "Zum Bestätigen wiederholen", // "forgot-password.form.error.empty-password": "Please enter a password in the box below.", - // TODO New key - Add a translation - "forgot-password.form.error.empty-password": "Please enter a password in the box below.", + "forgot-password.form.error.empty-password": "Bitte geben Sie in das unten stehende Feld ein Passwort ein.", // "forgot-password.form.error.matching-passwords": "The passwords do not match.", - // TODO New key - Add a translation - "forgot-password.form.error.matching-passwords": "The passwords do not match.", + "forgot-password.form.error.matching-passwords": "Die Passwörter stimmen nicht überein.", // "forgot-password.form.error.password-length": "The password should be at least 6 characters long.", - // TODO New key - Add a translation - "forgot-password.form.error.password-length": "The password should be at least 6 characters long.", + "forgot-password.form.error.password-length": "Das Passwort sollte mindestens 6 Zeichen lang sein.", // "forgot-password.form.notification.error.title": "Error when trying to submit new password", - // TODO New key - Add a translation - "forgot-password.form.notification.error.title": "Error when trying to submit new password", + "forgot-password.form.notification.error.title": "Fehler beim Versuch, ein neues Passwort zu übermitteln", // "forgot-password.form.notification.success.content": "The password reset was successful. You have been logged in as the created user.", - // TODO New key - Add a translation - "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": "Das Zurücksetzen des Passworts war erfolgreich. Sie wurden in den erstellten Account eingeloggt.", // "forgot-password.form.notification.success.title": "Password reset completed", - // TODO New key - Add a translation - "forgot-password.form.notification.success.title": "Password reset completed", + "forgot-password.form.notification.success.title": "Passwort zurücksetzen abgeschlossen", // "forgot-password.form.submit": "Submit password", - // TODO New key - Add a translation - "forgot-password.form.submit": "Submit password", + "forgot-password.form.submit": "Passwort abschicken", @@ -1967,7 +1928,6 @@ "form.search": "Suche", // "form.search-help": "Click here to look for an existing correspondence", - // TODO Source message changed - Revise the translation "form.search-help": "Klicken Sie hier, um eine Übereinstimmung zu suchen", // "form.submit": "Submit", @@ -1979,8 +1939,7 @@ "home.description": "", // "home.breadcrumbs": "Home", - // TODO New key - Add a translation - "home.breadcrumbs": "Home", + "home.breadcrumbs": "Startseite", // "search.search-form.placeholder": "Search the repository ...", "home.search-form.placeholder": "Durchsuche Repositorium", @@ -2032,12 +1991,10 @@ // "item.alerts.private": "This item is private", - // TODO New key - Add a translation - "item.alerts.private": "This item is private", + "item.alerts.private": "Dieses Item ist privat", // "item.alerts.withdrawn": "This item has been withdrawn", - // TODO New key - Add a translation - "item.alerts.withdrawn": "This item has been withdrawn", + "item.alerts.withdrawn": "Dieses Item wurde zurückgezogen", @@ -2046,8 +2003,7 @@ "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.title": "Edit item's Policies", - // TODO New key - Add a translation - "item.edit.authorizations.title": "Edit item's Policies", + "item.edit.authorizations.title": "Item-Richtlinien bearbeiten", @@ -2079,7 +2035,6 @@ "item.bitstreams.upload.drop-message": "Eine Datei zum Hochladen ablegen", // "item.bitstreams.upload.item": "Item: ", - // TODO New key - Add a translation "item.bitstreams.upload.item": "Item: ", // "item.bitstreams.upload.notifications.bundle.created.content": "Successfully created new bundle.", @@ -2092,8 +2047,7 @@ "item.bitstreams.upload.notifications.upload.failed": "Hochladen fehlgeschlagen. Bitte überprüfen Sie den Inhalt, bevor Sie es erneut versuchen.", // "item.bitstreams.upload.title": "Upload bitstream", - // TODO New key - Add a translation - "item.bitstreams.upload.title": "Upload bitstream", + "item.bitstreams.upload.title": "Datei hochladen", @@ -2105,8 +2059,7 @@ "item.edit.bitstreams.bundle.displaying": "Currently displaying {{ amount }} bitstreams of {{ total }}.", // "item.edit.bitstreams.bundle.load.all": "Load all ({{ total }})", - // TODO New key - Add a translation - "item.edit.bitstreams.bundle.load.all": "Load all ({{ total }})", + "item.edit.bitstreams.bundle.load.all": "Alle ({{ total }}) laden", // "item.edit.bitstreams.bundle.load.more": "Load more", "item.edit.bitstreams.bundle.load.more": "Mehr laden", @@ -2168,7 +2121,7 @@ "item.edit.bitstreams.notifications.move.saved.title": "Move changes saved", // "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": "Die Ressource, an der Sie gerade arbeiten, wurde von einem anderen Benutzer geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", + "item.edit.bitstreams.notifications.outdated.content": "Die Ressource, an der Sie gerade arbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", // "item.edit.bitstreams.notifications.outdated.title": "Changes outdated", "item.edit.bitstreams.notifications.outdated.title": "Veraltete Änderungen", @@ -2236,7 +2189,7 @@ "item.edit.item-mapper.cancel": "Abbrechen", // "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": "Sammlungsadministratoren haben die Möglichkeit Ressourcen von einer Sammlung in eine andere zu spiegeln. Man kann nach Ressourcen in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Ressourcen anzeigen lassen.", + "item.edit.item-mapper.description": "Sammlungsadministrator:innen haben die Möglichkeit Ressourcen von einer Sammlung in eine andere zu spiegeln. Man kann nach Ressourcen in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Ressourcen anzeigen lassen.", // "item.edit.item-mapper.head": "Item Mapper - Map Item to Collections", "item.edit.item-mapper.head": "Ressourcen spiegeln - Spiegelt eine Ressource in andere Sammlungen", @@ -2322,8 +2275,7 @@ "item.edit.metadata.notifications.discarded.title": "Änderungen verworfen", // "item.edit.metadata.notifications.error.title": "An error occurred", - // TODO New key - Add a translation - "item.edit.metadata.notifications.error.title": "An error occurred", + "item.edit.metadata.notifications.error.title": "Es ist ein Fehler aufgetreten", // "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": "Ihre Änderungen wurden nicht gespeichert. Stellen Sie sicher, dass alle Felder gültig sind, bevor Sie Abspeichern.", @@ -2332,7 +2284,7 @@ "item.edit.metadata.notifications.invalid.title": "Metadaten ungültig", // "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": "Die Ressource, an der Sie gerade arbeiten, wurde von einem anderen Benutzer geändert. Ihre aktuellen Änderungen werden nicht angewandt, um Konflikte zu vermeiden.", + "item.edit.metadata.notifications.outdated.content": "Die Ressource, an der Sie gerade arbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden nicht angewandt, um Konflikte zu vermeiden.", // "item.edit.metadata.notifications.outdated.title": "Changed outdated", "item.edit.metadata.notifications.outdated.title": "Änderung veraltet", @@ -2482,7 +2434,7 @@ "item.edit.relationships.notifications.failed.title": "Fehler beim Bearbeiten der Beziehung", // "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": "Die Ressource, die Sie gerade bearbeiten, wurde von einem anderen Benutzer geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", + "item.edit.relationships.notifications.outdated.content": "Die Ressource, die Sie gerade bearbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", // "item.edit.relationships.notifications.outdated.title": "Changes outdated", "item.edit.relationships.notifications.outdated.title": "Änderungen veraltet", @@ -2530,7 +2482,7 @@ "item.edit.tabs.relationships.title": "Ressource bearbeiten - Relationen", // "item.edit.tabs.status.buttons.authorizations.button": "Authorizations...", - "item.edit.tabs.status.buttons.authorizations.button": "Rechte...", + "item.edit.tabs.status.buttons.authorizations.button": "Berechtigungen...", // "item.edit.tabs.status.buttons.authorizations.label": "Edit item's authorization policies", "item.edit.tabs.status.buttons.authorizations.label": "Rechte der Ressource bearbeiten", @@ -2671,7 +2623,7 @@ "item.page.abstract": "Zusammenfassung", // "item.page.author": "Authors", - "item.page.author": "AutorInnen", + "item.page.author": "Autor:innen", // "item.page.citation": "Citation", "item.page.citation": "Zitierform", @@ -2713,7 +2665,7 @@ "item.page.link.simple": "Kurzanzeige", // "item.page.person.search.title": "Articles by this author", - "item.page.person.search.title": "Veröffentlichungen dieses/r Autor/in", + "item.page.person.search.title": "Veröffentlichungen dieses/dieser Autor:in", // "item.page.related-items.view-more": "Show {{ amount }} more", "item.page.related-items.view-more": "{{ amount }} mehr anzeigen", @@ -2728,7 +2680,7 @@ "item.page.relationships.isJournalOfPublication": "Veröffentlichungen", // "item.page.relationships.isOrgUnitOfPerson": "Authors", - "item.page.relationships.isOrgUnitOfPerson": "AutorInnen", + "item.page.relationships.isOrgUnitOfPerson": "Autor:innen", // "item.page.relationships.isOrgUnitOfProject": "Research Projects", "item.page.relationships.isOrgUnitOfProject": "Forschungsprojekte", @@ -2755,7 +2707,7 @@ "item.preview.dc.identifier.uri": "Identifikator:", // "item.preview.dc.contributor.author": "Authors:", - "item.preview.dc.contributor.author": "AutorInnen:", + "item.preview.dc.contributor.author": "Autor:innen:", // "item.preview.dc.date.issued": "Published date:", "item.preview.dc.date.issued": "Erscheinungsdatum:", @@ -2793,7 +2745,7 @@ "item.select.empty": "Es gibt keine Ressourcen dazu", // "item.select.table.author": "Author", - "item.select.table.author": "Autor/in", + "item.select.table.author": "Autor:in", // "item.select.table.collection": "Collection", "item.select.table.collection": "Sammlung", @@ -2809,7 +2761,6 @@ "item.version.history.head": "Versionsgeschichte", // "item.version.history.return": "Return", - // TODO New key - Add a translation "item.version.history.return": "Zurück", // "item.version.history.selected": "Selected version", @@ -2822,7 +2773,7 @@ "item.version.history.table.item": "Ressource", // "item.version.history.table.editor": "Editor", - "item.version.history.table.editor": "Herausgeber/in", + "item.version.history.table.editor": "Herausgeber:in", // "item.version.history.table.date": "Date", "item.version.history.table.date": "Datum", @@ -2848,7 +2799,7 @@ "journal.page.edit": "Diese Ressource bearbeiten", // "journal.page.editor": "Editor-in-Chief", - "journal.page.editor": "Chefredakteur", + "journal.page.editor": "Chefredakteur:in", // "journal.page.issn": "ISSN", "journal.page.issn": "ISSN", @@ -3038,7 +2989,7 @@ // "menu.section.admin_search": "Admin Search", - "menu.section.admin_search": "Administratoren Suche", + "menu.section.admin_search": "Administrator:innen Suche", @@ -3046,7 +2997,7 @@ "menu.section.browse_community": "Dieser Bereich", // "menu.section.browse_community_by_author": "By Author", - "menu.section.browse_community_by_author": "Nach Autor/in", + "menu.section.browse_community_by_author": "Nach Autor:in", // "menu.section.browse_community_by_issue_date": "By Issue Date", "menu.section.browse_community_by_issue_date": "Nach Erscheinungsdateum", @@ -3058,7 +3009,7 @@ "menu.section.browse_global": "Das gesamte Repositorium", // "menu.section.browse_global_by_author": "By Author", - "menu.section.browse_global_by_author": "Nach Autor/in", + "menu.section.browse_global_by_author": "Nach Autor:in", // "menu.section.browse_global_by_dateissued": "By Issue Date", "menu.section.browse_global_by_dateissued": "Nach Erscheinungsjahr", @@ -3260,8 +3211,7 @@ "mydspace.description": "", // "mydspace.general.text-here": "here", - // TODO New key - Add a translation - "mydspace.general.text-here": "here", + "mydspace.general.text-here": "hier", // "mydspace.messages.controller-help": "Select this option to send a message to item's submitter.", "mydspace.messages.controller-help": "Wählen Sie diese Option, um dem/derjenigen, die die Ressource eingereicht hat, eine Nachricht zu schicken.", @@ -3318,7 +3268,7 @@ "mydspace.results.no-abstract": "Keine Zusammenfassung", // "mydspace.results.no-authors": "No Authors", - "mydspace.results.no-authors": "Keine AutorInnen", + "mydspace.results.no-authors": "Keine Autor:innen", // "mydspace.results.no-collections": "No Collections", "mydspace.results.no-collections": "Keine Sammlungen", @@ -3587,14 +3537,12 @@ "process.detail.output-files" : "Output Files", // "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" : "Dieser Prozess enthält keine Ausgabedateien", // "process.detail.script" : "Script", "process.detail.script" : "Skript", // "process.detail.title" : "Process: {{ id }} - {{ name }}", - // TODO New key - Add a translation "process.detail.title" : "Prozess: {{ id }} - {{ name }}", // "process.detail.start-time" : "Start time", @@ -3627,7 +3575,7 @@ "process.overview.table.status" : "Status", // "process.overview.table.user" : "User", - "process.overview.table.user" : "Benutzer", + "process.overview.table.user" : "Benutzer:in", // "process.overview.title": "Processes Overview", "process.overview.title": "Überblick der Prozesse", @@ -3652,8 +3600,7 @@ "profile.form.submit": "Profil aktualisieren", // "profile.groups.head": "Authorization groups you belong to", - // TODO New key - Add a translation - "profile.groups.head": "Authorization groups you belong to", + "profile.groups.head": "Berechtigungsgruppen, denen Sie angehören", // "profile.head": "Update Profile", "profile.head": "Profil aktualisieren", @@ -3793,7 +3740,7 @@ // "register-email.title": "New user registration", - "register-email.title": "Registrierung neuer Benutzer", + "register-email.title": "Registrierung neuer Benutzer:innen", // "register-page.create-profile.header": "Create Profile", "register-page.create-profile.header": "Profil erstellen", @@ -3847,20 +3794,20 @@ "register-page.create-profile.submit": "Vollständige Registrierung", // "register-page.create-profile.submit.error.content": "Something went wrong while registering a new user.", - "register-page.create-profile.submit.error.content": "Bei der Registrierung eines neuen Benutzers ist ein Fehler aufgetreten.", + "register-page.create-profile.submit.error.content": "Bei der Registrierung eines neuen Accounts ist ein Fehler aufgetreten.", // "register-page.create-profile.submit.error.head": "Registration failed", "register-page.create-profile.submit.error.head": "Registrierung fehlgeschlagen.", // "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": "Die Registrierung war erfolgreich. Sie wurden als der angelegte Benutzer angemeldet.", + "register-page.create-profile.submit.success.content": "Die Registrierung war erfolgreich. Sie wurden als der/die angelegte Benutzer:in angemeldet.", // "register-page.create-profile.submit.success.head": "Registration completed", "register-page.create-profile.submit.success.head": "Registrierung abgeschlossen", // "register-page.registration.header": "New user registration", - "register-page.registration.header": "Registrierung neuer Benutzer", + "register-page.registration.header": "Registrierung neuer Benutzer:innen", // "register-page.registration.info": "Register an account to subscribe to collections for email updates, and submit new items to DSpace.", "register-page.registration.info": "Registrieren Sie ein Konto, um Sammlungen für E-Mail-Updates zu abonnieren und neue Ressourcen in DSpace einzugeben.", @@ -3875,7 +3822,7 @@ "register-page.registration.email.error.pattern": "Bitte geben Sie eine gültige E-Mail Adresse an", // "register-page.registration.email.hint": "This address will be verified and used as your login name.", - "register-page.registration.email.hint": "Diese Adresse wird überprüft und als Ihr Benutzername verwendet..", + "register-page.registration.email.hint": "Diese Adresse wird überprüft und als Ihr Accountname verwendet..", // "register-page.registration.submit": "Register", "register-page.registration.submit": "Registrieren", @@ -3904,13 +3851,13 @@ "relationships.add.error.title": "Beziehung kann nicht hinzugefügt werden", // "relationships.isAuthorOf": "Authors", - "relationships.isAuthorOf": "AutorInnen", + "relationships.isAuthorOf": "Autor:innen", // "relationships.isAuthorOf.Person": "Authors (persons)", "relationships.isAuthorOf.Person": "AutorInnen (Personen)", // "relationships.isAuthorOf.OrgUnit": "Authors (organizational units)", - "relationships.isAuthorOf.OrgUnit": "AutorInnen (Organisationseinheiten)", + "relationships.isAuthorOf.OrgUnit": "Autor:innen (Organisationseinheiten)", // "relationships.isIssueOf": "Journal Issues", "relationships.isIssueOf": "Zeitschriftenausgaben", @@ -3925,7 +3872,7 @@ "relationships.isOrgUnitOf": "Organisationseinheiten", // "relationships.isPersonOf": "Authors", - "relationships.isPersonOf": "AutorInnen", + "relationships.isPersonOf": "Autor:innen", // "relationships.isProjectOf": "Research Projects", "relationships.isProjectOf": "Forschungsvorhaben", @@ -3957,24 +3904,19 @@ "resource-policies.add.for.": "Eine neue Richtlinie hinzufügen", // "resource-policies.add.for.bitstream": "Add a new Bitstream policy", - // TODO New key - Add a translation - "resource-policies.add.for.bitstream": "Add a new Bitstream policy", + "resource-policies.add.for.bitstream": "Eine neue Datei-Richtlinie hinzufügen", // "resource-policies.add.for.bundle": "Add a new Bundle policy", - // TODO New key - Add a translation - "resource-policies.add.for.bundle": "Add a new Bundle policy", + "resource-policies.add.for.bundle": "Eine neue Bündel-Richtlinie hinzufügen", // "resource-policies.add.for.item": "Add a new Item policy", - // TODO New key - Add a translation - "resource-policies.add.for.item": "Add a new Item policy", + "resource-policies.add.for.item": "Eine neue Item-Richtlinie hinzufügen", // "resource-policies.add.for.community": "Add a new Community policy", - // TODO New key - Add a translation - "resource-policies.add.for.community": "Add a new Community policy", + "resource-policies.add.for.community": "Eine neue Bereiche-Richtlinie hinzufügen", // "resource-policies.add.for.collection": "Add a new Collection policy", - // TODO New key - Add a translation - "resource-policies.add.for.collection": "Add a new Collection policy", + "resource-policies.add.for.collection": "Eine neue Sammlungen-Richtlinie hinzufügen", // "resource-policies.create.page.heading": "Create new resource policy for ", // TODO New key - Add a translation @@ -4021,15 +3963,13 @@ "resource-policies.edit.page.title": "Edit resource policy", // "resource-policies.form.action-type.label": "Select the action type", - // TODO New key - Add a translation - "resource-policies.form.action-type.label": "Select the action type", + "resource-policies.form.action-type.label": "Wählen Sie die Aktionsart", // "resource-policies.form.action-type.required": "You must select the resource policy action.", // TODO New key - Add a translation "resource-policies.form.action-type.required": "You must select the resource policy action.", // "resource-policies.form.eperson-group-list.label": "The eperson or group that will be granted the permission", - // TODO New key - Add a translation "resource-policies.form.eperson-group-list.label": "Die Person oder Gruppe, der die Genehmigung erteilt wird", // "resource-policies.form.eperson-group-list.select.btn": "Select", @@ -4064,16 +4004,14 @@ "resource-policies.form.name.label": "Name", // "resource-policies.form.policy-type.label": "Select the policy type", - // TODO New key - Add a translation - "resource-policies.form.policy-type.label": "Select the policy type", + "resource-policies.form.policy-type.label": "Wählen Sie den Richtlinientyp", // "resource-policies.form.policy-type.required": "You must select the resource policy type.", // TODO New key - Add a translation "resource-policies.form.policy-type.required": "You must select the resource policy type.", // "resource-policies.table.headers.action": "Action", - // TODO New key - Add a translation - "resource-policies.table.headers.action": "Action", + "resource-policies.table.headers.action": "Aktion", // "resource-policies.table.headers.date.end": "End Date", "resource-policies.table.headers.date.end": "Enddatum", @@ -4106,24 +4044,19 @@ "resource-policies.table.headers.policyType": "Typ", // "resource-policies.table.headers.title.for.bitstream": "Policies for Bitstream", - // TODO New key - Add a translation - "resource-policies.table.headers.title.for.bitstream": "Policies for Bitstream", + "resource-policies.table.headers.title.for.bitstream": "Richtlinien für Dateien", // "resource-policies.table.headers.title.for.bundle": "Policies for Bundle", - // TODO New key - Add a translation - "resource-policies.table.headers.title.for.bundle": "Policies for Bundle", + "resource-policies.table.headers.title.for.bundle": "Richtlinien für Bündel", // "resource-policies.table.headers.title.for.item": "Policies for Item", - // TODO New key - Add a translation - "resource-policies.table.headers.title.for.item": "Policies for Item", + "resource-policies.table.headers.title.for.item": "Richtlinien für Items", // "resource-policies.table.headers.title.for.community": "Policies for Community", - // TODO New key - Add a translation - "resource-policies.table.headers.title.for.community": "Policies for Community", + "resource-policies.table.headers.title.for.community": "Richtlinien für Bereiche", // "resource-policies.table.headers.title.for.collection": "Policies for Collection", - // TODO New key - Add a translation - "resource-policies.table.headers.title.for.collection": "Policies for Collection", + "resource-policies.table.headers.title.for.collection": "Richtlinien für Sammlungen", @@ -4143,7 +4076,7 @@ "search.search-form.placeholder": "Durchsuche Repositorium", // "search.filters.applied.f.author": "Author", - "search.filters.applied.f.author": "AutorIn", + "search.filters.applied.f.author": "Autor:in", // "search.filters.applied.f.dateIssued.max": "End date", "search.filters.applied.f.dateIssued.max": "Enddatum", @@ -4173,7 +4106,7 @@ "search.filters.applied.f.subject": "Thema", // "search.filters.applied.f.submitter": "Submitter", - "search.filters.applied.f.submitter": "Einreichende(r)", + "search.filters.applied.f.submitter": "Einreichende:r", // "search.filters.applied.f.jobTitle": "Job Title", "search.filters.applied.f.jobTitle": "Berufsbezeichnung", @@ -4190,10 +4123,10 @@ // "search.filters.filter.author.head": "Author", - "search.filters.filter.author.head": "AutorIn", + "search.filters.filter.author.head": "Autor:in", // "search.filters.filter.author.placeholder": "Author name", - "search.filters.filter.author.placeholder": "Name des/r AutorIn", + "search.filters.filter.author.placeholder": "Name des/der Autor:in", // "search.filters.filter.birthDate.head": "Birth Date", "search.filters.filter.birthDate.head": "Geburtsdatum", @@ -4208,10 +4141,10 @@ "search.filters.filter.creativeDatePublished.placeholder": "Erscheinungsdatum", // "search.filters.filter.creativeWorkEditor.head": "Editor", - "search.filters.filter.creativeWorkEditor.head": "Herausgeber", + "search.filters.filter.creativeWorkEditor.head": "Herausgeber:in", // "search.filters.filter.creativeWorkEditor.placeholder": "Editor", - "search.filters.filter.creativeWorkEditor.placeholder": "Herausgeber", + "search.filters.filter.creativeWorkEditor.placeholder": "Herausgeber:in", // "search.filters.filter.creativeWorkKeywords.head": "Subject", "search.filters.filter.creativeWorkKeywords.head": "Thema", @@ -4322,10 +4255,10 @@ "search.filters.filter.subject.placeholder": "Thema", // "search.filters.filter.submitter.head": "Submitter", - "search.filters.filter.submitter.head": "Einreichende(r)", + "search.filters.filter.submitter.head": "Einreichende:r", // "search.filters.filter.submitter.placeholder": "Submitter", - "search.filters.filter.submitter.placeholder": "Einreichende(r)", + "search.filters.filter.submitter.placeholder": "Einreichende:r", @@ -4591,10 +4524,10 @@ "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.title": "Import Remote Author", // "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": "Erfolgreicher Import und Hinzufügen eines lokalen Autors zur Auswahl", + "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.local-entity": "Erfolgreicher Import und Hinzufügen eines/einer lokalen Autor:in zur Auswahl", // "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": "Erfolgreicher Import und Hinzufügen eines externen Autors zur Auswahl", + "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.new-entity": "Erfolgreicher Import und Hinzufügen eines/einer externen Autor:in zur Auswahl", // "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Authority", "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Referenz", @@ -4756,12 +4689,10 @@ "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", - // TODO New key - Add a translation "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingAgencyOfPublication": "Suche nach Fördereinrichtungen", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingOfPublication": "Search for Funding", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingOfPublication": "Search for Funding", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isFundingOfPublication": "Suche nach Förderungen", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isChildOrgUnitOf": "Search for Organizational Units", "submission.sections.describe.relationship-lookup.search-tab.tab-title.isChildOrgUnitOf": "Suche nach Organisationseinheiten", @@ -4783,7 +4714,7 @@ "submission.sections.describe.relationship-lookup.title.isJournalOfPublication": "Zeitschriften", // "submission.sections.describe.relationship-lookup.title.isAuthorOfPublication": "Authors", - "submission.sections.describe.relationship-lookup.title.isAuthorOfPublication": "AutorInnen", + "submission.sections.describe.relationship-lookup.title.isAuthorOfPublication": "Autor:innen", // "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfPublication": "Funding Agency", "submission.sections.describe.relationship-lookup.title.isFundingAgencyOfPublication": "Fördereinrichtung", @@ -4794,14 +4725,13 @@ "submission.sections.describe.relationship-lookup.title.Publication": "Publikationen", // "submission.sections.describe.relationship-lookup.title.Person": "Authors", - "submission.sections.describe.relationship-lookup.title.Person": "AutorInnen", + "submission.sections.describe.relationship-lookup.title.Person": "Autor:innen", // "submission.sections.describe.relationship-lookup.title.OrgUnit": "Organizational Units", "submission.sections.describe.relationship-lookup.title.OrgUnit": "Organisationseinheiten", // "submission.sections.describe.relationship-lookup.title.DataPackage": "Data Packages", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.title.DataPackage": "Data Packages", + "submission.sections.describe.relationship-lookup.title.DataPackage": "Datenpakete", // "submission.sections.describe.relationship-lookup.title.DataFile": "Data Files", // TODO New key - Add a translation @@ -4811,12 +4741,10 @@ "submission.sections.describe.relationship-lookup.title.Funding Agency": "Fördereinrichtung", // "submission.sections.describe.relationship-lookup.title.isFundingOfPublication": "Funding", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.title.isFundingOfPublication": "Funding", + "submission.sections.describe.relationship-lookup.title.isFundingOfPublication": "Förderung", // "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Parent Organizational Unit", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Parent Organizational Unit", + "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Übergeordnete Organisationeinheit", // "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown", "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Liste umschalten", @@ -4828,13 +4756,14 @@ "submission.sections.describe.relationship-lookup.selection-tab.no-selection": "Ihre Auswahl ist momentan leer.", // "submission.sections.describe.relationship-lookup.selection-tab.title.isAuthorOfPublication": "Selected Authors", - "submission.sections.describe.relationship-lookup.selection-tab.title.isAuthorOfPublication": "Ausgewählte AutorInnen", + "submission.sections.describe.relationship-lookup.selection-tab.title.isAuthorOfPublication": "Ausgewählte Autor:innen", // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalOfPublication": "Selected Journals", "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalOfPublication": "Ausgewählte Zeitschriften", // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalVolumeOfPublication": "Selected Journal Volume", "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalVolumeOfPublication": "Ausgewählte Zeitschriftenbände", + // "submission.sections.describe.relationship-lookup.selection-tab.title.Project": "Selected Projects", "submission.sections.describe.relationship-lookup.selection-tab.title.Project": "Ausgewählte Projekte", @@ -4842,14 +4771,13 @@ "submission.sections.describe.relationship-lookup.selection-tab.title.Publication": "Ausgewählte Publikationen", // "submission.sections.describe.relationship-lookup.selection-tab.title.Person": "Selected Authors", - "submission.sections.describe.relationship-lookup.selection-tab.title.Person": "Ausgewählte AutorInnen", + "submission.sections.describe.relationship-lookup.selection-tab.title.Person": "Ausgewählte Autor:innen", // "submission.sections.describe.relationship-lookup.selection-tab.title.OrgUnit": "Selected Organizational Units", "submission.sections.describe.relationship-lookup.selection-tab.title.OrgUnit": "Ausgewählte Organisationseinheiten", // "submission.sections.describe.relationship-lookup.selection-tab.title.DataPackage": "Selected Data Packages", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.selection-tab.title.DataPackage": "Selected Data Packages", + "submission.sections.describe.relationship-lookup.selection-tab.title.DataPackage": "Ausgewählte Datenpakete", // "submission.sections.describe.relationship-lookup.selection-tab.title.DataFile": "Selected Data Files", // TODO New key - Add a translation @@ -4860,6 +4788,7 @@ // "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalIssueOfPublication": "Selected Issue", "submission.sections.describe.relationship-lookup.selection-tab.title.isJournalIssueOfPublication": "Ausgewählte Ausgaben", + // "submission.sections.describe.relationship-lookup.selection-tab.title.JournalVolume": "Selected Journal Volume", "submission.sections.describe.relationship-lookup.selection-tab.title.JournalVolume": "Ausgewählte Zeitschriftenbände", @@ -4867,8 +4796,8 @@ "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingAgencyOfPublication": "Ausgewählte Fördereinrichtung", // "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingOfPublication": "Selected Funding", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingOfPublication": "Selected Funding", + "submission.sections.describe.relationship-lookup.selection-tab.title.isFundingOfPublication": "Ausgewählte Förderung", + // "submission.sections.describe.relationship-lookup.selection-tab.title.JournalIssue": "Selected Issue", "submission.sections.describe.relationship-lookup.selection-tab.title.JournalIssue": "Ausgewählte Ausgabe", @@ -5110,7 +5039,7 @@ "submission.workflow.tasks.claimed.edit_help": "Wählen Sie diese Option, um die Metadaten der Ressource zu bearbeiten.", // "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": "Bitte geben Sie den Grund für die Ablehnung der eingereichten Ressource in das Feld unten ein. Bitte geben Sie an ob und wie der/die Einreichenden das Problem beheben und die Ressource erneut einreichen kann.", + "submission.workflow.tasks.claimed.reject.reason.info": "Bitte geben Sie den Grund für die Ablehnung der eingereichten Ressource in das Feld unten ein. Bitte geben Sie an ob und wie der/die Einreichende:n das Problem beheben und die Ressource erneut einreichen kann.", // "submission.workflow.tasks.claimed.reject.reason.placeholder": "Describe the reason of reject", "submission.workflow.tasks.claimed.reject.reason.placeholder": "Beschreiben Sie den Grund für die Ablehnung", @@ -5142,7 +5071,7 @@ "submission.workflow.tasks.generic.processing": "Verarbeitung läuft...", // "submission.workflow.tasks.generic.submitter": "Submitter", - "submission.workflow.tasks.generic.submitter": "Einreichende(r)", + "submission.workflow.tasks.generic.submitter": "Einreichende:r", // "submission.workflow.tasks.generic.success": "Operation successful", "submission.workflow.tasks.generic.success": "Aktion erfolgreich", @@ -5169,32 +5098,25 @@ // "vocabulary-treeview.header": "Hierarchical tree view", - // TODO New key - Add a translation - "vocabulary-treeview.header": "Hierarchical tree view", + "vocabulary-treeview.header": "Hierarchische Baumstruktur", // "vocabulary-treeview.load-more": "Load more", - // TODO New key - Add a translation - "vocabulary-treeview.load-more": "Load more", + "vocabulary-treeview.load-more": "Mehr laden", // "vocabulary-treeview.search.form.reset": "Reset", - // TODO New key - Add a translation - "vocabulary-treeview.search.form.reset": "Reset", + "vocabulary-treeview.search.form.reset": "Zurücksetzen", // "vocabulary-treeview.search.form.search": "Search", - // TODO New key - Add a translation - "vocabulary-treeview.search.form.search": "Search", + "vocabulary-treeview.search.form.search": "Suche", // "vocabulary-treeview.search.no-result": "There were no items to show", - // TODO New key - Add a translation - "vocabulary-treeview.search.no-result": "There were no items to show", + "vocabulary-treeview.search.no-result": "Es gibt keine Items zu zeigen", // "vocabulary-treeview.tree.description.nsi": "The Norwegian Science Index", - // TODO New key - Add a translation - "vocabulary-treeview.tree.description.nsi": "The Norwegian Science Index", + "vocabulary-treeview.tree.description.nsi": "Der norwegische Wissenschaftsindex", // "vocabulary-treeview.tree.description.srsc": "Research Subject Categories", - // TODO New key - Add a translation - "vocabulary-treeview.tree.description.srsc": "Research Subject Categories", + "vocabulary-treeview.tree.description.srsc": "Kategorien von Forschungsthemen", @@ -5205,8 +5127,7 @@ "uploader.drag-message": "Ziehen Sie Ihre Dateien hierhin", // "uploader.or": ", or ", - // TODO Source message changed - Revise the translation - "uploader.or": " oder", + "uploader.or": ", oder", // "uploader.processing": "Processing", "uploader.processing": "Bearbeitung läuft", @@ -5226,74 +5147,57 @@ // "workflowAdmin.search.results.head": "Administer Workflow", - // TODO New key - Add a translation - "workflowAdmin.search.results.head": "Administer Workflow", + "workflowAdmin.search.results.head": "Workflow verwalten", // "workflow-item.delete.notification.success.title": "Deleted", - // TODO New key - Add a translation - "workflow-item.delete.notification.success.title": "Deleted", + "workflow-item.delete.notification.success.title": "Gelöscht", // "workflow-item.delete.notification.success.content": "This workflow item was successfully deleted", - // TODO New key - Add a translation - "workflow-item.delete.notification.success.content": "This workflow item was successfully deleted", + "workflow-item.delete.notification.success.content": "Dieses Workflow-Item wurde erfolgreich gelöscht", // "workflow-item.delete.notification.error.title": "Something went wrong", - // TODO New key - Add a translation - "workflow-item.delete.notification.error.title": "Something went wrong", + "workflow-item.delete.notification.error.title": "Etwas ist schief gelaufen", // "workflow-item.delete.notification.error.content": "The workflow item could not be deleted", - // TODO New key - Add a translation - "workflow-item.delete.notification.error.content": "The workflow item could not be deleted", + "workflow-item.delete.notification.error.content": "Das Workflow-Item konnte nicht gelöscht werden", // "workflow-item.delete.title": "Delete workflow item", - // TODO New key - Add a translation - "workflow-item.delete.title": "Delete workflow item", + "workflow-item.delete.title": "Workflow-Item löschen", // "workflow-item.delete.header": "Delete workflow item", - // TODO New key - Add a translation - "workflow-item.delete.header": "Delete workflow item", + "workflow-item.delete.header": "Workflow-Item löschen", // "workflow-item.delete.button.cancel": "Cancel", - // TODO New key - Add a translation - "workflow-item.delete.button.cancel": "Cancel", + "workflow-item.delete.button.cancel": "Abbrechen", // "workflow-item.delete.button.confirm": "Delete", - // TODO New key - Add a translation - "workflow-item.delete.button.confirm": "Delete", + "workflow-item.delete.button.confirm": "Löschen", // "workflow-item.send-back.notification.success.title": "Sent back to submitter", - // TODO New key - Add a translation - "workflow-item.send-back.notification.success.title": "Sent back to submitter", + "workflow-item.send-back.notification.success.title": "An die/den Einreichende:n zurückgeschickt", // "workflow-item.send-back.notification.success.content": "This workflow item was successfully sent back to the submitter", - // TODO New key - Add a translation - "workflow-item.send-back.notification.success.content": "This workflow item was successfully sent back to the submitter", + "workflow-item.send-back.notification.success.content": "Dieses Workflow-Item wurde erfolgreich an die/den Einreichende:n zurückgeschickt", // "workflow-item.send-back.notification.error.title": "Something went wrong", - // TODO New key - Add a translation - "workflow-item.send-back.notification.error.title": "Something went wrong", + "workflow-item.send-back.notification.error.title": "Etwas ist schief gelaufen", // "workflow-item.send-back.notification.error.content": "The workflow item could not be sent back to the submitter", - // TODO New key - Add a translation - "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": "Das Workflow-Item konnte nicht an die/den Einreichende:n zurückgeschickt werden", // "workflow-item.send-back.title": "Send workflow item back to submitter", - // TODO New key - Add a translation - "workflow-item.send-back.title": "Send workflow item back to submitter", + "workflow-item.send-back.title": "Workflow-Item an die/den Einreichende:n zurücksenden", // "workflow-item.send-back.header": "Send workflow item back to submitter", - // TODO New key - Add a translation - "workflow-item.send-back.header": "Send workflow item back to submitter", + "workflow-item.send-back.header": "Workflow-Item an die/den Einreichende:n zurücksenden", // "workflow-item.send-back.button.cancel": "Cancel", - // TODO New key - Add a translation - "workflow-item.send-back.button.cancel": "Cancel", + "workflow-item.send-back.button.cancel": "Abbrechen", // "workflow-item.send-back.button.confirm": "Send back" - // TODO New key - Add a translation - "workflow-item.send-back.button.confirm": "Send back" + "workflow-item.send-back.button.confirm": "Zurücksenden" } From 3bbd2d9d6405b802b9a020533c6c9dc9e37ecaa5 Mon Sep 17 00:00:00 2001 From: YPaulsen-TLC Date: Fri, 14 Jan 2022 14:05:41 +0100 Subject: [PATCH 072/570] message key translations Final German message keys translations for DSpace 7, --- src/assets/i18n/de.json5 | 454 +++++++++++++++++---------------------- 1 file changed, 194 insertions(+), 260 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 77105cc7aa..d3fc1131aa 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -483,8 +483,7 @@ "admin.access-control.groups.form.alert.permanent": "Diese Gruppe ist permanent, das heißt sie kann nicht bearbeitet oder gelöscht werden. Sie können jedoch über diese Seite Gruppenmitglieder hinzufügen und entfernen.", // "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.", - // TODO New key - Add a translation - "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": "Diese Gruppe kann nicht geändert oder gelöscht werden, da sie einer Rolle im Einreichungs- und Workflow-Prozess im \"{{name}}\" {{comcol}} zugeordnet ist. Sie können sie über die Registerkarte \"assign roles\" auf der Bearbeitungsseite {{comcol}} löschen. Sie können über diese Seite weiterhin Gruppenmitglieder hinzufügen und entfernen.", // "admin.access-control.groups.form.head.create": "Create group", "admin.access-control.groups.form.head.create": "Gruppe erstellen", @@ -766,9 +765,8 @@ "bitstream.edit.form.description.label": "Beschreibung", // "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.", - // TODO New key - Add a translation - "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.", - // Der erste Tag, ab wann der Zugriff möglich ist. Dieses Datum kann in dieser Form nicht verändert werden. Um eine Embargo-Frist für eine Datei festzulegen, zu dem ()-Tab gehen, auf () klicken, die ()-Richtline für die Datei erstellen oder verändern und das Startdatum wie gewünscht einstellen. + "bitstream.edit.form.embargo.hint": "Der erste Tag, ab dem der Zugang erlaubt ist. This date cannot be modified on this form. Um eine Embargo-Frist für eine Datei festzulegen, zu Item Status gehen, auf Authorizations... klicken, die Richtlinie READ für die Datei erstellen oder verändern und das Start Date wie gewünscht einstellen.", + // "bitstream.edit.form.embargo.label": "Embargo until specific date", "bitstream.edit.form.embargo.label": "Embargo bis zu einem bestimmten Datum", @@ -827,7 +825,7 @@ "browse.comcol.head": "Listen", // "browse.empty": "No items to show.", - "browse.empty": "Es gibt keine Dokumente, die angezeigt werden können.", + "browse.empty": "Es gibt keine Items, die angezeigt werden können.", // "browse.metadata.author": "Author", "browse.metadata.author": "Autor:in", @@ -963,10 +961,10 @@ // "collection.edit.tabs.mapper.head": "Item Mapper", - "collection.edit.tabs.mapper.head": "Ressource spiegeln", + "collection.edit.tabs.mapper.head": "Item spiegeln", // "collection.edit.tabs.item-mapper.title": "Collection Edit - Item Mapper", - "collection.edit.tabs.item-mapper.title": "Sammlung bearbeiten - Ressource spiegeln", + "collection.edit.tabs.item-mapper.title": "Sammlung bearbeiten - Item spiegeln", // "collection.edit.item-mapper.cancel": "Cancel", "collection.edit.item-mapper.cancel": "Abbrechen", @@ -975,13 +973,13 @@ "collection.edit.item-mapper.collection": "Sammlung: \"{{name}}\"", // "collection.edit.item-mapper.confirm": "Map selected items", - "collection.edit.item-mapper.confirm": "Ausgewählte Ressourcen spiegeln", + "collection.edit.item-mapper.confirm": "Ausgewählte Items spiegeln", // "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": "Sammlungsadministrator:innen haben die Möglichkeit Ressourcen von einer Sammlung in eine andere zu spiegeln. Man kann nach Ressourcen in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Ressourcen anzeigen lassen.", + "collection.edit.item-mapper.description": "Sammlungsadministrator:innen haben die Möglichkeit Items von einer Sammlung in eine andere zu spiegeln. Man kann nach Items in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Items anzeigen lassen.", // "collection.edit.item-mapper.head": "Item Mapper - Map Items from Other Collections", - "collection.edit.item-mapper.head": "Ressourcen spiegeln - Spiegelt Ressourcen aus anderen Sammlungen", + "collection.edit.item-mapper.head": "Item spiegeln - Spiegelt Items aus anderen Sammlungen", // "collection.edit.item-mapper.no-search": "Please enter a query to search", "collection.edit.item-mapper.no-search": "Bitte geben Sie eine Suchanfrage ein.", @@ -993,13 +991,13 @@ "collection.edit.item-mapper.notifications.map.error.head": "Fehler beim Spiegeln", // "collection.edit.item-mapper.notifications.map.success.content": "Successfully mapped {{amount}} items.", - "collection.edit.item-mapper.notifications.map.success.content": "{{amount}} Ressource(n) erfolgreich gespiegelt.", + "collection.edit.item-mapper.notifications.map.success.content": "{{amount}} Item(s) erfolgreich gespiegelt.", // "collection.edit.item-mapper.notifications.map.success.head": "Mapping completed", "collection.edit.item-mapper.notifications.map.success.head": "Spiegelung abgeschlossen", // "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": "Beim Spiegeln von {{amount}} Ressource(n) ist ein Fehler aufgetreten.", + "collection.edit.item-mapper.notifications.unmap.error.content": "Beim Spiegeln von {{amount}} Item(s) ist ein Fehler aufgetreten.", // "collection.edit.item-mapper.notifications.unmap.error.head": "Remove mapping errors", "collection.edit.item-mapper.notifications.unmap.error.head": "Spiegeln Entfernen Fehler", @@ -1011,13 +1009,13 @@ "collection.edit.item-mapper.notifications.unmap.success.head": "Spiegelung entfernen abgeschlossen", // "collection.edit.item-mapper.remove": "Remove selected item mappings", - "collection.edit.item-mapper.remove": "Spiegelung der gewählten Ressourcen entfernen.", + "collection.edit.item-mapper.remove": "Spiegelung der gewählten Items entfernen.", // "collection.edit.item-mapper.tabs.browse": "Browse mapped items", - "collection.edit.item-mapper.tabs.browse": "Gespiegelte Ressourcen auflisten", + "collection.edit.item-mapper.tabs.browse": "Gespiegelte Items auflisten", // "collection.edit.item-mapper.tabs.map": "Map new items", - "collection.edit.item-mapper.tabs.map": "Neue Ressourcen spiegeln", + "collection.edit.item-mapper.tabs.map": "Neue Items spiegeln", @@ -1062,8 +1060,7 @@ "collection.edit.tabs.authorizations.head": "Berechtigungen", // "collection.edit.tabs.authorizations.title": "Collection Edit - Authorizations", - // TODO New key - Add a translation - "collection.edit.tabs.authorizations.title": "Collection Edit - Authorizations", + "collection.edit.tabs.authorizations.title": "Sammlung bearbeiten - Berechtigungen", // "collection.edit.tabs.metadata.head": "Edit Metadata", "collection.edit.tabs.metadata.head": "Metadaten bearbeiten", @@ -1149,20 +1146,19 @@ "collection.edit.template.edit-button": "Bearbeiten", // "collection.edit.template.head": "Edit Template Item for Collection \"{{ collection }}\"", - // TODO New key - Add a translation - "collection.edit.template.head": "Itemvorlage für Collection \"{{ collection }}\" bearbeiten", + "collection.edit.template.head": "Itemvorlage für Sammlung \"{{ collection }}\" bearbeiten", // "collection.edit.template.label": "Template item", "collection.edit.template.label": "Itemvorlage", // "collection.edit.template.notifications.delete.error": "Failed to delete the item template", - "collection.edit.template.notifications.delete.error": "Löschen der Itemvorlage fehlgeschlagen", + "collection.edit.template.notifications.delete.error": "Löschen der Metadatenvorlage fehlgeschlagen", // "collection.edit.template.notifications.delete.success": "Successfully deleted the item template", - "collection.edit.template.notifications.delete.success": "Die Itemvorlage wurde erfolgreich gelöscht", + "collection.edit.template.notifications.delete.success": "Die Metadatenvorlage wurde erfolgreich gelöscht", // "collection.edit.template.title": "Edit Template Item", - "collection.edit.template.title": "Itemvorlage bearbeiten", + "collection.edit.template.title": "Metadatenvorlage bearbeiten", @@ -1201,7 +1197,7 @@ "collection.page.browse.recent.head": "Neueste Veröffentlichungen", // "collection.page.browse.recent.empty": "No items to show", - "collection.page.browse.recent.empty": "Es gibt keine Ressourcen zum Anzeigen", + "collection.page.browse.recent.empty": "Es gibt keine Items zum Anzeigen", // "collection.page.edit": "Edit this collection", "collection.page.edit": "Diese Sammlung bearbeiten", @@ -1362,8 +1358,7 @@ "comcol-role.edit.create": "Erstellen", // "comcol-role.edit.restrict": "Restrict", - // TODO New key - Add a translation - "comcol-role.edit.restrict": "Restrict", + "comcol-role.edit.restrict": "Beschränken", // "comcol-role.edit.delete": "Delete", "comcol-role.edit.delete": "Löschen", @@ -1377,29 +1372,24 @@ // "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": "Community-Administrator:innen können Teilbereiche oder Sammlungen erstellen, verwalten oder die Verwaltung anderen zuweisen. Darüber hinaus entscheiden sie, wer Items zu einer Teilsammlung einreichen, Item-Metadaten bearbeiten (nach der Einreichung) und bestehende Items von anderen Sammlungen verknüpfen kann (vorbehaltlich einer Berechtigung).", // "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": "Sammlungsadministrator:innen entscheiden, wer Items zu einer Sammlung einreichen, Item-Metadaten bearbeiten (nach der Einreichung) and und bestehende Items von anderen Sammlungen mit dieser Sammlung verknüpfen kann (vorbehaltlich einer Berechtigung für diese Sammlung).", // "comcol-role.edit.submitters.name": "Submitters", "comcol-role.edit.submitters.name": "Einreichende", // "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": "Die Personen und Gruppen, die die Berechtigungen haben, neue Items zu dieser Sammlung einzureichen.", // "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": "Standard-Lesezugriff 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.", - // 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": "Personen und Gruppen, die neu eingereichte Items dieser Sammlung lesen können. Änderungen an dieser Rolle sind nicht rückwirkend. Bestehende Items im System können weiterhin von denjenigen eingesehen werden, die zum Zeitpunkt des Hinzufügens Lesezugriff hatten.", // "comcol-role.edit.item_read.anonymous-group": "Default read for incoming items is currently set to Anonymous.", "comcol-role.edit.item_read.anonymous-group": "Die Standardeinstellung für das Lesen eingehender Items ist derzeit auf Anonym eingestellt.", @@ -1409,8 +1399,7 @@ "comcol-role.edit.bitstream_read.name": "Standard-Lesezugriff Datei", // "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": "Community-Administrator:innen können Teilbereiche oder Sammlungen erstellen, verwalten oder die Verwaltung anderen zuweisen. Darüber hinaus entscheiden sie, wer Items zu einer Teilsammlung einreichen, Item-Metadaten bearbeiten (nach der Einreichung) und bestehende Items von anderen Sammlungen verknüpfen kann (vorbehaltlich einer Berechtigung).", // "comcol-role.edit.bitstream_read.anonymous-group": "Default read for incoming bitstreams is currently set to Anonymous.", "comcol-role.edit.bitstream_read.anonymous-group": "Die Standardeinstellung für das Lesen eingehender Dateien ist derzeit auf Anonym eingestellt.", @@ -1486,11 +1475,9 @@ "cookies.consent.accept-selected": "Ausgewählte annehmen", // "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": "Diese App wird standardmäßig geladen (Sie können dies jedoch deaktivieren)", // "cookies.consent.app.opt-out.title": "(opt-out)", - // TODO New key - Add a translation "cookies.consent.app.opt-out.title": "(opt-out)", // "cookies.consent.app.purpose": "purpose", @@ -1512,8 +1499,7 @@ "cookies.consent.decline": "Ablehnen", // "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}.", - // TODO New key - Add a translation - "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": "Wir sammeln und verarbeiten Ihre personenbezogenen Daten für die folgenden Zwecke: Authentication, Preferences, Acknowledgement and Statistics.
    Um mehr zu erfahren, lesen Sie bitte unsere {privacyPolicy}.", // "cookies.consent.content-notice.learnMore": "Customize", "cookies.consent.content-notice.learnMore": "Anpassen", @@ -1574,12 +1560,10 @@ "curation-task.task.checklinks.label": "Links in Metadaten prüfen", // "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", - // TODO New key - Add a translation - "curation-task.task.profileformats.label": "Profile Bitstream Formats", + "curation-task.task.profileformats.label": "Dateiformate auflisten", // "curation-task.task.requiredmetadata.label": "Check for Required Metadata", "curation-task.task.requiredmetadata.label": "Prüfung auf erforderliche Metadaten", @@ -1614,8 +1598,7 @@ "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)", - // TODO New key - Add a translation - "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": "Hinweis: Geben Sie [your-handle-prefix]/0 ein, um eine Aufgabe für die gesamte Seite durchzuführen (nicht alle Aufgaben unterstützen diese Funktion)", @@ -1635,7 +1618,7 @@ "dso-selector.create.community.top-level": "Einen neuen Bereich auf oberster Ebene anlgen", // "dso-selector.create.item.head": "New item", - "dso-selector.create.item.head": "Neue Ressource", + "dso-selector.create.item.head": "Neues Item", // "dso-selector.create.item.sub-level": "Create a new item in", "dso-selector.create.item.sub-level": "Ein neues Item erstellen in", @@ -1650,7 +1633,7 @@ "dso-selector.edit.community.head": "Bereich bearbeiten", // "dso-selector.edit.item.head": "Edit item", - "dso-selector.edit.item.head": "Ressource bearbeiten", + "dso-selector.edit.item.head": "Item bearbeiten", // "dso-selector.export-metadata.dspaceobject.head": "Export metadata from", "dso-selector.export-metadata.dspaceobject.head": "Exportieren der Metadaten aus", @@ -1689,11 +1672,10 @@ // "error.bitstream": "Error fetching bitstream", - // TODO New key - Add a translation - "error.bitstream": "Error fetching bitstream", + "error.bitstream": "Fehler beim Laden der Datei", // "error.browse-by": "Error fetching items", - "error.browse-by": "Fehler beim Laden der Ressourcen", + "error.browse-by": "Fehler beim Laden der Items", // "error.collection": "Error fetching collection", "error.collection": "Fehler beim Laden der Sammlung", @@ -1705,16 +1687,16 @@ "error.community": "Fehler beim Laden des Bereichs", // "error.identifier": "No item found for the identifier", - "error.identifier": "Zu dieser Kennung wurde keine Ressource gefunden", + "error.identifier": "Zu dieser Kennung wurde kein Item gefunden", // "error.default": "Error", "error.default": "Fehler", // "error.item": "Error fetching item", - "error.item": "Fehler beim Laden der Ressource", + "error.item": "Fehler beim Laden des Items", // "error.items": "Error fetching items", - "error.items": "Fehler beim Laden der Ressourcen", + "error.items": "Fehler beim Laden der Items", // "error.objects": "Error fetching objects", "error.objects": "Fehler beim Laden der Objekte", @@ -1777,8 +1759,7 @@ "forgot-email.form.header": "Passwort vergessen", // "forgot-email.form.info": "Enter Register an account to subscribe to collections for email updates, and submit new items to DSpace.", - // TODO New key - Add a translation - "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": "Geben Sie einen Account ein, um Sammlungen für E-Mail-Updates zu abonnieren und neue Items an DSpace zu übermitteln.", // "forgot-email.form.email": "Email Address *", "forgot-email.form.email": "E-Mail-Addresse *", @@ -1799,14 +1780,12 @@ "forgot-email.form.success.head": "Verifizierungs-E-Mail gesendet", // "forgot-email.form.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", - // TODO New key - Add a translation - "forgot-email.form.success.content": "An email has been sent to {{ email }} containing a special URL and further instructions.", + "forgot-email.form.success.content": "Es wurde eine E-Mail mit einer speziellen URL und weiteren Anweisungen an {{ email }} gesendet.", // "forgot-email.form.error.head": "Error when trying to register email", "forgot-email.form.error.head": "Fehler beim Versuch, die E-Mail zu registrieren", // "forgot-email.form.error.content": "An error occured when registering the following email address: {{ email }}", - // TODO New key - Add a translation "forgot-email.form.error.content": "Bei der Registrierung der folgenden E-Mail-Adresse ist ein Fehler aufgetreten: {{ email }}", @@ -1999,8 +1978,7 @@ // "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.", - // TODO New key - Add a translation - "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": "Mit diesem Editor können Sie die Richtlinien eines Items anzeigen und ändern sowie die Richtlinien der einzelnen Itemkomponenten, d. h. Bündel und Dateien, ändern. Kurz gesagt, ein Item ist ein Container von Bündeln, und Bündel sind Container von Dateien. Container haben normalerweise ADD/REMOVE/READ/WRITE-Richtlinien, während Dateien nur READ/WRITE-Richtlinien haben.", // "item.edit.authorizations.title": "Edit item's Policies", "item.edit.authorizations.title": "Item-Richtlinien bearbeiten", @@ -2025,8 +2003,7 @@ "item.bitstreams.upload.bundle.new": "Bündel erstellen", // "item.bitstreams.upload.bundles.empty": "This item doesn\'t contain any bundles to upload a bitstream to.", - // TODO New key - Add a translation - "item.bitstreams.upload.bundles.empty": "This item doesn\'t contain any bundles to upload a bitstream to.", + "item.bitstreams.upload.bundles.empty": "Dieses Item enthält keine Bündel, um eine Datei hochzuladen.", // "item.bitstreams.upload.cancel": "Cancel", "item.bitstreams.upload.cancel": "Abbrechen", @@ -2055,8 +2032,7 @@ "item.edit.bitstreams.bundle.edit.buttons.upload": "Hochladen", // "item.edit.bitstreams.bundle.displaying": "Currently displaying {{ amount }} bitstreams of {{ total }}.", - // TODO New key - Add a translation - "item.edit.bitstreams.bundle.displaying": "Currently displaying {{ amount }} bitstreams of {{ total }}.", + "item.edit.bitstreams.bundle.displaying": "Derzeit werden {{ amount }} Dateien von insgesamt {{ total }} angezeigt.", // "item.edit.bitstreams.bundle.load.all": "Load all ({{ total }})", "item.edit.bitstreams.bundle.load.all": "Alle ({{ total }}) laden", @@ -2086,7 +2062,7 @@ "item.edit.bitstreams.edit.buttons.undo": "Änderungen rückgängig machen", // "item.edit.bitstreams.empty": "This item doesn't contain any bitstreams. Click the upload button to create one.", - "item.edit.bitstreams.empty": "Diese Ressource enthält keine Dateien. Klicken Sie auf das Feld Hochladen, um eine zu erstellen.", + "item.edit.bitstreams.empty": "Dieses Item enthält keine Dateien. Klicken Sie auf das Feld Hochladen, um eine zu erstellen.", // "item.edit.bitstreams.headers.actions": "Actions", "item.edit.bitstreams.headers.actions": "Aktionen", @@ -2113,15 +2089,13 @@ "item.edit.bitstreams.notifications.move.failed.title": "Fehler beim Verschieben von Dateien", // "item.edit.bitstreams.notifications.move.saved.content": "Your move changes to this item's bitstreams and bundles have been saved.", - // TODO New key - Add a translation - "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": "Die von Ihnen vorgenommenen Änderungen an den Dateien und Bündeln dieses Artikels wurden gespeichert.", // "item.edit.bitstreams.notifications.move.saved.title": "Move changes saved", - // TODO New key - Add a translation - "item.edit.bitstreams.notifications.move.saved.title": "Move changes saved", + "item.edit.bitstreams.notifications.move.saved.title": "Änderungen gespeichert", // "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": "Die Ressource, an der Sie gerade arbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", + "item.edit.bitstreams.notifications.outdated.content": "Das Item, an der Sie gerade arbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", // "item.edit.bitstreams.notifications.outdated.title": "Changes outdated", "item.edit.bitstreams.notifications.outdated.title": "Veraltete Änderungen", @@ -2130,12 +2104,10 @@ "item.edit.bitstreams.notifications.remove.failed.title": "Fehler beim Löschen der Datei", // "item.edit.bitstreams.notifications.remove.saved.content": "Your removal changes to this item's bitstreams have been saved.", - // TODO New key - Add a translation - "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": "Ihre Entfernungsänderungen an den Dateien dieses Items wurden gespeichert.", // "item.edit.bitstreams.notifications.remove.saved.title": "Removal changes saved", - // TODO New key - Add a translation - "item.edit.bitstreams.notifications.remove.saved.title": "Removal changes saved", + "item.edit.bitstreams.notifications.remove.saved.title": "Änderungen gespeichert", // "item.edit.bitstreams.reinstate-button": "Undo", "item.edit.bitstreams.reinstate-button": "Rückgängig", @@ -2155,59 +2127,59 @@ "item.edit.delete.confirm": "Löschen", // "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": "Sind Sie sicher, dass diese Ressource komplett gelöscht werden soll. Warnung: Zur Zeit wird kein Grabstein hinterlassen", + "item.edit.delete.description": "Sind Sie sicher, dass dieses Item komplett gelöscht werden soll. Warnung: Zur Zeit wird kein Grabstein hinterlassen", // "item.edit.delete.error": "An error occurred while deleting the item", "item.edit.delete.error": "Beim Löschen der Ressource ist ein Fehler aufgetreten", // "item.edit.delete.header": "Delete item: {{ id }}", - "item.edit.delete.header": "Löschen der Ressource: {{ id }}", + "item.edit.delete.header": "Löschen des Items: {{ id }}", // "item.edit.delete.success": "The item has been deleted", - "item.edit.delete.success": "Die Ressource wurde gelöscht", + "item.edit.delete.success": "Das Item wurde gelöscht", // "item.edit.head": "Edit Item", - "item.edit.head": "Ressource bearbeiten", + "item.edit.head": "Item bearbeiten", // "item.edit.breadcrumbs": "Edit Item", - "item.edit.breadcrumbs": "Ressource bearbeiten", + "item.edit.breadcrumbs": "Item bearbeiten", // "item.edit.tabs.mapper.head": "Collection Mapper", "item.edit.tabs.mapper.head": "Spiegeln", // "item.edit.tabs.item-mapper.title": "Item Edit - Collection Mapper", - "item.edit.tabs.item-mapper.title": "Ressource bearbeiten - Spiegeln", + "item.edit.tabs.item-mapper.title": "Item bearbeiten - Spiegeln", // "item.edit.item-mapper.buttons.add": "Map item to selected collections", - "item.edit.item-mapper.buttons.add": "Ressource in die gewählte(n) Sammlung(en) spiegeln", + "item.edit.item-mapper.buttons.add": "Item in die gewählte(n) Sammlung(en) spiegeln", // "item.edit.item-mapper.buttons.remove": "Remove item's mapping for selected collections", - "item.edit.item-mapper.buttons.remove": "Spiegelung der Ressource aus der/den gewählten Sammlung(en) entfernen", + "item.edit.item-mapper.buttons.remove": "Spiegelung des Items aus der/den gewählten Sammlung(en) entfernen", // "item.edit.item-mapper.cancel": "Cancel", "item.edit.item-mapper.cancel": "Abbrechen", // "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": "Sammlungsadministrator:innen haben die Möglichkeit Ressourcen von einer Sammlung in eine andere zu spiegeln. Man kann nach Ressourcen in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Ressourcen anzeigen lassen.", + "item.edit.item-mapper.description": "Sammlungsadministrator:innen haben die Möglichkeit Items von einer Sammlung in eine andere zu spiegeln. Man kann nach Items in anderen Sammlungen suchen und diese spiegeln oder sich eine Liste der gespiegelten Items anzeigen lassen.", // "item.edit.item-mapper.head": "Item Mapper - Map Item to Collections", - "item.edit.item-mapper.head": "Ressourcen spiegeln - Spiegelt eine Ressource in andere Sammlungen", + "item.edit.item-mapper.head": "Item spiegeln - Spiegelt ein Item in andere Sammlungen", // "item.edit.item-mapper.item": "Item: \"{{name}}\"", - "item.edit.item-mapper.item": "Ressource: \"{{name}}\"", + "item.edit.item-mapper.item": "Item: \"{{name}}\"", // "item.edit.item-mapper.no-search": "Please enter a query to search", "item.edit.item-mapper.no-search": "Bitte geben Sie einen Suchbegriff ein", // "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": "Beim Spiegeln der Ressource in {{amount}} Sammlung(en) ist ein Fehler aufgetreten.", + "item.edit.item-mapper.notifications.add.error.content": "Beim Spiegeln des Items in {{amount}} Sammlung(en) ist ein Fehler aufgetreten.", // "item.edit.item-mapper.notifications.add.error.head": "Mapping errors", "item.edit.item-mapper.notifications.add.error.head": "Fehler beim Spiegeln", // "item.edit.item-mapper.notifications.add.success.content": "Successfully mapped item to {{amount}} collections.", - "item.edit.item-mapper.notifications.add.success.content": "Ressource erfolgreich in {{amount}} Sammlung(en) gespiegelt.", + "item.edit.item-mapper.notifications.add.success.content": "Item erfolgreich in {{amount}} Sammlung(en) gespiegelt.", // "item.edit.item-mapper.notifications.add.success.head": "Mapping completed", "item.edit.item-mapper.notifications.add.success.head": "Spiegeln abgeschlossen", @@ -2216,10 +2188,10 @@ "item.edit.item-mapper.notifications.remove.error.content": "Beim Entfernen der Spiegelung in {{amount}} Sammlung(en) ist ein Fehler aufgetreten.", // "item.edit.item-mapper.notifications.remove.error.head": "Removal of mapping errors", - "item.edit.item-mapper.notifications.remove.error.head": "Fehler beim Entfernen von gespiegelten Ressourcen", + "item.edit.item-mapper.notifications.remove.error.head": "Fehler beim Entfernen von gespiegelten Items", // "item.edit.item-mapper.notifications.remove.success.content": "Successfully removed mapping of item to {{amount}} collections.", - "item.edit.item-mapper.notifications.remove.success.content": "Spiegelung der Ressource aus {{amount}} Sammlung(en) erfolgreich.", + "item.edit.item-mapper.notifications.remove.success.content": "Spiegelung des Items aus {{amount}} Sammlung(en) erfolgreich.", // "item.edit.item-mapper.notifications.remove.success.head": "Removal of mapping completed", "item.edit.item-mapper.notifications.remove.success.head": "Entfernen der Spiegelung abgeschlossen", @@ -2251,7 +2223,7 @@ "item.edit.metadata.edit.buttons.unedit": "Bearbeitung beenden", // "item.edit.metadata.empty": "The item currently doesn't contain any metadata. Click Add to start adding a metadata value.", - "item.edit.metadata.empty": "Die Ressource enthält derzeit keine Metadaten. Klicken Sie auf Hinzufügen, um einen Metadatenwert hinzuzufügen.", + "item.edit.metadata.empty": "Das Item enthält derzeit keine Metadaten. Klicken Sie auf Hinzufügen, um einen Metadatenwert hinzuzufügen.", // "item.edit.metadata.headers.edit": "Edit", "item.edit.metadata.headers.edit": "Bearbeiten", @@ -2284,13 +2256,13 @@ "item.edit.metadata.notifications.invalid.title": "Metadaten ungültig", // "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": "Die Ressource, an der Sie gerade arbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden nicht angewandt, um Konflikte zu vermeiden.", + "item.edit.metadata.notifications.outdated.content": "Das Item, an dem Sie gerade arbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden nicht angewandt, um Konflikte zu vermeiden.", // "item.edit.metadata.notifications.outdated.title": "Changed outdated", "item.edit.metadata.notifications.outdated.title": "Änderung veraltet", // "item.edit.metadata.notifications.saved.content": "Your changes to this item's metadata were saved.", - "item.edit.metadata.notifications.saved.content": "Ihre Änderungen an den Metadaten der Ressource wurden gespeichert.", + "item.edit.metadata.notifications.saved.content": "Ihre Änderungen an den Metadaten des Items wurden gespeichert.", // "item.edit.metadata.notifications.saved.title": "Metadata saved", "item.edit.metadata.notifications.saved.title": "Metadaten gespeichert", @@ -2321,10 +2293,10 @@ "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.error": "An error occurred when attempting to move the item", - "item.edit.move.error": "Ein Fehler ist beim Verschieben der Ressource aufgetreten", + "item.edit.move.error": "Ein Fehler ist beim Verschieben des Items aufgetreten", // "item.edit.move.head": "Move item: {{id}}", - "item.edit.move.head": "Ressource verschieben: {{id}}", + "item.edit.move.head": "Item verschieben: {{id}}", // "item.edit.move.inheritpolicies.checkbox": "Inherit policies", "item.edit.move.inheritpolicies.checkbox": "Rechte erben", @@ -2342,10 +2314,10 @@ "item.edit.move.search.placeholder": "Geben Sie einen Begriff ein, um nach Sammlungen zu suchen", // "item.edit.move.success": "The item has been moved successfully", - "item.edit.move.success": "Die Ressource wurde erfolgreich verschoben", + "item.edit.move.success": "Das Item wurde erfolgreich verschoben", // "item.edit.move.title": "Move item", - "item.edit.move.title": "Ressource verschieben", + "item.edit.move.title": "Item verschieben", @@ -2356,16 +2328,16 @@ "item.edit.private.confirm": "Privat machen", // "item.edit.private.description": "Are you sure this item should be made private in the archive?", - "item.edit.private.description": "Wollen Sie diese Ressource als privat markieren.", + "item.edit.private.description": "Wollen Sie dieses Item als privat markieren.", // "item.edit.private.error": "An error occurred while making the item private", - "item.edit.private.error": "Ein Fehler ist aufgetreten bei dem Versuch, die Ressource privat zu machen", + "item.edit.private.error": "Ein Fehler ist aufgetreten bei dem Versuch, das Item privat zu machen", // "item.edit.private.header": "Make item private: {{ id }}", - "item.edit.private.header": "Ressource: {{ id }} privat machen", + "item.edit.private.header": "Item: {{ id }} privat machen", // "item.edit.private.success": "The item is now private", - "item.edit.private.success": "Diese Ressource ist nun privat", + "item.edit.private.success": "Dieses Item ist nun privat", @@ -2376,16 +2348,16 @@ "item.edit.public.confirm": "Öffentlich machen", // "item.edit.public.description": "Are you sure this item should be made public in the archive?", - "item.edit.public.description": "Sind Sie sicher, dass diese Ressource im Repositorium öffentlich gemacht werden soll?", + "item.edit.public.description": "Sind Sie sicher, dass dieses Item im Repositorium öffentlich gemacht werden soll?", // "item.edit.public.error": "An error occurred while making the item public", - "item.edit.public.error": "Ein Fehler ist aufgetreten, als die Ressource öffentlich gemacht werden sollte.", + "item.edit.public.error": "Ein Fehler ist aufgetreten, als das Item öffentlich gemacht werden sollte.", // "item.edit.public.header": "Make item public: {{ id }}", - "item.edit.public.header": "Ressource: {{ id }} öffentlich machen", + "item.edit.public.header": "Item: {{ id }} öffentlich machen", // "item.edit.public.success": "The item is now public", - "item.edit.public.success": "Die Ressource ist nun öffentlich", + "item.edit.public.success": "Das Item ist nun öffentlich", @@ -2396,16 +2368,16 @@ "item.edit.reinstate.confirm": "Reinstantiieren", // "item.edit.reinstate.description": "Are you sure this item should be reinstated to the archive?", - "item.edit.reinstate.description": "Sind Sie sicher, dass die Ressource reinstantiiert werden soll?", + "item.edit.reinstate.description": "Sind Sie sicher, dass das Item reinstantiiert werden soll?", // "item.edit.reinstate.error": "An error occurred while reinstating the item", - "item.edit.reinstate.error": "Ein Fehler ist bei der Reinstantiierung der Ressource aufgetreten.", + "item.edit.reinstate.error": "Ein Fehler ist bei der Reinstantiierung des Items aufgetreten.", // "item.edit.reinstate.header": "Reinstate item: {{ id }}", - "item.edit.reinstate.header": "Ressource: {{ id }} reinstantiieren", + "item.edit.reinstate.header": "Item: {{ id }} reinstantiieren", // "item.edit.reinstate.success": "The item was reinstated successfully", - "item.edit.reinstate.success": "Die Ressource wurde erfolgreich reinstantiiert", + "item.edit.reinstate.success": "Das Item wurde erfolgreich reinstantiiert", @@ -2434,13 +2406,13 @@ "item.edit.relationships.notifications.failed.title": "Fehler beim Bearbeiten der Beziehung", // "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": "Die Ressource, die Sie gerade bearbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", + "item.edit.relationships.notifications.outdated.content": "Das Item, das Sie gerade bearbeiten, wurde von einem anderen Account geändert. Ihre aktuellen Änderungen werden verworfen, um Konflikte zu vermeiden.", // "item.edit.relationships.notifications.outdated.title": "Changes outdated", "item.edit.relationships.notifications.outdated.title": "Änderungen veraltet", // "item.edit.relationships.notifications.saved.content": "Your changes to this item's relationships were saved.", - "item.edit.relationships.notifications.saved.content": "Ihre Änderungen an den Beziehungen der Ressource wurden gespeichert.", + "item.edit.relationships.notifications.saved.content": "Ihre Änderungen an den Beziehungen des Items wurden gespeichert.", // "item.edit.relationships.notifications.saved.title": "Relationships saved", "item.edit.relationships.notifications.saved.title": "Beziehungen gespeichert", @@ -2452,40 +2424,39 @@ "item.edit.relationships.save-button": "Speichern", // "item.edit.relationships.no-entity-type": "Add 'dspace.entity.type' metadata to enable relationships for this item", - "item.edit.relationships.no-entity-type": "Füge 'dspace.entity.type' in die Metadaten hinzu, um Beziehungen für diese Ressource zu ermöglichen", + "item.edit.relationships.no-entity-type": "Füge 'dspace.entity.type' in die Metadaten hinzu, um Beziehungen für dieses Item zu ermöglichen", // "item.edit.tabs.bitstreams.head": "Bitstreams", - // TODO Source message changed - Revise the translation "item.edit.tabs.bitstreams.head": "Dateien", // "item.edit.tabs.bitstreams.title": "Item Edit - Bitstreams", - "item.edit.tabs.bitstreams.title": "Ressource bearbeiten - Dateien", + "item.edit.tabs.bitstreams.title": "Item bearbeiten - Dateien", // "item.edit.tabs.curate.head": "Curate", "item.edit.tabs.curate.head": "Pflegen", // "item.edit.tabs.curate.title": "Item Edit - Curate", - "item.edit.tabs.curate.title": "Ressource bearbeiten - Pflegen", + "item.edit.tabs.curate.title": "Item bearbeiten - Pflegen", // "item.edit.tabs.metadata.head": "Metadata", "item.edit.tabs.metadata.head": "Metadaten", // "item.edit.tabs.metadata.title": "Item Edit - Metadata", - "item.edit.tabs.metadata.title": "Ressource bearbeiten - Metadaten", + "item.edit.tabs.metadata.title": "Item bearbeiten - Metadaten", // "item.edit.tabs.relationships.head": "Relationships", "item.edit.tabs.relationships.head": "Beziehungen", // "item.edit.tabs.relationships.title": "Item Edit - Relationships", - "item.edit.tabs.relationships.title": "Ressource bearbeiten - Relationen", + "item.edit.tabs.relationships.title": "Item bearbeiten - Relationen", // "item.edit.tabs.status.buttons.authorizations.button": "Authorizations...", "item.edit.tabs.status.buttons.authorizations.button": "Berechtigungen...", // "item.edit.tabs.status.buttons.authorizations.label": "Edit item's authorization policies", - "item.edit.tabs.status.buttons.authorizations.label": "Rechte der Ressource bearbeiten", + "item.edit.tabs.status.buttons.authorizations.label": "Rechte des Items bearbeiten", // "item.edit.tabs.status.buttons.delete.button": "Permanently delete", "item.edit.tabs.status.buttons.delete.button": "Endgültig löschen", @@ -2503,34 +2474,34 @@ "item.edit.tabs.status.buttons.move.button": "Verschieben...", // "item.edit.tabs.status.buttons.move.label": "Move item to another collection", - "item.edit.tabs.status.buttons.move.label": "Ressource in eine andere Sammlung verschieben", + "item.edit.tabs.status.buttons.move.label": "Item in eine andere Sammlung verschieben", // "item.edit.tabs.status.buttons.private.button": "Make it private...", "item.edit.tabs.status.buttons.private.button": "Privat machen...", // "item.edit.tabs.status.buttons.private.label": "Make item private", - "item.edit.tabs.status.buttons.private.label": "Ressource privat machen", + "item.edit.tabs.status.buttons.private.label": "Item privat machen", // "item.edit.tabs.status.buttons.public.button": "Make it public...", "item.edit.tabs.status.buttons.public.button": "Öffentlich machen...", // "item.edit.tabs.status.buttons.public.label": "Make item public", - "item.edit.tabs.status.buttons.public.label": "Ressource öffentlich machen", + "item.edit.tabs.status.buttons.public.label": "Item öffentlich machen", // "item.edit.tabs.status.buttons.reinstate.button": "Reinstate...", "item.edit.tabs.status.buttons.reinstate.button": "Reinstantiieren...", // "item.edit.tabs.status.buttons.reinstate.label": "Reinstate item into the repository", - "item.edit.tabs.status.buttons.reinstate.label": "Ressource wieder ins Repositorium einsetzen", + "item.edit.tabs.status.buttons.reinstate.label": "Item wieder ins Repositorium einsetzen", // "item.edit.tabs.status.buttons.withdraw.button": "Withdraw...", "item.edit.tabs.status.buttons.withdraw.button": "Zurückziehen...", // "item.edit.tabs.status.buttons.withdraw.label": "Withdraw item from the repository", - "item.edit.tabs.status.buttons.withdraw.label": "Ressource aus dem Repositorium zurückziehen", + "item.edit.tabs.status.buttons.withdraw.label": "Item aus dem Repositorium zurückziehen", // "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": "Ressource bearbeiten. Von hier können Sie Ressourcen zurückziehen, wiedereinsetzen, verschieben oder Löschen. Des weiteren können die Metadaten und die zugehörigen Dateien bearbeitet werden.", + "item.edit.tabs.status.description": "Item bearbeiten. Von hier können Sie Items zurückziehen, wiedereinsetzen, verschieben oder Löschen. Des weiteren können die Metadaten und die zugehörigen Dateien bearbeitet werden.", // "item.edit.tabs.status.head": "Status", "item.edit.tabs.status.head": "Status", @@ -2539,31 +2510,31 @@ "item.edit.tabs.status.labels.handle": "Handle", // "item.edit.tabs.status.labels.id": "Item Internal ID", - "item.edit.tabs.status.labels.id": "Interne ID der Ressource", + "item.edit.tabs.status.labels.id": "Interne ID des Items", // "item.edit.tabs.status.labels.itemPage": "Item Page", - "item.edit.tabs.status.labels.itemPage": "Startseite der Ressource", + "item.edit.tabs.status.labels.itemPage": "Startseite des Items", // "item.edit.tabs.status.labels.lastModified": "Last Modified", "item.edit.tabs.status.labels.lastModified": "Zuletzt geändert", // "item.edit.tabs.status.title": "Item Edit - Status", - "item.edit.tabs.status.title": "Ressource bearbeiten - Status", + "item.edit.tabs.status.title": "Item bearbeiten - Status", // "item.edit.tabs.versionhistory.head": "Version History", "item.edit.tabs.versionhistory.head": "Versionsgeschichte", // "item.edit.tabs.versionhistory.title": "Item Edit - Version History", - "item.edit.tabs.versionhistory.title": "Ressource bearbeiten - Versionsgeschichte", + "item.edit.tabs.versionhistory.title": "Item bearbeiten - Versionsgeschichte", // "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": "Das Bearbeiten oder Hinzufügen neuer Versionen ist in dieser Benutzeroberfläche noch nicht möglich.", // "item.edit.tabs.view.head": "View Item", - "item.edit.tabs.view.head": "Ressource ansehen", + "item.edit.tabs.view.head": "Item ansehen", // "item.edit.tabs.view.title": "Item Edit - View", - "item.edit.tabs.view.title": "Ressource bearbeiten - Ansicht", + "item.edit.tabs.view.title": "Item bearbeiten - Ansicht", @@ -2574,27 +2545,27 @@ "item.edit.withdraw.confirm": "Zurückziehen", // "item.edit.withdraw.description": "Are you sure this item should be withdrawn from the archive?", - "item.edit.withdraw.description": "Sind Sie sicher, dass Sie diese Ressource aus dem Repositorium zurückziehen wollen?", + "item.edit.withdraw.description": "Sind Sie sicher, dass Sie dieses Item aus dem Repositorium zurückziehen wollen?", // "item.edit.withdraw.error": "An error occurred while withdrawing the item", - "item.edit.withdraw.error": "Ein Fehler ist beim Zurückziehen der Ressource aufgetreten", + "item.edit.withdraw.error": "Ein Fehler ist beim Zurückziehen des Items aufgetreten", // "item.edit.withdraw.header": "Withdraw item: {{ id }}", - "item.edit.withdraw.header": "Ressource: {{ id }} zurückziehen", + "item.edit.withdraw.header": "Item: {{ id }} zurückziehen", // "item.edit.withdraw.success": "The item was withdrawn successfully", - "item.edit.withdraw.success": "Die Ressource wurde erfolgreich zurückgezogen", + "item.edit.withdraw.success": "Das Item wurde erfolgreich zurückgezogen", // "item.listelement.badge": "Item", - "item.listelement.badge": "Ressource", + "item.listelement.badge": "Item", // "item.page.description": "Description", "item.page.description": "Beschreibung", // "item.page.edit": "Edit this item", - "item.page.edit": "Diese Ressource bearbeiten", + "item.page.edit": "Dieses Item bearbeiten", // "item.page.journal-issn": "Journal ISSN", "item.page.journal-issn": "ISSN der Zeitschrift", @@ -2606,16 +2577,16 @@ "item.page.publisher": "Verlag", // "item.page.titleprefix": "Item: ", - "item.page.titleprefix": "Ressource: ", + "item.page.titleprefix": "Item: ", // "item.page.volume-title": "Volume Title", "item.page.volume-title": "Bandtitel", // "item.search.results.head": "Item Search Results", - "item.search.results.head": "Ressourcen Suchergebnisse", + "item.search.results.head": "Item Suchergebnisse", // "item.search.title": "DSpace Angular :: Item Search", - "item.search.title": "DSpace Angular :: Ressourcen-Suche", + "item.search.title": "DSpace Angular :: Item-Suche", @@ -2635,7 +2606,7 @@ "item.page.date": "Datum", // "item.page.edit": "Edit this item", - "item.page.edit": "Diese Ressource bearbeiten", + "item.page.edit": "Dieses Item bearbeiten", // "item.page.files": "Files", "item.page.files": "Dateien", @@ -2728,7 +2699,6 @@ "item.preview.dc.title": "Titel:", // "item.preview.person.familyName": "Surname:", - // TODO New key - Add a translation "item.preview.person.familyName": "Nachname:", // "item.preview.person.givenName": "Name:", @@ -2742,7 +2712,7 @@ "item.select.confirm": "Auswahl bestätigen", // "item.select.empty": "No items to show", - "item.select.empty": "Es gibt keine Ressourcen dazu", + "item.select.empty": "Es gibt keine Items dazu", // "item.select.table.author": "Author", "item.select.table.author": "Autor:in", @@ -2755,7 +2725,7 @@ // "item.version.history.empty": "There are no other versions for this item yet.", - "item.version.history.empty": "Es gibt noch keine anderen Versionen für diese Ressource.", + "item.version.history.empty": "Es gibt noch keine anderen Versionen für dieses Item.", // "item.version.history.head": "Version History", "item.version.history.head": "Versionsgeschichte", @@ -2770,7 +2740,7 @@ "item.version.history.table.version": "Version", // "item.version.history.table.item": "Item", - "item.version.history.table.item": "Ressource", + "item.version.history.table.item": "Item", // "item.version.history.table.editor": "Editor", "item.version.history.table.editor": "Herausgeber:in", @@ -2784,8 +2754,7 @@ // "item.version.notice": "This is not the latest version of this item. The latest version can be found here.", - // TODO New key - Add a translation - "item.version.notice": "Dies ist nicht die neueste Version dieses Artikels. Die neueste Version finden Sie hier.", + "item.version.notice": "Dies ist nicht die neueste Version dieses Items. Die neueste Version finden Sie unter hier.", @@ -2796,7 +2765,7 @@ "journal.page.description": "Beschreibung", // "journal.page.edit": "Edit this item", - "journal.page.edit": "Diese Ressource bearbeiten", + "journal.page.edit": "Dieses Item bearbeiten", // "journal.page.editor": "Editor-in-Chief", "journal.page.editor": "Chefredakteur:in", @@ -2825,7 +2794,7 @@ "journalissue.page.description": "Beschreibeung", // "journalissue.page.edit": "Edit this item", - "journalissue.page.edit": "Diese Ressource bearbeiten", + "journalissue.page.edit": "Dieses Item bearbeiten", // "journalissue.page.issuedate": "Issue Date", "journalissue.page.issuedate": "Erscheinungsdatum", @@ -2854,7 +2823,7 @@ "journalvolume.page.description": "Beschreibung", // "journalvolume.page.edit": "Edit this item", - "journalvolume.page.edit": "Diese Ressource bearbeiten", + "journalvolume.page.edit": "Dieses Item bearbeiten", // "journalvolume.page.issuedate": "Issue Date", "journalvolume.page.issuedate": "Erscheinungsdatum", @@ -2874,7 +2843,7 @@ "loading.bitstreams": "Lade Datei...", // "loading.browse-by": "Loading items...", - "loading.browse-by": "Lade Ressourcen...", + "loading.browse-by": "Lade Items...", // "loading.browse-by-page": "Loading page...", "loading.browse-by-page": "Lade Seite...", @@ -2895,13 +2864,13 @@ "loading.default": "Lade...", // "loading.item": "Loading item...", - "loading.item": "Lade Ressource...", + "loading.item": "Lade Item...", // "loading.items": "Loading items...", - "loading.items": "Lade Ressourcen...", + "loading.items": "Lade Items...", // "loading.mydspace-results": "Loading items...", - "loading.mydspace-results": "Lade Ressourcen...", + "loading.mydspace-results": "Lade Items...", // "loading.objects": "Loading...", "loading.objects": "Lade...", @@ -3043,7 +3012,7 @@ "menu.section.edit_community": "Bereich", // "menu.section.edit_item": "Item", - "menu.section.edit_item": "Ressource", + "menu.section.edit_item": "Item", @@ -3057,7 +3026,7 @@ "menu.section.export_community": "Bereich", // "menu.section.export_item": "Item", - "menu.section.export_item": "Ressource", + "menu.section.export_item": "Item", // "menu.section.export_metadata": "Metadata", "menu.section.export_metadata": "Metadaten", @@ -3068,8 +3037,7 @@ "menu.section.icon.access_control": "Menübereich Zugriffskontrolle", // "menu.section.icon.admin_search": "Admin search menu section", - // TODO New key - Add a translation - "menu.section.icon.admin_search": "Admin search menu section", + "menu.section.icon.admin_search": "Menübereich Admin-Suche", // "menu.section.icon.control_panel": "Control Panel menu section", "menu.section.icon.control_panel": "Menübereich Kontrollfeld", @@ -3130,10 +3098,10 @@ "menu.section.new_community": "Bereich", // "menu.section.new_item": "Item", - "menu.section.new_item": "Ressource", + "menu.section.new_item": "Item", // "menu.section.new_item_version": "Item Version", - "menu.section.new_item_version": "Ressourcenversion", + "menu.section.new_item_version": "Item-Version", // "menu.section.new_process": "Process", "menu.section.new_process": "Prozess", @@ -3214,7 +3182,7 @@ "mydspace.general.text-here": "hier", // "mydspace.messages.controller-help": "Select this option to send a message to item's submitter.", - "mydspace.messages.controller-help": "Wählen Sie diese Option, um dem/derjenigen, die die Ressource eingereicht hat, eine Nachricht zu schicken.", + "mydspace.messages.controller-help": "Wählen Sie diese Option, um dem/derjenigen, die das Item eingereicht hat, eine Nachricht zu schicken.", // "mydspace.messages.description-placeholder": "Insert your message here...", "mydspace.messages.description-placeholder": "Geben Sie Ihre Nachricht hier ein...", @@ -3280,7 +3248,7 @@ "mydspace.results.no-files": "Keine Dateien", // "mydspace.results.no-results": "There were no items to show", - "mydspace.results.no-results": "Es gibt keine Ressourcen anzuzeigen", + "mydspace.results.no-results": "Es gibt keine Items anzuzeigen", // "mydspace.results.no-title": "No title", "mydspace.results.no-title": "Kein Titel", @@ -3322,10 +3290,10 @@ "mydspace.upload.upload-failed-moreonefile": "Unverarbeitbare Anfrage. Nur eine Datei ist erlaubt.", // "mydspace.upload.upload-multiple-successful": "{{qty}} new workspace items created.", - "mydspace.upload.upload-multiple-successful": "{{qty}} neue(s) Arbeitsbereichressource(n) angelegt.", + "mydspace.upload.upload-multiple-successful": "{{qty}} neue(s) Arbeitsbereichitem(s) angelegt.", // "mydspace.upload.upload-successful": "New workspace item created. Click {{here}} for edit it.", - "mydspace.upload.upload-successful": "Neue Arbeitsbereichressource angelegt. Klicken Sie {{here}}, um sie zu bearbeiten.", + "mydspace.upload.upload-successful": "Neues Arbeitsbereichitem angelegt. Klicken Sie {{here}}, um sie zu bearbeiten.", // "mydspace.view-btn": "View", "mydspace.view-btn": "Anzeige", @@ -3380,7 +3348,7 @@ "orgunit.page.description": "Beschreibung", // "orgunit.page.edit": "Edit this item", - "orgunit.page.edit": "Diese Ressourcen bearbeiten", + "orgunit.page.edit": "Dieses Item bearbeiten", // "orgunit.page.id": "ID", "orgunit.page.id": "ID", @@ -3414,7 +3382,7 @@ "person.page.birthdate": "Geburtsdatum", // "person.page.edit": "Edit this item", - "person.page.edit": "Diese Ressource bearbeiten", + "person.page.edit": "Dieses Item bearbeiten", // "person.page.email": "Email Address", "person.page.email": "E-Mail-Adresse", @@ -3517,24 +3485,19 @@ "process.detail.back" : "Zurück", // "process.detail.output" : "Process Output", - // TODO New key - Add a translation - "process.detail.output" : "Process Output", + "process.detail.output" : "Prozessausgabe", // "process.detail.logs.button": "Retrieve process output", - // TODO New key - Add a translation - "process.detail.logs.button": "Retrieve process output", + "process.detail.logs.button": "Abrufen der Prozessausgabe", // "process.detail.logs.loading": "Retrieving", - // TODO New key - Add a translation - "process.detail.logs.loading": "Retrieving", + "process.detail.logs.loading": "Abrufen", // "process.detail.logs.none": "This process has no output", - // TODO New key - Add a translation - "process.detail.logs.none": "This process has no output", + "process.detail.logs.none": "Dieser Prozess hat keine Ausgabe", // "process.detail.output-files" : "Output Files", - // TODO New key - Add a translation - "process.detail.output-files" : "Output Files", + "process.detail.output-files" : "Ausgabedateien", // "process.detail.output-files.empty" : "This process doesn't contain any output files", "process.detail.output-files.empty" : "Dieser Prozess enthält keine Ausgabedateien", @@ -3683,7 +3646,7 @@ "project.page.description": "Beschreibung", // "project.page.edit": "Edit this item", - "project.page.edit": "Diese Ressource bearbeiten", + "project.page.edit": "Dieses Item bearbeiten", // "project.page.expectedcompletion": "Expected Completion", "project.page.expectedcompletion": "Erwartetes Abschlussdatum", @@ -3715,7 +3678,7 @@ "publication.page.description": "Beschreibung", // "publication.page.edit": "Edit this item", - "publication.page.edit": "Diese Ressource bearbeiten", + "publication.page.edit": "Dieses Item bearbeiten", // "publication.page.journal-issn": "Journal ISSN", "publication.page.journal-issn": "ISSN der Zeitschrift", @@ -3810,7 +3773,7 @@ "register-page.registration.header": "Registrierung neuer Benutzer:innen", // "register-page.registration.info": "Register an account to subscribe to collections for email updates, and submit new items to DSpace.", - "register-page.registration.info": "Registrieren Sie ein Konto, um Sammlungen für E-Mail-Updates zu abonnieren und neue Ressourcen in DSpace einzugeben.", + "register-page.registration.info": "Registrieren Sie ein Konto, um Sammlungen für E-Mail-Updates zu abonnieren und neue Items in DSpace einzugeben.", // "register-page.registration.email": "Email Address *", "register-page.registration.email": "E-Mail-Adresse *", @@ -3842,7 +3805,7 @@ // "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": "Für den Beziehungstyp {{ type }} zwischen den beiden Ressourcen konnte keine passende Übereinstimmung gefunden werden", + "relationships.add.error.relationship-type.content": "Für den Beziehungstyp {{ type }} zwischen den beiden Items konnte keine passende Übereinstimmung gefunden werden", // "relationships.add.error.server.content": "The server returned an error", "relationships.add.error.server.content": "Der Server hat einen Fehler zurückgegeben", @@ -3919,55 +3882,46 @@ "resource-policies.add.for.collection": "Eine neue Sammlungen-Richtlinie hinzufügen", // "resource-policies.create.page.heading": "Create new resource policy for ", - // TODO New key - Add a translation - "resource-policies.create.page.heading": "Create new resource policy for ", + "resource-policies.create.page.heading": "Eine neue Ressourcen-Richtlinie erstellen für ", // "resource-policies.create.page.failure.content": "An error occurred while creating the resource policy.", - // TODO New key - Add a translation - "resource-policies.create.page.failure.content": "An error occurred while creating the resource policy.", + "resource-policies.create.page.failure.content": "Bei der Erstellung der Ressourcen-Richtlinie ist ein Fehler aufgetreten.", // "resource-policies.create.page.success.content": "Operation successful", "resource-policies.create.page.success.content": "Vorgang erfolgreich", // "resource-policies.create.page.title": "Create new resource policy", - // TODO New key - Add a translation - "resource-policies.create.page.title": "Create new resource policy", + "resource-policies.create.page.title": "Eine neue Ressourcen-Richtlinie erstellen", // "resource-policies.delete.btn": "Delete selected", "resource-policies.delete.btn": "Ausgewählte löschen", // "resource-policies.delete.btn.title": "Delete selected resource policies", - // TODO New key - Add a translation - "resource-policies.delete.btn.title": "Delete selected resource policies", + "resource-policies.delete.btn.title": "Ausgewählte Ressourcen-Richtlinien löschen", // "resource-policies.delete.failure.content": "An error occurred while deleting selected resource policies.", - // TODO New key - Add a translation - "resource-policies.delete.failure.content": "An error occurred while deleting selected resource policies.", + "resource-policies.delete.failure.content": "Beim Löschen der ausgewählten Ressourcen-Richtlinien ist ein Fehler aufgetreten.", // "resource-policies.delete.success.content": "Operation successful", "resource-policies.delete.success.content": "Vorgang erfolgreich", // "resource-policies.edit.page.heading": "Edit resource policy ", - // TODO New key - Add a translation - "resource-policies.edit.page.heading": "Edit resource policy ", + "resource-policies.edit.page.heading": "Ressourcen-Richtlinie bearbeiten ", // "resource-policies.edit.page.failure.content": "An error occurred while editing the resource policy.", - // TODO New key - Add a translation - "resource-policies.edit.page.failure.content": "An error occurred while editing the resource policy.", + "resource-policies.edit.page.failure.content": "Beim Bearbeiten der Ressourcen-Richtlinie ist ein Fehler aufgetreten.", // "resource-policies.edit.page.success.content": "Operation successful", "resource-policies.edit.page.success.content": "Vorgang erfolgreich", // "resource-policies.edit.page.title": "Edit resource policy", - // TODO New key - Add a translation - "resource-policies.edit.page.title": "Edit resource policy", + "resource-policies.edit.page.title": "Ressourcen-Richtlinie bearbeiten", // "resource-policies.form.action-type.label": "Select the action type", "resource-policies.form.action-type.label": "Wählen Sie die Aktionsart", // "resource-policies.form.action-type.required": "You must select the resource policy action.", - // TODO New key - Add a translation - "resource-policies.form.action-type.required": "You must select the resource policy action.", + "resource-policies.form.action-type.required": "Sie müssen die Aktion der Ressourcen-Richtlinie auswählen.", // "resource-policies.form.eperson-group-list.label": "The eperson or group that will be granted the permission", "resource-policies.form.eperson-group-list.label": "Die Person oder Gruppe, der die Genehmigung erteilt wird", @@ -3982,8 +3936,7 @@ "resource-policies.form.eperson-group-list.tab.group": "Suche nach einer Gruppe", // "resource-policies.form.eperson-group-list.table.headers.action": "Action", - // TODO New key - Add a translation - "resource-policies.form.eperson-group-list.table.headers.action": "Action", + "resource-policies.form.eperson-group-list.table.headers.action": "Aktion", // "resource-policies.form.eperson-group-list.table.headers.id": "ID", "resource-policies.form.eperson-group-list.table.headers.id": "ID", @@ -4007,8 +3960,7 @@ "resource-policies.form.policy-type.label": "Wählen Sie den Richtlinientyp", // "resource-policies.form.policy-type.required": "You must select the resource policy type.", - // TODO New key - Add a translation - "resource-policies.form.policy-type.required": "You must select the resource policy type.", + "resource-policies.form.policy-type.required": "Sie müssen den Typ der Ressourcen-Richtlinie auswählen", // "resource-policies.table.headers.action": "Action", "resource-policies.table.headers.action": "Aktion", @@ -4091,7 +4043,7 @@ "search.filters.applied.f.discoverable": "Privat", // "search.filters.applied.f.entityType": "Item Type", - "search.filters.applied.f.entityType": "Art der Ressource", + "search.filters.applied.f.entityType": "Item-Typ", // "search.filters.applied.f.has_content_in_original_bundle": "Has files", "search.filters.applied.f.has_content_in_original_bundle": "Hat Dateien", @@ -4180,10 +4132,10 @@ "search.filters.filter.withdrawn.head": "Zurückgezogen", // "search.filters.filter.entityType.head": "Item Type", - "search.filters.filter.entityType.head": "Art der Ressource", + "search.filters.filter.entityType.head": "Item-Typ", // "search.filters.filter.entityType.placeholder": "Item Type", - "search.filters.filter.entityType.placeholder": "Art der Ressource", + "search.filters.filter.entityType.placeholder": "Item-Typ", // "search.filters.filter.has_content_in_original_bundle.head": "Has files", "search.filters.filter.has_content_in_original_bundle.head": "Enthält Dateien", @@ -4447,7 +4399,7 @@ "submission.import-external.title": "Importieren von Metadaten aus einer externen Quelle", // "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": "Geben Sie oben eine Abfrage ein, um Ressourcen aus dem Internet zu finden, die in DSpace importiert werden sollen.", + "submission.import-external.page.hint": "Geben Sie oben eine Abfrage ein, um Items aus dem Internet zu finden, die in DSpace importiert werden sollen.", // "submission.import-external.back-to-my-dspace": "Back to MyDSpace", "submission.import-external.back-to-my-dspace": "Zurück zu MyDSpace", @@ -4486,7 +4438,7 @@ "submission.import-external.source.lcname": "Library of Congress Names", // "submission.import-external.preview.title": "Item Preview", - "submission.import-external.preview.title": "Ressourcen-Vorschau", + "submission.import-external.preview.title": "Item-Vorschau", // "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": "Die folgenden Metadaten wurden aus einer externen Quelle importiert. Sie werden vorausgefüllt, wenn Sie die Eingabe starten.", @@ -4507,8 +4459,7 @@ "submission.sections.describe.relationship-lookup.external-source.added": "Der lokale Eintrage wurde erfolgreich zur Auswahl hinzugefügt.", // "submission.sections.describe.relationship-lookup.external-source.import-button-title.isAuthorOfPublication": "Import remote author", - // TODO New key - Add a translation - "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": "Importiere Autor:innen-Metadaten", // "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": "Zeitschrift importieren", @@ -4520,8 +4471,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Volume": "Zeitschriftenband importieren", // "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.title": "Import Remote Author", - // TODO New key - Add a translation - "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": "Importiere Autor:innen-Metadaten", // "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": "Erfolgreicher Import und Hinzufügen eines/einer lokalen Autor:in zur Auswahl", @@ -4620,55 +4570,46 @@ "submission.sections.describe.relationship-lookup.search-tab.select-page": "Seite auswählen", // "submission.sections.describe.relationship-lookup.selected": "Selected {{ size }} items", - "submission.sections.describe.relationship-lookup.selected": "{{ size }} Ressourcen ausgewählt", + "submission.sections.describe.relationship-lookup.selected": "{{ size }} Items ausgewählt", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Local Authors ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Local Authors ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Lokale Autoren ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Local Journals ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Local Journals ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Lokale Zeitschriften ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Project": "Local Projects ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.Project": "Local Projects ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Project": "Lokale Projekte ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Publication": "Local Publications ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.Publication": "Local Publications ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Publication": "Lokale Publicationen ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Local Authors ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Local Authors ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Lokale Autoren ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.OrgUnit": "Local Organizational Units ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.OrgUnit": "Local Organizational Units ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.OrgUnit": "Lokale Organisationseinheiten ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataPackage": "Local Data Packages ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataPackage": "Local Data Packages ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataPackage": "Lokale Datenpakete ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataFile": "Local Data Files ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataFile": "Local Data Files ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.DataFile": "Lokale Dateien ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Local Journals ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Lokale Zeitschriften ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalIssueOfPublication": "Local Journal Issues ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalIssueOfPublication": "Local Journal Issues ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalIssueOfPublication": "Lokale Zeitschriftenhefte ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalIssue": "Local Journal Issues ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalIssue": "Local Journal Issues ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalIssue": "Lokale Zeitschriftenhefte ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalVolumeOfPublication": "Local Journal Volumes ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalVolumeOfPublication": "Local Journal Volumes ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalVolumeOfPublication": "Lokale Zeitschriftenbände ({{ count }})", + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalVolume": "Local Journal Volumes ({{ count }})", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalVolume": "Local Journal Volumes ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.JournalVolume": "Lokale Zeitschriftenbände ({{ 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 Zeitschriften ({{ count }})", @@ -4734,8 +4675,7 @@ "submission.sections.describe.relationship-lookup.title.DataPackage": "Datenpakete", // "submission.sections.describe.relationship-lookup.title.DataFile": "Data Files", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.title.DataFile": "Data Files", + "submission.sections.describe.relationship-lookup.title.DataFile": "Dateien", // "submission.sections.describe.relationship-lookup.title.Funding Agency": "Funding Agency", "submission.sections.describe.relationship-lookup.title.Funding Agency": "Fördereinrichtung", @@ -4780,8 +4720,7 @@ "submission.sections.describe.relationship-lookup.selection-tab.title.DataPackage": "Ausgewählte Datenpakete", // "submission.sections.describe.relationship-lookup.selection-tab.title.DataFile": "Selected Data Files", - // TODO New key - Add a translation - "submission.sections.describe.relationship-lookup.selection-tab.title.DataFile": "Selected Data Files", + "submission.sections.describe.relationship-lookup.selection-tab.title.DataFile": "Ausgewählte Dateien", // "submission.sections.describe.relationship-lookup.selection-tab.title.Journal": "Selected Journals", "submission.sections.describe.relationship-lookup.selection-tab.title.Journal": "Ausgewählte Zeitschriften", @@ -4850,12 +4789,10 @@ "submission.sections.ccLicense.option.select": "Wählen Sie eine Option…", // "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": "Sie haben die folgende Lizenz ausgewählt:", // "submission.sections.ccLicense.confirmation": "I grant the license above", - // TODO New key - Add a translation - "submission.sections.ccLicense.confirmation": "I grant the license above", + "submission.sections.ccLicense.confirmation": "Ich erteile die oben genannte Lizenz", // "submission.sections.general.add-more": "Add more", "submission.sections.general.add-more": "Mehr Hinzufügen", @@ -4864,7 +4801,7 @@ "submission.sections.general.collection": "Sammlung", // "submission.sections.general.deposit_error_notice": "There was an issue when submitting the item, please try again later.", - "submission.sections.general.deposit_error_notice": "Beim Einreichen der Ressource ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal.", + "submission.sections.general.deposit_error_notice": "Beim Einreichen des Items ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal.", // "submission.sections.general.deposit_success_notice": "Submission deposited successfully.", "submission.sections.general.deposit_success_notice": "Veröffentlichung erfolgreich eingereicht", @@ -4888,7 +4825,7 @@ "submission.sections.general.no-sections": "Es stehen keine Optionen zur Verfügung", // "submission.sections.general.save_error_notice": "There was an issue when saving the item, please try again later.", - "submission.sections.general.save_error_notice": "Beim Speichern der Ressource ist ein Fehler aufgetreten, bitte versuchen Sie es später noch einmal.", + "submission.sections.general.save_error_notice": "Beim Speichern des Items ist ein Fehler aufgetreten, bitte versuchen Sie es später noch einmal.", // "submission.sections.general.save_success_notice": "Submission saved successfully.", "submission.sections.general.save_success_notice": "Einreichung erfolgreich gespeichert.", @@ -4902,7 +4839,6 @@ // "submission.sections.submit.progressbar.CClicense": "Creative commons license", - // TODO New key - Add a translation "submission.sections.submit.progressbar.CClicense": "Creative commons license", // "submission.sections.submit.progressbar.describe.recycle": "Recycle", @@ -4944,7 +4880,7 @@ "submission.sections.upload.delete.submit": "Löschen", // "submission.sections.upload.drop-message": "Drop files to attach them to the item", - "submission.sections.upload.drop-message": "Dateien herüberziehen, um sie der Ressource hinzuzufügen", + "submission.sections.upload.drop-message": "Dateien herüberziehen, um sie dem Item hinzuzufügen", // "submission.sections.upload.form.access-condition-label": "Access condition type", "submission.sections.upload.form.access-condition-label": "Zugriffsbedingung Typ", @@ -4953,7 +4889,6 @@ "submission.sections.upload.form.date-required": "Datum erforderlich.", // "submission.sections.upload.form.from-label": "Grant access from", - // TODO Source message changed - Revise the translation "submission.sections.upload.form.from-label": "Zugriff gewährt ab", // "submission.sections.upload.form.from-placeholder": "From", @@ -4966,7 +4901,6 @@ "submission.sections.upload.form.group-required": "Gruppe ist erforderlich", // "submission.sections.upload.form.until-label": "Grant access until", - // TODO Source message changed - Revise the translation "submission.sections.upload.form.until-label": "Zugriff gewährt bis", // "submission.sections.upload.form.until-placeholder": "Until", @@ -4979,7 +4913,7 @@ "submission.sections.upload.header.policy.default.withlist": "Bitte beachten Sie, dass in diese Sammlung {{collectionName}} hochgeladene Dateien zugüglich zu dem, was für einzelne Dateien entschieden wurde, für folgende Gruppe(n) zugänglich sein:", // "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": "Hier finden Sie alle Dateien, die aktuell zur Ressource gehören. Sie können die Metadaten und Zugriffsrechte bearbeiten oder weitere Dateien hinzufügen, indem Sie sie einfach irgenwo auf diese Seite ziehen.", + "submission.sections.upload.info": "Hier finden Sie alle Dateien, die aktuell zum Item gehören. Sie können die Metadaten und Zugriffsrechte bearbeiten oder weitere Dateien hinzufügen, indem Sie sie einfach irgenwo auf diese Seite ziehen.", // "submission.sections.upload.no-entry": "No", "submission.sections.upload.no-entry": "Kein Eintrag", @@ -5010,19 +4944,19 @@ "submission.workflow.generic.delete": "Löschen", // "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": "Wenn Sie die Ressource verwerfen möchten, wählen Sie \"Löschen\". Sie werden dies noch einmal gefragt, um die Aktion zu bestätigen.", + "submission.workflow.generic.delete-help": "Wenn Sie das Item verwerfen möchten, wählen Sie \"Löschen\". Sie werden dies noch einmal gefragt, um die Aktion zu bestätigen.", // "submission.workflow.generic.edit": "Edit", "submission.workflow.generic.edit": "Bearbeiten", // "submission.workflow.generic.edit-help": "Select this option to change the item's metadata.", - "submission.workflow.generic.edit-help": "Wählen Sie diese Option, um die Metadaten der Ressource zu bearbeiten.", + "submission.workflow.generic.edit-help": "Wählen Sie diese Option, um die Metadaten des Items zu bearbeiten.", // "submission.workflow.generic.view": "View", "submission.workflow.generic.view": "Anzeige", // "submission.workflow.generic.view-help": "Select this option to view the item's metadata.", - "submission.workflow.generic.view-help": "Wählen Sie diese Option, um die Metadaten der Ressourcen anzuzeigen", + "submission.workflow.generic.view-help": "Wählen Sie diese Option, um die Metadaten des Items anzuzeigen", @@ -5030,13 +4964,13 @@ "submission.workflow.tasks.claimed.approve": "Zustimmen", // "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": "Wenn Sie die Ressource begutachtet haben und die Aufnahme in die Sammlung befürworten, wählen Sie \"Zustimmen\".", + "submission.workflow.tasks.claimed.approve_help": "Wenn Sie das Item begutachtet haben und die Aufnahme in die Sammlung befürworten, wählen Sie \"Zustimmen\".", // "submission.workflow.tasks.claimed.edit": "Edit", "submission.workflow.tasks.claimed.edit": "Bearbeiten", // "submission.workflow.tasks.claimed.edit_help": "Select this option to change the item's metadata.", - "submission.workflow.tasks.claimed.edit_help": "Wählen Sie diese Option, um die Metadaten der Ressource zu bearbeiten.", + "submission.workflow.tasks.claimed.edit_help": "Wählen Sie diese Option, um die Metadaten des Items zu bearbeiten.", // "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": "Bitte geben Sie den Grund für die Ablehnung der eingereichten Ressource in das Feld unten ein. Bitte geben Sie an ob und wie der/die Einreichende:n das Problem beheben und die Ressource erneut einreichen kann.", @@ -5045,7 +4979,7 @@ "submission.workflow.tasks.claimed.reject.reason.placeholder": "Beschreiben Sie den Grund für die Ablehnung", // "submission.workflow.tasks.claimed.reject.reason.submit": "Reject item", - "submission.workflow.tasks.claimed.reject.reason.submit": "Ressource ablehnen", + "submission.workflow.tasks.claimed.reject.reason.submit": "Item ablehnen", // "submission.workflow.tasks.claimed.reject.reason.title": "Reason", "submission.workflow.tasks.claimed.reject.reason.title": "Grund", @@ -5054,7 +4988,7 @@ "submission.workflow.tasks.claimed.reject.submit": "Ablehnen", // "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": "Wenn Sie die Ressource begutachtet und als ungeeignet für die Aufnahme in die Sammlung befunden haben, wählen Sie \"Ablehnen\". Sie haben dann die Möglichkeit dem/der Einreichenden, den Grund für die Ablehnung zu erklären und ob es eine Möglichkeit gibt, durch entsprechenden Änderungen die Ressource erneut einzureichen.", + "submission.workflow.tasks.claimed.reject_help": "Wenn Sie das Item begutachtet und als ungeeignet für die Aufnahme in die Sammlung befunden haben, wählen Sie \"Ablehnen\". Sie haben dann die Möglichkeit dem/der Einreichenden, den Grund für die Ablehnung zu erklären und ob es eine Möglichkeit gibt, durch entsprechenden Änderungen das Item erneut einzureichen.", // "submission.workflow.tasks.claimed.return": "Return to pool", "submission.workflow.tasks.claimed.return": "Zurück in den gemeinsamen Aufgabenbereich", @@ -5142,7 +5076,7 @@ "virtual-metadata.delete-item.modal-head": "Virtuelle Metadaten dieser Relation", // "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": "Wählen Sie die Ressourcen für die Sie die virtuellen Metadaten als reelle Metadaten speichern wollen", + "virtual-metadata.delete-relationship.modal-head": "Wählen Sie die Items für die Sie die virtuellen Metadaten als reelle Metadaten speichern wollen", From 07000297183c5182326132624cb0948f252dd708 Mon Sep 17 00:00:00 2001 From: Pascal-Nicolas Becker Date: Wed, 9 Mar 2022 16:28:38 +0100 Subject: [PATCH 073/570] Working on inclusivness of the German translation --- src/assets/i18n/de.json5 | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index d3fc1131aa..bbac4ccdef 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -107,7 +107,7 @@ "admin.registries.bitstream-formats.edit.head": "Dateiformat: {{ 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": "Dateiformate, die als intern gekennzeichnet sind, dienen administrativen Zwecken und bleiben dem/der Endnutzer:in verborgen.", + "admin.registries.bitstream-formats.edit.internal.hint": "Dateiformate, die als intern gekennzeichnet sind, dienen administrativen Zwecken und bleiben dem:der Endnutzer:in verborgen.", // "admin.registries.bitstream-formats.edit.internal.label": "Internal", "admin.registries.bitstream-formats.edit.internal.label": "Intern", @@ -765,7 +765,7 @@ "bitstream.edit.form.description.label": "Beschreibung", // "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": "Der erste Tag, ab dem der Zugang erlaubt ist. This date cannot be modified on this form. Um eine Embargo-Frist für eine Datei festzulegen, zu Item Status gehen, auf Authorizations... klicken, die Richtlinie READ für die Datei erstellen oder verändern und das Start Date wie gewünscht einstellen.", + "bitstream.edit.form.embargo.hint": "Der erste Tag, ab dem der Zugang erlaubt ist. Dieses Datum kann in diesem Formular nicht verändert werden. Um eine Embargo-Frist für eine Datei festzulegen, zu Item Status gehen, auf Authorizations... klicken, die Richtlinie READ für die Datei erstellen oder verändern und das Start Date wie gewünscht einstellen.", // "bitstream.edit.form.embargo.label": "Embargo until specific date", @@ -1499,7 +1499,7 @@ "cookies.consent.decline": "Ablehnen", // "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": "Wir sammeln und verarbeiten Ihre personenbezogenen Daten für die folgenden Zwecke: Authentication, Preferences, Acknowledgement and Statistics.
    Um mehr zu erfahren, lesen Sie bitte unsere {privacyPolicy}.", + "cookies.consent.content-notice.description": "Wir sammeln und verarbeiten Ihre personenbezogenen Daten für die folgenden Zwecke: Authentifikation, Einstellungen, Zustimmungen und Statistiken.
    Um mehr zu erfahren, lesen Sie bitte unsere {privacyPolicy}.", // "cookies.consent.content-notice.learnMore": "Customize", "cookies.consent.content-notice.learnMore": "Anpassen", @@ -1598,7 +1598,7 @@ "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": "Hinweis: Geben Sie [your-handle-prefix]/0 ein, um eine Aufgabe für die gesamte Seite durchzuführen (nicht alle Aufgaben unterstützen diese Funktion)", + "curation.form.handle.hint": "Hinweis: Geben Sie [handle-prefix]/0 ein, um eine Aufgabe für die gesamte Seite durchzuführen (nicht alle Aufgaben unterstützen diese Funktion)", @@ -2636,7 +2636,7 @@ "item.page.link.simple": "Kurzanzeige", // "item.page.person.search.title": "Articles by this author", - "item.page.person.search.title": "Veröffentlichungen dieses/dieser Autor:in", + "item.page.person.search.title": "Veröffentlichungen dieses:dieser Autor:in", // "item.page.related-items.view-more": "Show {{ amount }} more", "item.page.related-items.view-more": "{{ amount }} mehr anzeigen", @@ -3182,7 +3182,7 @@ "mydspace.general.text-here": "hier", // "mydspace.messages.controller-help": "Select this option to send a message to item's submitter.", - "mydspace.messages.controller-help": "Wählen Sie diese Option, um dem/derjenigen, die das Item eingereicht hat, eine Nachricht zu schicken.", + "mydspace.messages.controller-help": "Wählen Sie diese Option, um dem:derjenigen, die das Item eingereicht hat, eine Nachricht zu schicken.", // "mydspace.messages.description-placeholder": "Insert your message here...", "mydspace.messages.description-placeholder": "Geben Sie Ihre Nachricht hier ein...", @@ -3290,10 +3290,10 @@ "mydspace.upload.upload-failed-moreonefile": "Unverarbeitbare Anfrage. Nur eine Datei ist erlaubt.", // "mydspace.upload.upload-multiple-successful": "{{qty}} new workspace items created.", - "mydspace.upload.upload-multiple-successful": "{{qty}} neue(s) Arbeitsbereichitem(s) angelegt.", + "mydspace.upload.upload-multiple-successful": "{{qty}} neue(s) Arbeitsbereichsitem(s) angelegt.", // "mydspace.upload.upload-successful": "New workspace item created. Click {{here}} for edit it.", - "mydspace.upload.upload-successful": "Neues Arbeitsbereichitem angelegt. Klicken Sie {{here}}, um sie zu bearbeiten.", + "mydspace.upload.upload-successful": "Neues Arbeitsbereichsitem angelegt. Klicken Sie {{here}}, um sie zu bearbeiten.", // "mydspace.view-btn": "View", "mydspace.view-btn": "Anzeige", @@ -3763,7 +3763,7 @@ "register-page.create-profile.submit.error.head": "Registrierung fehlgeschlagen.", // "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": "Die Registrierung war erfolgreich. Sie wurden als der/die angelegte Benutzer:in angemeldet.", + "register-page.create-profile.submit.success.content": "Die Registrierung war erfolgreich. Sie wurden als der:die angelegte Benutzer:in angemeldet.", // "register-page.create-profile.submit.success.head": "Registration completed", "register-page.create-profile.submit.success.head": "Registrierung abgeschlossen", @@ -4078,7 +4078,7 @@ "search.filters.filter.author.head": "Autor:in", // "search.filters.filter.author.placeholder": "Author name", - "search.filters.filter.author.placeholder": "Name des/der Autor:in", + "search.filters.filter.author.placeholder": "Name des:der Autor:in", // "search.filters.filter.birthDate.head": "Birth Date", "search.filters.filter.birthDate.head": "Geburtsdatum", @@ -4474,10 +4474,10 @@ "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.title": "Importiere Autor:innen-Metadaten", // "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": "Erfolgreicher Import und Hinzufügen eines/einer lokalen Autor:in zur Auswahl", + "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.local-entity": "Erfolgreicher Import und Hinzufügen eines:einer lokalen Autor:in zur Auswahl", // "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": "Erfolgreicher Import und Hinzufügen eines/einer externen Autor:in zur Auswahl", + "submission.sections.describe.relationship-lookup.external-source.import-modal.isAuthorOfPublication.added.new-entity": "Erfolgreicher Import und Hinzufügen eines:einer externen Autor:in zur Auswahl", // "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Authority", "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Referenz", @@ -4573,7 +4573,7 @@ "submission.sections.describe.relationship-lookup.selected": "{{ size }} Items ausgewählt", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Local Authors ({{ count }})", - "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Lokale Autoren ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isAuthorOfPublication": "Lokale Autor:innen ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Local Journals ({{ count }})", "submission.sections.describe.relationship-lookup.search-tab.tab-title.isJournalOfPublication": "Lokale Zeitschriften ({{ count }})", @@ -4585,7 +4585,7 @@ "submission.sections.describe.relationship-lookup.search-tab.tab-title.Publication": "Lokale Publicationen ({{ count }})", // "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Local Authors ({{ count }})", - "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Lokale Autoren ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.Person": "Lokale Autor:innen ({{ 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": "Lokale Organisationseinheiten ({{ count }})", @@ -4973,7 +4973,7 @@ "submission.workflow.tasks.claimed.edit_help": "Wählen Sie diese Option, um die Metadaten des Items zu bearbeiten.", // "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": "Bitte geben Sie den Grund für die Ablehnung der eingereichten Ressource in das Feld unten ein. Bitte geben Sie an ob und wie der/die Einreichende:n das Problem beheben und die Ressource erneut einreichen kann.", + "submission.workflow.tasks.claimed.reject.reason.info": "Bitte geben Sie den Grund für die Ablehnung der eingereichten Ressource in das Feld unten ein. Bitte geben Sie an ob und wie der:die Einreichende:n das Problem beheben und die Ressource erneut einreichen kann.", // "submission.workflow.tasks.claimed.reject.reason.placeholder": "Describe the reason of reject", "submission.workflow.tasks.claimed.reject.reason.placeholder": "Beschreiben Sie den Grund für die Ablehnung", @@ -4988,7 +4988,7 @@ "submission.workflow.tasks.claimed.reject.submit": "Ablehnen", // "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": "Wenn Sie das Item begutachtet und als ungeeignet für die Aufnahme in die Sammlung befunden haben, wählen Sie \"Ablehnen\". Sie haben dann die Möglichkeit dem/der Einreichenden, den Grund für die Ablehnung zu erklären und ob es eine Möglichkeit gibt, durch entsprechenden Änderungen das Item erneut einzureichen.", + "submission.workflow.tasks.claimed.reject_help": "Wenn Sie das Item begutachtet und als ungeeignet für die Aufnahme in die Sammlung befunden haben, wählen Sie \"Ablehnen\". Sie haben dann die Möglichkeit dem:der Einreichenden, den Grund für die Ablehnung zu erklären und ob es eine Möglichkeit gibt, durch entsprechenden Änderungen das Item erneut einzureichen.", // "submission.workflow.tasks.claimed.return": "Return to pool", "submission.workflow.tasks.claimed.return": "Zurück in den gemeinsamen Aufgabenbereich", @@ -5111,22 +5111,22 @@ // "workflow-item.send-back.notification.success.title": "Sent back to submitter", - "workflow-item.send-back.notification.success.title": "An die/den Einreichende:n zurückgeschickt", + "workflow-item.send-back.notification.success.title": "An die:den Einreichende:n zurückgeschickt", // "workflow-item.send-back.notification.success.content": "This workflow item was successfully sent back to the submitter", - "workflow-item.send-back.notification.success.content": "Dieses Workflow-Item wurde erfolgreich an die/den Einreichende:n zurückgeschickt", + "workflow-item.send-back.notification.success.content": "Dieses Workflow-Item wurde erfolgreich an die:den Einreichende:n zurückgeschickt", // "workflow-item.send-back.notification.error.title": "Something went wrong", "workflow-item.send-back.notification.error.title": "Etwas ist schief gelaufen", // "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": "Das Workflow-Item konnte nicht an die/den Einreichende:n zurückgeschickt werden", + "workflow-item.send-back.notification.error.content": "Das Workflow-Item konnte nicht an die:den Einreichende:n zurückgeschickt werden", // "workflow-item.send-back.title": "Send workflow item back to submitter", - "workflow-item.send-back.title": "Workflow-Item an die/den Einreichende:n zurücksenden", + "workflow-item.send-back.title": "Workflow-Item an die:den Einreichende:n zurücksenden", // "workflow-item.send-back.header": "Send workflow item back to submitter", - "workflow-item.send-back.header": "Workflow-Item an die/den Einreichende:n zurücksenden", + "workflow-item.send-back.header": "Workflow-Item an die:den Einreichende:n zurücksenden", // "workflow-item.send-back.button.cancel": "Cancel", "workflow-item.send-back.button.cancel": "Abbrechen", From 2ffb72320221b923d0064fec6fc47a04d34606ac Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Thu, 10 Mar 2022 13:04:22 +0530 Subject: [PATCH 074/570] [CST-5329] Add validate only check in the Import > Metadata page --- .../metadata-import-page.component.html | 4 +++ .../metadata-import-page.component.spec.ts | 25 ++++++++++++++++++- .../metadata-import-page.component.ts | 8 ++++++ src/assets/i18n/en.json5 | 2 ++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html index 42a04b0de6..c70bc45947 100644 --- a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html +++ b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html @@ -1,6 +1,10 @@

    {{'admin.metadata-import.page.help' | translate}}

    +

    + + {{'admin.metadata-import.page.validateOnly' | translate}} +

    { comp.setFile(fileMock); }); - describe('if proceed button is pressed', () => { + describe('if proceed button is pressed without validate only', () => { beforeEach(fakeAsync(() => { + comp.validateOnly = false; const proceed = fixture.debugElement.query(By.css('#proceedButton')).nativeElement; proceed.click(); fixture.detectChanges(); @@ -107,6 +108,28 @@ describe('MetadataImportPageComponent', () => { }); }); + describe('if proceed button is pressed with validate only', () => { + beforeEach(fakeAsync(() => { + comp.validateOnly = true; + const proceed = fixture.debugElement.query(By.css('#proceedButton')).nativeElement; + proceed.click(); + fixture.detectChanges(); + })); + it('metadata-import script is invoked with -f fileName and the mockFile and -v validate-only', () => { + const parameterValues: ProcessParameter[] = [ + Object.assign(new ProcessParameter(), { name: '-f', value: 'filename.txt' }), + Object.assign(new ProcessParameter(), { name: '-v', value: true }), + ]; + expect(scriptService.invoke).toHaveBeenCalledWith(METADATA_IMPORT_SCRIPT_NAME, parameterValues, [fileMock]); + }); + it('success notification is shown', () => { + expect(notificationService.success).toHaveBeenCalled(); + }); + it('redirected to process page', () => { + expect(router.navigateByUrl).toHaveBeenCalledWith('/processes/45'); + }); + }); + describe('if proceed is pressed; but script invoke fails', () => { beforeEach(fakeAsync(() => { jasmine.getEnv().allowRespy(true); 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 3bdcca3084..deb16c0d73 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 @@ -30,6 +30,11 @@ export class MetadataImportPageComponent { */ fileObject: File; + /** + * The validate only flag + */ + validateOnly = true; + public constructor(private location: Location, protected translate: TranslateService, protected notificationsService: NotificationsService, @@ -62,6 +67,9 @@ export class MetadataImportPageComponent { const parameterValues: ProcessParameter[] = [ Object.assign(new ProcessParameter(), { name: '-f', value: this.fileObject.name }), ]; + if (this.validateOnly) { + parameterValues.push(Object.assign(new ProcessParameter(), { name: '-v', value: true })); + } this.scriptDataService.invoke(METADATA_IMPORT_SCRIPT_NAME, parameterValues, [this.fileObject]).pipe( getFirstCompletedRemoteData(), diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index f33a195cfe..426fcb12d2 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -538,6 +538,8 @@ "admin.metadata-import.page.error.addFile": "Select file first!", + "admin.metadata-import.page.validateOnly": "Validate Only", + From 027b281d7a61031370a62f1388d23b8369410808 Mon Sep 17 00:00:00 2001 From: Sufiyan Shaikh Date: Thu, 10 Mar 2022 14:45:53 +0530 Subject: [PATCH 075/570] [CST-5329] Add validate only check in the Import > Metadata page --- .../metadata-import-page.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html index c70bc45947..fb96c4becd 100644 --- a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html +++ b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html @@ -3,7 +3,7 @@

    {{'admin.metadata-import.page.help' | translate}}

    - {{'admin.metadata-import.page.validateOnly' | translate}} + {{'admin.metadata-import.page.validateOnly' | translate}}

    Date: Fri, 11 Mar 2022 12:18:14 +0100 Subject: [PATCH 076/570] [CST-5449] Fix failed test --- .../shared/browse-by/browse-by.component.html | 6 ++--- .../browse-by/browse-by.component.spec.ts | 26 +++++++------------ 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index cc126768e4..3b72ce2d23 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -1,15 +1,15 @@ -

    {{title | translate}}

    +

    {{title | translate}}

    - - +
    diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index 1e739dafbf..601c352705 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -4,20 +4,17 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; -import { SharedModule } from '../shared.module'; import { CommonModule } from '@angular/common'; import { Item } from '../../core/shared/item.model'; import { buildPaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { StoreModule } from '@ngrx/store'; import { TranslateLoaderMock } from '../mocks/translate-loader.mock'; import { RouterTestingModule } from '@angular/router/testing'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; -import { storeModuleConfig } from '../../app.reducer'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; import { ListableObjectComponentLoaderComponent } from '../object-collection/shared/listable-object/listable-object-component-loader.component'; @@ -30,17 +27,18 @@ import { import { BrowseEntry } from '../../core/shared/browse-entry.model'; import { ITEM } from '../../core/shared/item.resource-type'; import { ThemeService } from '../theme-support/theme.service'; -import SpyObj = jasmine.SpyObj; import { SelectableListService } from '../object-list/selectable-list/selectable-list.service'; +import { HostWindowServiceStub } from '../testing/host-window-service.stub'; import { HostWindowService } from '../host-window.service'; -import { CSSVariableService } from '../sass-helper/sass-helper.service'; +import SpyObj = jasmine.SpyObj; @listableObjectComponent(BrowseEntry, ViewMode.ListElement, DEFAULT_CONTEXT, 'custom') @Component({ selector: 'ds-browse-entry-list-element', template: '' }) -class MockThemedBrowseEntryListElementComponent {} +class MockThemedBrowseEntryListElementComponent { +} describe('BrowseByComponent', () => { let comp: BrowseByComponent; @@ -86,10 +84,7 @@ describe('BrowseByComponent', () => { TestBed.configureTestingModule({ imports: [ CommonModule, - TranslateModule.forRoot(), - SharedModule, NgbModule, - StoreModule.forRoot({}, storeModuleConfig), TranslateModule.forRoot({ loader: { provide: TranslateLoader, @@ -97,16 +92,15 @@ describe('BrowseByComponent', () => { } }), RouterTestingModule, - BrowserAnimationsModule + NoopAnimationsModule ], declarations: [], providers: [ - {provide: PaginationService, useValue: paginationService}, - {provide: MockThemedBrowseEntryListElementComponent}, + { provide: PaginationService, useValue: paginationService }, + { provide: MockThemedBrowseEntryListElementComponent }, { provide: ThemeService, useValue: themeService }, - SelectableListService, - HostWindowService, - CSSVariableService + { provide: SelectableListService, useValue: {} }, + { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); From d4ed4ca88350eccaab9f127644ff7843c7d61cfa Mon Sep 17 00:00:00 2001 From: reetagithub <51482276+reetagithub@users.noreply.github.com> Date: Mon, 14 Mar 2022 09:05:48 +0200 Subject: [PATCH 077/570] Corrected a typo --- src/assets/i18n/fi.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/fi.json5 b/src/assets/i18n/fi.json5 index 0348b906c2..02f020a45d 100644 --- a/src/assets/i18n/fi.json5 +++ b/src/assets/i18n/fi.json5 @@ -4120,7 +4120,7 @@ "search.filters.filter.dateSubmitted.placeholder": "Tallennnusajankohta", // "search.filters.filter.discoverable.head": "Private", - "search.filters.filter.discoverable.head": "Ykstyinen", + "search.filters.filter.discoverable.head": "Yksityinen", // "search.filters.filter.withdrawn.head": "Withdrawn", "search.filters.filter.withdrawn.head": "Poistettu käytöstä", From 3a4a10e45385b46ee977fd4282b86b8ed9b140f5 Mon Sep 17 00:00:00 2001 From: William Welling <8352733+wwelling@users.noreply.github.com> Date: Mon, 14 Mar 2022 08:26:22 -0500 Subject: [PATCH 078/570] set default env preboot to false leaving production env to true --- src/environments/environment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/environments/environment.ts b/src/environments/environment.ts index a64ccf2608..dc0e808be0 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -10,7 +10,7 @@ export const environment: Partial = { // Angular Universal settings universal: { - preboot: true, + preboot: false, async: true, time: false } From b21f76456d641192d1b9b65362c0c6a7396be5b1 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Mon, 14 Mar 2022 16:33:10 +0100 Subject: [PATCH 079/570] 88248: #346: Withdrawn item tombstone page --- .../full/full-item-page.component.html | 44 ++++++------ .../full/full-item-page.component.spec.ts | 67 ++++++++++++++++++- .../full/full-item-page.component.ts | 4 +- .../item-page/simple/item-page.component.html | 2 +- .../simple/item-page.component.spec.ts | 62 +++++++++++++++++ .../item-page/simple/item-page.component.ts | 12 +++- .../item-alerts/item-alerts.component.html | 17 +++-- 7 files changed, 178 insertions(+), 30 deletions(-) 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 d2655c4ad0..e71dd92f96 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -4,19 +4,21 @@ -
    - -
    - +
    +
    + +
    + +
    -
    - - - + +
    + @@ -24,14 +26,16 @@ - -
    {{mdEntry.key}}{{mdValue.language}}
    - - - -
    -
    - + + + + + +
    +
    + +
    diff --git a/src/app/item-page/full/full-item-page.component.spec.ts b/src/app/item-page/full/full-item-page.component.spec.ts index b4ab926667..c7643a940e 100644 --- a/src/app/item-page/full/full-item-page.component.spec.ts +++ b/src/app/item-page/full/full-item-page.component.spec.ts @@ -11,12 +11,15 @@ import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { VarDirective } from '../../shared/utils/var.directive'; import { RouterTestingModule } from '@angular/router/testing'; import { Item } from '../../core/shared/item.model'; -import { of as observableOf } from 'rxjs'; +import { BehaviorSubject, of as observableOf } from 'rxjs'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { AuthService } from '../../core/auth/auth.service'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { createRelationshipsObservable } from '../simple/item-types/shared/item.component.spec'; +import { RemoteData } from '../../core/data/remote-data'; const mockItem: Item = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])), @@ -30,6 +33,13 @@ const mockItem: Item = Object.assign(new Item(), { } }); +const mockWithdrawnItem: Item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])), + metadata: [], + relationships: createRelationshipsObservable(), + isWithdrawn: true +}); + const metadataServiceStub = { /* tslint:disable:no-empty */ processRemoteData: () => { @@ -44,6 +54,7 @@ describe('FullItemPageComponent', () => { let authService: AuthService; let routeStub: ActivatedRouteStub; let routeData; + let authorizationDataService: AuthorizationDataService; @@ -61,6 +72,10 @@ describe('FullItemPageComponent', () => { data: observableOf(routeData) }); + authorizationDataService = jasmine.createSpyObj('authorizationDataService', { + isAuthorized: observableOf(false), + }); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ loader: { @@ -74,6 +89,7 @@ describe('FullItemPageComponent', () => { { provide: ItemDataService, useValue: {} }, { provide: MetadataService, useValue: metadataServiceStub }, { provide: AuthService, useValue: authService }, + { provide: AuthorizationDataService, useValue: authorizationDataService }, ], schemas: [NO_ERRORS_SCHEMA] @@ -111,4 +127,53 @@ describe('FullItemPageComponent', () => { expect(simpleViewBtn).toBeFalsy(); }); })); + + describe('when the item is withdrawn and the user is an admin', () => { + beforeEach(() => { + comp.isAdmin$ = observableOf(true); + comp.itemRD$ = new BehaviorSubject>(createSuccessfulRemoteDataObject(mockWithdrawnItem)); + fixture.detectChanges(); + }); + + it('should display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('.full-item-info')); + expect(objectLoader.nativeElement).toBeDefined(); + }); + }); + describe('when the item is withdrawn and the user is not an admin', () => { + beforeEach(() => { + comp.itemRD$ = new BehaviorSubject>(createSuccessfulRemoteDataObject(mockWithdrawnItem)); + fixture.detectChanges(); + }); + + it('should not display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('.full-item-info')); + expect(objectLoader).toBeNull(); + }); + }); + + describe('when the item is not withdrawn and the user is an admin', () => { + beforeEach(() => { + comp.isAdmin$ = observableOf(true); + comp.itemRD$ = new BehaviorSubject>(createSuccessfulRemoteDataObject(mockItem)); + fixture.detectChanges(); + }); + + it('should display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('.full-item-info')); + expect(objectLoader.nativeElement).toBeDefined(); + }); + }); + + describe('when the item is not withdrawn and the user is not an admin', () => { + beforeEach(() => { + comp.itemRD$ = new BehaviorSubject>(createSuccessfulRemoteDataObject(mockItem)); + fixture.detectChanges(); + }); + + it('should display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('.full-item-info')); + expect(objectLoader.nativeElement).toBeDefined(); + }); + }); }); diff --git a/src/app/item-page/full/full-item-page.component.ts b/src/app/item-page/full/full-item-page.component.ts index 7f1b6de614..369769c77d 100644 --- a/src/app/item-page/full/full-item-page.component.ts +++ b/src/app/item-page/full/full-item-page.component.ts @@ -15,6 +15,7 @@ import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; import { AuthService } from '../../core/auth/auth.service'; import { Location } from '@angular/common'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; /** @@ -46,8 +47,9 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit, router: Router, items: ItemDataService, authService: AuthService, + authorizationService: AuthorizationDataService, private _location: Location) { - super(route, router, items, authService); + super(route, router, items, authService, authorizationService); } /*** AoT inheritance fix, will hopefully be resolved in the near future **/ diff --git a/src/app/item-page/simple/item-page.component.html b/src/app/item-page/simple/item-page.component.html index e843155d10..3e834b53db 100644 --- a/src/app/item-page/simple/item-page.component.html +++ b/src/app/item-page/simple/item-page.component.html @@ -4,7 +4,7 @@ - +
    diff --git a/src/app/item-page/simple/item-page.component.spec.ts b/src/app/item-page/simple/item-page.component.spec.ts index ff5a1e38d5..2ad0bbb272 100644 --- a/src/app/item-page/simple/item-page.component.spec.ts +++ b/src/app/item-page/simple/item-page.component.spec.ts @@ -21,6 +21,7 @@ import { } from '../../shared/remote-data.utils'; import { AuthService } from '../../core/auth/auth.service'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; const mockItem: Item = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])), @@ -28,10 +29,18 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); +const mockWithdrawnItem: Item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])), + metadata: [], + relationships: createRelationshipsObservable(), + isWithdrawn: true +}); + describe('ItemPageComponent', () => { let comp: ItemPageComponent; let fixture: ComponentFixture; let authService: AuthService; + let authorizationDataService: AuthorizationDataService; const mockMetadataService = { /* tslint:disable:no-empty */ @@ -48,6 +57,9 @@ describe('ItemPageComponent', () => { isAuthenticated: observableOf(true), setRedirectUrl: {} }); + authorizationDataService = jasmine.createSpyObj('authorizationDataService', { + isAuthorized: observableOf(false), + }); TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ @@ -63,6 +75,7 @@ describe('ItemPageComponent', () => { { provide: MetadataService, useValue: mockMetadataService }, { provide: Router, useValue: {} }, { provide: AuthService, useValue: authService }, + { provide: AuthorizationDataService, useValue: authorizationDataService }, ], schemas: [NO_ERRORS_SCHEMA] @@ -102,4 +115,53 @@ describe('ItemPageComponent', () => { }); }); + describe('when the item is withdrawn and the user is an admin', () => { + beforeEach(() => { + comp.isAdmin$ = observableOf(true); + comp.itemRD$ = createSuccessfulRemoteDataObject$(mockWithdrawnItem); + fixture.detectChanges(); + }); + + it('should display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader')); + expect(objectLoader.nativeElement).toBeDefined(); + }); + }); + describe('when the item is withdrawn and the user is not an admin', () => { + beforeEach(() => { + comp.itemRD$ = createSuccessfulRemoteDataObject$(mockWithdrawnItem); + fixture.detectChanges(); + }); + + it('should not display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader')); + expect(objectLoader).toBeNull(); + }); + }); + + describe('when the item is not withdrawn and the user is an admin', () => { + beforeEach(() => { + comp.isAdmin$ = observableOf(true); + comp.itemRD$ = createSuccessfulRemoteDataObject$(mockItem); + fixture.detectChanges(); + }); + + it('should display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader')); + expect(objectLoader.nativeElement).toBeDefined(); + }); + }); + + describe('when the item is not withdrawn and the user is not an admin', () => { + beforeEach(() => { + comp.itemRD$ = createSuccessfulRemoteDataObject$(mockItem); + fixture.detectChanges(); + }); + + it('should display the item', () => { + const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader')); + expect(objectLoader.nativeElement).toBeDefined(); + }); + }); + }); diff --git a/src/app/item-page/simple/item-page.component.ts b/src/app/item-page/simple/item-page.component.ts index cc23ba86d5..0559dc2219 100644 --- a/src/app/item-page/simple/item-page.component.ts +++ b/src/app/item-page/simple/item-page.component.ts @@ -1,4 +1,4 @@ -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; @@ -13,6 +13,8 @@ import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../../core/shar import { ViewMode } from '../../core/shared/view-mode.model'; import { AuthService } from '../../core/auth/auth.service'; import { getItemPageRoute } from '../item-page-routing-paths'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; /** * This component renders a simple item page. @@ -48,11 +50,17 @@ export class ItemPageComponent implements OnInit { */ itemPageRoute$: Observable; + /** + * Whether the current user is an admin or not + */ + isAdmin$: Observable; + constructor( protected route: ActivatedRoute, private router: Router, private items: ItemDataService, private authService: AuthService, + private authorizationService: AuthorizationDataService ) { } /** @@ -67,5 +75,7 @@ export class ItemPageComponent implements OnInit { getAllSucceededRemoteDataPayload(), map((item) => getItemPageRoute(item)) ); + + this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf); } } diff --git a/src/app/shared/item/item-alerts/item-alerts.component.html b/src/app/shared/item/item-alerts/item-alerts.component.html index fce5c038e5..740ea2595d 100644 --- a/src/app/shared/item/item-alerts/item-alerts.component.html +++ b/src/app/shared/item/item-alerts/item-alerts.component.html @@ -1,8 +1,13 @@
    -
    - -
    -
    - -
    +
    + +
    +
    + +
    + {{'item.alerts.withdrawn' | translate}} + {{"404.link.home-page" | translate}} +
    +
    +
    From 90e469ee167a3c37046825f0aea0f98ec9b7064f Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Mon, 14 Mar 2022 17:21:34 +0100 Subject: [PATCH 080/570] Fix LGTM issue --- src/app/item-page/simple/item-page.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/item-page/simple/item-page.component.ts b/src/app/item-page/simple/item-page.component.ts index 0559dc2219..95fbd7a2e0 100644 --- a/src/app/item-page/simple/item-page.component.ts +++ b/src/app/item-page/simple/item-page.component.ts @@ -1,4 +1,4 @@ -import { map, switchMap } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; From 97dc241c5208d949826a6ebd930615adb47c31c7 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 17 Mar 2022 13:00:00 +0100 Subject: [PATCH 081/570] 88300: Issue 1379 - Angular: Browse-by Subject does not allow to proceed to next pages once filtered --- .../shared/browse-by/browse-by.component.html | 23 +++- .../browse-by/browse-by.component.spec.ts | 20 +++- .../shared/browse-by/browse-by.component.ts | 19 ++- .../text/starts-with-text.component.html | 19 --- .../text/starts-with-text.component.spec.ts | 110 ------------------ src/app/shared/testing/route-service.stub.ts | 7 +- src/assets/i18n/en.json5 | 6 +- 7 files changed, 65 insertions(+), 139 deletions(-) diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index c133324681..4837c92db8 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -12,6 +12,10 @@
- - + +
- 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 8b104cc9b1..ad2863034a 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,7 @@ import { Component } from '@angular/core'; -import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; +import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component'; @listableObjectComponent('Person', ViewMode.StandalonePage) @Component({ @@ -12,5 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh /** * The component for displaying metadata and relations of an item of the type Person */ -export class PersonComponent extends ItemComponent { +export class PersonComponent extends VersionedItemComponent { } 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 8f2ff6adcd..243dae8b43 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 @@ -3,6 +3,9 @@ {{'project.page.titleprefix' | translate}}
+
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.ts b/src/app/entity-groups/research-entities/item-pages/project/project.component.ts index e53d8afd69..066427fc0d 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.ts +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; -import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; +import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component'; @listableObjectComponent('Project', ViewMode.StandalonePage) @Component({ @@ -12,5 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh /** * The component for displaying metadata and relations of an item of the type Project */ -export class ProjectComponent extends ItemComponent { +export class ProjectComponent extends VersionedItemComponent { } diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index bace9fcd0a..667dee96f5 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -12,6 +12,9 @@ {{'publication.page.titleprefix' | translate}}
+
diff --git a/src/app/item-page/simple/item-types/publication/publication.component.ts b/src/app/item-page/simple/item-types/publication/publication.component.ts index 5ace8d0473..ba5037a104 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/item-page/simple/item-types/publication/publication.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ItemComponent } from '../shared/item.component'; 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'; /** * Component that represents a publication Item page @@ -14,6 +14,6 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh templateUrl: './publication.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PublicationComponent extends ItemComponent { +export class PublicationComponent extends VersionedItemComponent { } From 26f821e8655d595796005670dd81e6c07cb68d1e Mon Sep 17 00:00:00 2001 From: louiserumpf <101128103+louiserumpf@users.noreply.github.com> Date: Tue, 22 Mar 2022 11:18:44 +0100 Subject: [PATCH 090/570] updated German translation --- src/assets/i18n/de.json5 | 236 +++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index bbac4ccdef..bf7df7619a 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -4,7 +4,7 @@ "401.help": "Sie sind nicht berechtigt, auf diese Seite zuzugreifen. Mit dem Link unten kommen Sie zurück zur Startseite.", // "401.link.home-page": "Take me to the home page", - "401.link.home-page": "Bringe mich zur Startseite", + "401.link.home-page": "Zur Startseite", // "401.unauthorized": "unauthorized", "401.unauthorized": "unautorisiert", @@ -12,10 +12,10 @@ // "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": "Sie haben keine Berechtigung, auf diese Seite zuzugreifen. Mit dem Link unten kommen Sie zurück zur Startseite.", + "403.help": "Sie sind nicht berechtigt, auf diese Seite zuzugreifen. Über den Button unten auf der Seite gelangen Sie zurück zur Startseite.", // "403.link.home-page": "Take me to the home page", - "403.link.home-page": "Bringe mich zur Startseite", + "403.link.home-page": "Zur Startseite", // "403.forbidden": "forbidden", "403.forbidden": "verboten", @@ -23,22 +23,22 @@ // "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": "Die Seite, die Sie aufrufen wollten, konnte nicht gefunden werden. Sie könnte verschoben oder gelöscht worden sein. Mit dem Link unten kommen Sie zurück zur Startseite. ", + "404.help": "Die Seite konnte nicht gefunden werden. Eventuell wurde sie verschoben oder gelöscht. Über den Button unten auf der Seite gelangen Sie zurück zur Startseite.", // "404.link.home-page": "Take me to the home page", - "404.link.home-page": "Zurück zur Startseite", + "404.link.home-page": "Zur Startseite", // "404.page-not-found": "page not found", "404.page-not-found": "Seite nicht gefunden", // "admin.curation-tasks.breadcrumbs": "System curation tasks", - "admin.curation-tasks.breadcrumbs": "Datenpflegeroutinen", + "admin.curation-tasks.breadcrumbs": "Wartungsarbeiten", // "admin.curation-tasks.title": "System curation tasks", - "admin.curation-tasks.title": "Datenpflegeroutinen", + "admin.curation-tasks.title": "Wartungsarbeiten", // "admin.curation-tasks.header": "System curation tasks", - "admin.curation-tasks.header": "Datenpflegeroutinen", + "admin.curation-tasks.header": "Wartungsarbeiten", // "admin.registries.bitstream-formats.breadcrumbs": "Format registry", "admin.registries.bitstream-formats.breadcrumbs": "Referenzliste der Dateiformate", @@ -47,10 +47,10 @@ "admin.registries.bitstream-formats.create.breadcrumbs": "Dateiformat", // "admin.registries.bitstream-formats.create.failure.content": "An error occurred while creating the new bitstream format.", - "admin.registries.bitstream-formats.create.failure.content": "Ein Fehler ist beim Anlegen eines neuen Dateiformates aufgetreten.", + "admin.registries.bitstream-formats.create.failure.content": "Beim Anlegen eines neuen Dateiformates ist ein Fehler aufgetreten.", // "admin.registries.bitstream-formats.create.failure.head": "Failure", - "admin.registries.bitstream-formats.create.failure.head": "Fehler", + "admin.registries.bitstream-formats.create.failure.head": "Aktion fehlgeschlagen", // "admin.registries.bitstream-formats.create.head": "Create Bitstream format", "admin.registries.bitstream-formats.create.head": "Neues Dateiformat anlegen", @@ -62,19 +62,19 @@ "admin.registries.bitstream-formats.create.success.content": "Das neue Dateiformat wurde erfolgreich angelegt.", // "admin.registries.bitstream-formats.create.success.head": "Success", - "admin.registries.bitstream-formats.create.success.head": "Erfolg", + "admin.registries.bitstream-formats.create.success.head": "Aktion erfolgreich", // "admin.registries.bitstream-formats.delete.failure.amount": "Failed to remove {{ amount }} format(s)", - "admin.registries.bitstream-formats.delete.failure.amount": "{{ amount }} Format(e) konnte(n) nicht gelöscht werden", + "admin.registries.bitstream-formats.delete.failure.amount": "{{ amount }} Dateiformat(e) konnte(n) nicht gelöscht werden.", // "admin.registries.bitstream-formats.delete.failure.head": "Failure", "admin.registries.bitstream-formats.delete.failure.head": "Fehler", // "admin.registries.bitstream-formats.delete.success.amount": "Successfully removed {{ amount }} format(s)", - "admin.registries.bitstream-formats.delete.success.amount": "{{ amount }} Format(e) erfolgreich gelöscht", + "admin.registries.bitstream-formats.delete.success.amount": "{{ amount }} Dateiformat(e) wurden erfolgreich gelöscht.", // "admin.registries.bitstream-formats.delete.success.head": "Success", - "admin.registries.bitstream-formats.delete.success.head": "Erfolg", + "admin.registries.bitstream-formats.delete.success.head": "Aktion erfolgreich", // "admin.registries.bitstream-formats.description": "This list of bitstream formats provides information about known formats and their support level.", "admin.registries.bitstream-formats.description": "Die Liste der Dateiformate enthält Informationen über bekannte Formate und deren Unterstützungsgrad.", @@ -89,19 +89,19 @@ "admin.registries.bitstream-formats.edit.description.label": "Beschreibung", // "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": "Extensionen sind Dateieindungen, welche zur Identifizierung der Formate von hochgeladenen Dateien dienen. Sie können mehrere Endungen pro Format angeben.", + "admin.registries.bitstream-formats.edit.extensions.hint": "Hier können Sie Endungen angeben, anhand derer das Format hochgeladener Dateien automatisch erkannt wird. Sie können mehrere Endungen pro Dateiformat angeben.", // "admin.registries.bitstream-formats.edit.extensions.label": "File extensions", "admin.registries.bitstream-formats.edit.extensions.label": "Dateiendungen", // "admin.registries.bitstream-formats.edit.extensions.placeholder": "Enter a file extension without the dot", - "admin.registries.bitstream-formats.edit.extensions.placeholder": "Geben Sie die Endung des Dateinamens ohne Punkt ein", + "admin.registries.bitstream-formats.edit.extensions.placeholder": "Bitte geben Sie die Dateiendung ohne Punkt ein.", // "admin.registries.bitstream-formats.edit.failure.content": "An error occurred while editing the bitstream format.", - "admin.registries.bitstream-formats.edit.failure.content": "Ein Fehler ist beim Editieren des Dateiformates", + "admin.registries.bitstream-formats.edit.failure.content": "Beim Bearbeiten des Dateiformats ist ein Fehler aufgetreten.", // "admin.registries.bitstream-formats.edit.failure.head": "Failure", - "admin.registries.bitstream-formats.edit.failure.head": "Fehler", + "admin.registries.bitstream-formats.edit.failure.head": "Aktion fehlgeschlagen", // "admin.registries.bitstream-formats.edit.head": "Bitstream format: {{ format }}", "admin.registries.bitstream-formats.edit.head": "Dateiformat: {{ format }}", @@ -110,13 +110,13 @@ "admin.registries.bitstream-formats.edit.internal.hint": "Dateiformate, die als intern gekennzeichnet sind, dienen administrativen Zwecken und bleiben dem:der Endnutzer:in verborgen.", // "admin.registries.bitstream-formats.edit.internal.label": "Internal", - "admin.registries.bitstream-formats.edit.internal.label": "Intern", + "admin.registries.bitstream-formats.edit.internal.label": "intern", // "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": "Der MIME Typ dieses Formates. Er muss nicht einzigartig sein.", + "admin.registries.bitstream-formats.edit.mimetype.hint": "MIME-Type dieses Dateiformats. Ein MIME-Type kann auch mehreren Dateiformaten zugeordnet werden.", // "admin.registries.bitstream-formats.edit.mimetype.label": "MIME Type", - "admin.registries.bitstream-formats.edit.mimetype.label": "MIME Typ", + "admin.registries.bitstream-formats.edit.mimetype.label": "MIME-Type", // "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": "Ein eindeutiger Name für dieses Format, (z.B. Microsoft Word XP oder Microsoft Word 2000)", @@ -128,10 +128,10 @@ "admin.registries.bitstream-formats.edit.success.content": "Das Dateiformat wurde erfolgreich bearbeitet.", // "admin.registries.bitstream-formats.edit.success.head": "Success", - "admin.registries.bitstream-formats.edit.success.head": "Erfolg", + "admin.registries.bitstream-formats.edit.success.head": "Aktion erfolgreich", // "admin.registries.bitstream-formats.edit.supportLevel.hint": "The level of support your institution pledges for this format.", - "admin.registries.bitstream-formats.edit.supportLevel.hint": "Der Unterstützungsgrad den Ihre Einrichtung für dieses Format anbietet.", + "admin.registries.bitstream-formats.edit.supportLevel.hint": "Der Unterstützungsgrad, den Ihre Einrichtung für dieses Format anbietet", // "admin.registries.bitstream-formats.edit.supportLevel.label": "Support level", "admin.registries.bitstream-formats.edit.supportLevel.label": "Unterstützungsgrad", @@ -146,13 +146,13 @@ "admin.registries.bitstream-formats.table.delete": "Auswahl löschen", // "admin.registries.bitstream-formats.table.deselect-all": "Deselect all", - "admin.registries.bitstream-formats.table.deselect-all": "Alle abwählen", + "admin.registries.bitstream-formats.table.deselect-all": "Nichts auswählen", // "admin.registries.bitstream-formats.table.internal": "internal", "admin.registries.bitstream-formats.table.internal": "intern", // "admin.registries.bitstream-formats.table.mimetype": "MIME Type", - "admin.registries.bitstream-formats.table.mimetype": "MIME Typ", + "admin.registries.bitstream-formats.table.mimetype": "MIME-Type", // "admin.registries.bitstream-formats.table.name": "Name", "admin.registries.bitstream-formats.table.name": "Name", @@ -199,7 +199,7 @@ "admin.registries.metadata.head": "Metadatenreferenzliste", // "admin.registries.metadata.schemas.no-items": "No metadata schemas to show.", - "admin.registries.metadata.schemas.no-items": "Es gibt keinen Metadatenschemata in der Referenzliste.", + "admin.registries.metadata.schemas.no-items": "Es gibt keine Metadatenschemata in der Referenzliste.", // "admin.registries.metadata.schemas.table.delete": "Delete selected", "admin.registries.metadata.schemas.table.delete": "Auswahl löschen", @@ -261,7 +261,7 @@ "admin.registries.schema.notification.created": "\"{{prefix}}\" wurde erfolgreich angelegt.", // "admin.registries.schema.notification.deleted.failure": "Failed to delete {{amount}} metadata schemas", - "admin.registries.schema.notification.deleted.failure": "{{amount}} Metadatenschema(ta) konnte(n) nicht gelöscht werden", + "admin.registries.schema.notification.deleted.failure": "{{amount}} Metadatenschema(ta) konnte(n) nicht gelöscht werden.", // "admin.registries.schema.notification.deleted.success": "Successfully deleted {{amount}} metadata schemas", "admin.registries.schema.notification.deleted.success": "{{amount}} Metadatenschema(ta) wurde(n) gelöscht.", @@ -368,37 +368,37 @@ "admin.access-control.epeople.form.email": "E-Mail-Adresse", // "admin.access-control.epeople.form.emailHint": "Must be valid e-mail address", - "admin.access-control.epeople.form.emailHint": "Muss eine gültige E-Mail-Adresse sein", + "admin.access-control.epeople.form.emailHint": "Bitte geben Sie eine gültige E-Mail-Adresse an.", // "admin.access-control.epeople.form.canLogIn": "Can log in", "admin.access-control.epeople.form.canLogIn": "Darf sich anmelden", // "admin.access-control.epeople.form.requireCertificate": "Requires certificate", - "admin.access-control.epeople.form.requireCertificate": "Zertifikat benötigt", + "admin.access-control.epeople.form.requireCertificate": "Es wird ein Zertifikat benötigt.", // "admin.access-control.epeople.form.notification.created.success": "Successfully created EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.created.success": "Person \"{{name}}\" erfolgreich erstellt", + "admin.access-control.epeople.form.notification.created.success": "Die Person \"{{name}}\" wurde erstellt.", // "admin.access-control.epeople.form.notification.created.failure": "Failed to create EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.created.failure": "Person \"{{name}}\" konnte nicht erstellt werden", + "admin.access-control.epeople.form.notification.created.failure": "Die Person \"{{name}}\" konnte nicht erstellt werden.", // "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": "Person \"{{name}}\" konnte nicht erstellt werden, E-Mail \"{{email}}\" wird bereits verwendet.", + "admin.access-control.epeople.form.notification.created.failure.emailInUse": "Die Person \"{{name}}\" konnte nicht erstellt werden, weil die E-Mail-Adresse \"{{email}}\" bereits verwendet wird.", // "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": "Person \"{{name}}\" konnte nicht bearbeitet werden, E-Mail \"{{email}}\" wird bereits verwendet.", + "admin.access-control.epeople.form.notification.edited.failure.emailInUse": "Die Person \"{{name}}\" konnte nicht bearbeitet werden, weil die E-Mail-Adresse \"{{email}}\" bereits verwendet wird.", // "admin.access-control.epeople.form.notification.edited.success": "Successfully edited EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.edited.success": "Person \"{{name}}\" erfolgreich bearbeitet", + "admin.access-control.epeople.form.notification.edited.success": "Die Person \"{{name}}\" wurde erfolgreich bearbeitet.", // "admin.access-control.epeople.form.notification.edited.failure": "Failed to edit EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.edited.failure": "Person \"{{name}}\" konnte nicht bearbeitet werden", + "admin.access-control.epeople.form.notification.edited.failure": "Die Person \"{{name}}\" konnte nicht bearbeitet werden.", // "admin.access-control.epeople.form.notification.deleted.success": "Successfully deleted EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.deleted.success": "Person \"{{name}}\" erfolgreich gelöscht", + "admin.access-control.epeople.form.notification.deleted.success": "Die Person \"{{name}}\" wurde gelöscht.", // "admin.access-control.epeople.form.notification.deleted.failure": "Failed to delete EPerson \"{{name}}\"", - "admin.access-control.epeople.form.notification.deleted.failure": "Person \"{{name}}\" konnte nicht gelöscht werden", + "admin.access-control.epeople.form.notification.deleted.failure": "Die Person \"{{name}}\" konnte nicht gelöscht werden.", // "admin.access-control.epeople.form.groupsEPersonIsMemberOf": "Member of these groups:", "admin.access-control.epeople.form.groupsEPersonIsMemberOf": "Mitglied dieser Gruppen:", @@ -410,7 +410,7 @@ "admin.access-control.epeople.form.table.name": "Name", // "admin.access-control.epeople.form.memberOfNoGroups": "This EPerson is not a member of any groups", - "admin.access-control.epeople.form.memberOfNoGroups": "Diese Person ist kein Mitglied von einer Gruppe", + "admin.access-control.epeople.form.memberOfNoGroups": "Diese Person ist kein Mitglied einer Gruppe.", // "admin.access-control.epeople.form.goToGroups": "Add to groups", "admin.access-control.epeople.form.goToGroups": "Zu Gruppen hinzufügen", @@ -573,16 +573,16 @@ "admin.access-control.groups.form.members-list.table.edit.buttons.remove": "Mitglieder mit dem Namen \"{{name}}\" entfernen", // "admin.access-control.groups.form.members-list.notification.success.addMember": "Successfully added member: \"{{name}}\"", - "admin.access-control.groups.form.members-list.notification.success.addMember": "Mitglied: \"{{name}}\" erfolgreich hinzugefügt", + "admin.access-control.groups.form.members-list.notification.success.addMember": "Das Mitglied \"{{name}}\" wurde hinzugefügt.", // "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": "Mitglied: \"{{name}}\" konnte nicht hinzugefügt werden", + "admin.access-control.groups.form.members-list.notification.failure.addMember": "Das Mitglied \"{{name}}\" konnte nicht hinzugefügt werden.", // "admin.access-control.groups.form.members-list.notification.success.deleteMember": "Successfully deleted member: \"{{name}}\"", - "admin.access-control.groups.form.members-list.notification.success.deleteMember": "Mitglied: \"{{name}}\" erfolgreich gelöscht", + "admin.access-control.groups.form.members-list.notification.success.deleteMember": "Das Mitglied \"{{name}}\" wurde gelöscht.", // "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": "Mitglied: \"{{name}}\" konnte nicht gelöscht werden", + "admin.access-control.groups.form.members-list.notification.failure.deleteMember": "Das Mitglied \"{{name}}\" konnte nicht gelöscht werden.", // "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": "Mitglied mit dem Namen \"{{name}}\" hinzufügen", @@ -654,7 +654,7 @@ "admin.access-control.groups.form.subgroups-list.no-items": "Keine Gruppen gefunden, die dies in ihrem Namen oder als UUID haben", // "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": "Noch keine Untergruppen in der Gruppe.", + "admin.access-control.groups.form.subgroups-list.no-subgroups-yet": "Die Gruppe hat noch keine Untergruppen.", // "admin.access-control.groups.form.return": "Return to groups", "admin.access-control.groups.form.return": "Zurück zu den Gruppen", @@ -662,7 +662,7 @@ // "admin.search.breadcrumbs": "Administrative Search", - "admin.search.breadcrumbs": "Administrative Suche", + "admin.search.breadcrumbs": "Admin-Suche", // "admin.search.collection.edit": "Edit", "admin.search.collection.edit": "Bearbeiten", @@ -677,10 +677,10 @@ "admin.search.item.edit": "Bearbeiten", // "admin.search.item.make-private": "Make Private", - "admin.search.item.make-private": "Privat stellen", + "admin.search.item.make-private": "Verbergen", // "admin.search.item.make-public": "Make Public", - "admin.search.item.make-public": "Öffentlich stellen", + "admin.search.item.make-public": "Öffentlich anzeigen", // "admin.search.item.move": "Move", "admin.search.item.move": "Verschieben", @@ -692,22 +692,22 @@ "admin.search.item.withdraw": "Zurückziehen", // "admin.search.title": "Administrative Search", - "admin.search.title": "Administrative Suche", + "admin.search.title": "Admin-Suche", // "administrativeView.search.results.head": "Administrative Search", - "administrativeView.search.results.head": "Administrative Suche", + "administrativeView.search.results.head": "Admin-Suche", // "admin.workflow.breadcrumbs": "Administer Workflow", - "admin.workflow.breadcrumbs": "Geschäftsgänge verwalten", + "admin.workflow.breadcrumbs": "Workflows verwalten", // "admin.workflow.title": "Administer Workflow", - "admin.workflow.title": "Geschäftsgänghe verwalten", + "admin.workflow.title": "Workflows verwalten", // "admin.workflow.item.workflow": "Workflow", - "admin.workflow.item.workflow": "Geschäftsgänge", + "admin.workflow.item.workflow": "Workflows", // "admin.workflow.item.delete": "Delete", "admin.workflow.item.delete": "Löschen", @@ -727,13 +727,13 @@ "admin.metadata-import.page.header": "Metadaten importieren", // "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": "Zum Importieren von Metadaten im Batch-Verfahren, wählen Sie eine CSV-Datei zum Hochladen aus, oder ziehen Sie diese in das Browser-Fenster", + "admin.metadata-import.page.help": "Um Metadaten im Batch-Verfahren zu importieren, wählen Sie bitte eine CSV-Datei zum Hochladen aus oder ziehen Sie diese ins Browser-Fenster.", // "admin.metadata-import.page.dropMsg": "Drop a metadata CSV to import", - "admin.metadata-import.page.dropMsg": "Ziehen Sie eine CSV-Datei mit Metadaten zum Importieren in das Browser-Fenster", + "admin.metadata-import.page.dropMsg": "Ziehen Sie eine CSV-Datei mit Metadaten zum Importieren in das Browser-Fenster.", // "admin.metadata-import.page.dropMsgReplace": "Drop to replace the metadata CSV to import", - "admin.metadata-import.page.dropMsgReplace": "Legen Sie ab, um die CSV-Datei für den Import zu ersetzen.", + "admin.metadata-import.page.dropMsgReplace": "Legen Sie eine neue CSV-Datei hier ab, um die Importdatei zu ersetzen.", // "admin.metadata-import.page.button.return": "Return", "admin.metadata-import.page.button.return": "Zurück", @@ -742,7 +742,7 @@ "admin.metadata-import.page.button.proceed": "Fortfahren", // "admin.metadata-import.page.error.addFile": "Select file first!", - "admin.metadata-import.page.error.addFile": "Zuerst Datei auswählen!", + "admin.metadata-import.page.error.addFile": "Bitte wählen Sie zuerst eine Datei aus.", @@ -751,7 +751,7 @@ "auth.errors.invalid-user": "Passwort oder E-Mail-Adresse ungültig.", // "auth.messages.expired": "Your session has expired. Please log in again.", - "auth.messages.expired": "Ihre Sitzung ist abgelaufen, bitte melden Sie sich erneut an.", + "auth.messages.expired": "Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.", @@ -759,7 +759,7 @@ "bitstream.edit.bitstream": "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": "Optional können Sie eine kurze Beschreibung der Datei angeben, zum Beispiel \"Main article\" oder \"Experiment data readings\".", + "bitstream.edit.form.description.hint": "Hier können Sie eine kurze Beschreibung der Datei angeben, zum Beispiel \"Artikel\" oder \"Tabellenhanhang\".", // "bitstream.edit.form.description.label": "Description", "bitstream.edit.form.description.label": "Beschreibung", @@ -769,7 +769,7 @@ // "bitstream.edit.form.embargo.label": "Embargo until specific date", - "bitstream.edit.form.embargo.label": "Embargo bis zu einem bestimmten Datum", + "bitstream.edit.form.embargo.label": "Sperrfrist", // "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": "Ändern Sie den Dateinamen für die Datei. Beachten Sie, dass sich dadurch die angezeigte Datei-URL ändert, aber alte Links weiterhin funktionieren, solange sich die Sequenz-ID nicht ändert.", @@ -793,7 +793,7 @@ "bitstream.edit.form.selectedFormat.label": "Ausgewähltes Format", // "bitstream.edit.form.selectedFormat.unknown": "Format not in list", - "bitstream.edit.form.selectedFormat.unknown": "Format nicht in der Liste", + "bitstream.edit.form.selectedFormat.unknown": "Anderes Format", // "bitstream.edit.notifications.error.format.title": "An error occurred saving the bitstream's format", "bitstream.edit.notifications.error.format.title": "Beim Speichern des Dateiformats ist ein Fehler aufgetreten", @@ -846,10 +846,10 @@ "browse.metadata.dateissued.breadcrumbs": "Auflistung nach Datum", // "browse.metadata.subject.breadcrumbs": "Browse by Subject", - "browse.metadata.subject.breadcrumbs": "Auflistung nach Schlagwort", + "browse.metadata.subject.breadcrumbs": "Schlagwörter", // "browse.metadata.title.breadcrumbs": "Browse by Title", - "browse.metadata.title.breadcrumbs": "Auflistung nach Titel", + "browse.metadata.title.breadcrumbs": "Titel", // "browse.startsWith.choose_start": "(Choose start)", "browse.startsWith.choose_start": "(Startpunkt wählen)", @@ -1023,7 +1023,7 @@ "collection.edit.logo.label": "Sammlungslogo", // "collection.edit.logo.notifications.add.error": "Uploading Collection logo failed. Please verify the content before retrying.", - "collection.edit.logo.notifications.add.error": "Hochladen des Sammlungslogos fehlgeschlagen. Bitte überprüfen Sie den Inhalt, bevor Sie es nochmal versuchen.", + "collection.edit.logo.notifications.add.error": "Hochladen des Sammlungslogos fehlgeschlagen. Bitte überprüfen Sie den Inhalt, bevor Sie es erneut versuchen.", // "collection.edit.logo.notifications.add.success": "Upload Collection logo successful.", "collection.edit.logo.notifications.add.success": "Sammlungslogo erfolgreich hochgeladen.", @@ -1038,7 +1038,7 @@ "collection.edit.logo.notifications.delete.error.title": "Fehler beim Löschen des Logos", // "collection.edit.logo.upload": "Drop a Collection Logo to upload", - "collection.edit.logo.upload": "Ziehen Sie ein Logo herüber, um es hochzuladen", + "collection.edit.logo.upload": "Ziehen Sie ein Logo herüber, um es hochzuladen.", @@ -1090,10 +1090,10 @@ "collection.edit.tabs.source.form.metadataConfigId": "Metadatenformat", // "collection.edit.tabs.source.form.oaiSetId": "OAI specific set id", - "collection.edit.tabs.source.form.oaiSetId": "OAI spezifische Set-ID", + "collection.edit.tabs.source.form.oaiSetId": "OAI-spezifische Set-ID", // "collection.edit.tabs.source.form.oaiSource": "OAI Provider", - "collection.edit.tabs.source.form.oaiSource": "OAI Anbieter", + "collection.edit.tabs.source.form.oaiSource": "OAI-Anbieter", // "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": "Bezieht Metadaten und Dateien (ORE-Schnittstelle erforderlich)", @@ -1108,7 +1108,7 @@ "collection.edit.tabs.source.head": "Herkunft des Inhalts", // "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": "Die Änderungen wurden verworfen. Um die Änderungen wieder einzusetzen klicken Sie auf den 'Rückgängig' Knopf", + "collection.edit.tabs.source.notifications.discarded.content": "Die Änderungen wurden verworfen. Bitte klicken Sie auf \"Rückgängig\", um sie wieder einzusetzen.", // "collection.edit.tabs.source.notifications.discarded.title": "Changed discarded", "collection.edit.tabs.source.notifications.discarded.title": "Änderungen verworfen", @@ -1120,7 +1120,7 @@ "collection.edit.tabs.source.notifications.invalid.title": "Metadaten ungültig", // "collection.edit.tabs.source.notifications.saved.content": "Your changes to this collection's content source were saved.", - "collection.edit.tabs.source.notifications.saved.content": "Ihre Änderunge bezüglich der Quelle für den Inhalt der Sammlung wurden gespeichert.", + "collection.edit.tabs.source.notifications.saved.content": "Die Quelle für den Inhalt der Sammlung wurde geändert.", // "collection.edit.tabs.source.notifications.saved.title": "Content Source saved", "collection.edit.tabs.source.notifications.saved.title": "Bezugsquelle für den Inhalt gespeichert", @@ -1287,7 +1287,7 @@ "community.edit.logo.label": "Bereichslogo", // "community.edit.logo.notifications.add.error": "Uploading Community logo failed. Please verify the content before retrying.", - "community.edit.logo.notifications.add.error": "Hochladen des Bereichslogos fehlgeschlagen. Bitte überprüfen Sie den Inhalt bevor Sie es noch einmal versuchen.", + "community.edit.logo.notifications.add.error": "Hochladen des Bereichslogos fehlgeschlagen. Bitte überprüfen Sie den Inhalt bevor Sie es erneut versuchen.", // "community.edit.logo.notifications.add.success": "Upload Community logo successful.", "community.edit.logo.notifications.add.success": "Hochladen des Bereichslogos erfolgreich.", @@ -1469,10 +1469,10 @@ // "cookies.consent.accept-all": "Accept all", - "cookies.consent.accept-all": "Alle annehmen", + "cookies.consent.accept-all": "Alle Cookies akzeptieren", // "cookies.consent.accept-selected": "Accept selected", - "cookies.consent.accept-selected": "Ausgewählte annehmen", + "cookies.consent.accept-selected": "Nur ausgewählte Cookies akzeptieren", // "cookies.consent.app.opt-out.description": "This app is loaded by default (but you can opt out)", "cookies.consent.app.opt-out.description": "Diese App wird standardmäßig geladen (Sie können dies jedoch deaktivieren)", @@ -1868,16 +1868,16 @@ "form.group-collapse-help": "Zum Zusammenfalten bitte hier klicken", // "form.group-expand": "Expand", - "form.group-expand": "Auffalten", + "form.group-expand": "Ausklappen", // "form.group-expand-help": "Click here to expand and add more elements", - "form.group-expand-help": "Zum Ausklappen und Hinzufügen von weiteren Elementen klicken Sie hier", + "form.group-expand-help": "Zum Ausklappen und Hinzufügen weiterer Elemente klicken Sie bitte hier.", // "form.last-name": "Last name", "form.last-name": "Nachname", // "form.loading": "Loading...", - "form.loading": "Am Laden ...", + "form.loading": "Lädt...", // "form.lookup": "Lookup", "form.lookup": "Nachschlagen", @@ -1986,7 +1986,7 @@ // "item.badge.private": "Private", - "item.badge.private": "Privat", + "item.badge.private": "Verborgen", // "item.badge.withdrawn": "Withdrawn", "item.badge.withdrawn": "Zurückgezogen", @@ -2080,7 +2080,7 @@ "item.edit.bitstreams.headers.name": "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": "Ihre Änderungen wurden verworfen. Um Ihre Änderungen wiederherzustellen, klicken Sie auf die Schaltfläche 'Rückgängig'", + "item.edit.bitstreams.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um sie wiederherzustellen, klicken Sie bitte auf \"Rückgängig\".", // "item.edit.bitstreams.notifications.discarded.title": "Changes discarded", "item.edit.bitstreams.notifications.discarded.title": "Änderungen verworfen", @@ -2241,7 +2241,7 @@ "item.edit.metadata.metadatafield.invalid": "Bitte wählen Sie ein gültiges Metadatenfeld", // "item.edit.metadata.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", - "item.edit.metadata.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um diese wieder anzuwenden, klicken Sie auf den 'Rückgängig machen' Knopf", + "item.edit.metadata.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um sie wiederherzustellen, klicken Sie bitte auf \"Rückgängig\".", // "item.edit.metadata.notifications.discarded.title": "Changed discarded", "item.edit.metadata.notifications.discarded.title": "Änderungen verworfen", @@ -2325,7 +2325,7 @@ "item.edit.private.cancel": "Abbrechen", // "item.edit.private.confirm": "Make it Private", - "item.edit.private.confirm": "Privat machen", + "item.edit.private.confirm": "Verbergen", // "item.edit.private.description": "Are you sure this item should be made private in the archive?", "item.edit.private.description": "Wollen Sie dieses Item als privat markieren.", @@ -2365,7 +2365,7 @@ "item.edit.reinstate.cancel": "Abbrechen", // "item.edit.reinstate.confirm": "Reinstate", - "item.edit.reinstate.confirm": "Reinstantiieren", + "item.edit.reinstate.confirm": "Wiederherstellen", // "item.edit.reinstate.description": "Are you sure this item should be reinstated to the archive?", "item.edit.reinstate.description": "Sind Sie sicher, dass das Item reinstantiiert werden soll?", @@ -2397,7 +2397,7 @@ "item.edit.relationships.no-relationships": "Keine Beziehungen", // "item.edit.relationships.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", - "item.edit.relationships.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um sie dennoch anzuwenden klicken Sie auf den 'Rückgängig machen' Knopf", + "item.edit.relationships.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um sie wiederherzustellen, klicken Sie bitte auf \"Rückgängig\".", // "item.edit.relationships.notifications.discarded.title": "Changes discarded", "item.edit.relationships.notifications.discarded.title": "Änderungen verworfen", @@ -2477,7 +2477,7 @@ "item.edit.tabs.status.buttons.move.label": "Item in eine andere Sammlung verschieben", // "item.edit.tabs.status.buttons.private.button": "Make it private...", - "item.edit.tabs.status.buttons.private.button": "Privat machen...", + "item.edit.tabs.status.buttons.private.button": "Verbergen", // "item.edit.tabs.status.buttons.private.label": "Make item private", "item.edit.tabs.status.buttons.private.label": "Item privat machen", @@ -2489,7 +2489,7 @@ "item.edit.tabs.status.buttons.public.label": "Item öffentlich machen", // "item.edit.tabs.status.buttons.reinstate.button": "Reinstate...", - "item.edit.tabs.status.buttons.reinstate.button": "Reinstantiieren...", + "item.edit.tabs.status.buttons.reinstate.button": "Wiederherstellen...", // "item.edit.tabs.status.buttons.reinstate.label": "Reinstate item into the repository", "item.edit.tabs.status.buttons.reinstate.label": "Item wieder ins Repositorium einsetzen", @@ -2780,7 +2780,7 @@ "journal.page.titleprefix": "Zeitschrift: ", // "journal.search.results.head": "Journal Search Results", - "journal.search.results.head": "Zeitschrift Suchergebenisse", + "journal.search.results.head": "Suchergebnisse für Zeitschriften", // "journal.search.title": "DSpace Angular :: Journal Search", "journal.search.title": "DSpace Angular :: Zeitschriftensuche", @@ -2788,7 +2788,7 @@ // "journalissue.listelement.badge": "Journal Issue", - "journalissue.listelement.badge": "Zeitschriftenausgabe", + "journalissue.listelement.badge": "Zeitschriftenheft", // "journalissue.page.description": "Description", "journalissue.page.description": "Beschreibeung", @@ -2812,7 +2812,7 @@ "journalissue.page.number": "Zählung", // "journalissue.page.titleprefix": "Journal Issue: ", - "journalissue.page.titleprefix": "Zeitschriftenausgabe: ", + "journalissue.page.titleprefix": "Zeitschriftenheft:", @@ -2925,7 +2925,7 @@ // "logout.form.header": "Log out from DSpace", - "logout.form.header": "Aus dem Repositorium abmelden", + "logout.form.header": "Abmelden", // "logout.form.submit": "Log out", "logout.form.submit": "Abmelden", @@ -2969,7 +2969,7 @@ "menu.section.browse_community_by_author": "Nach Autor:in", // "menu.section.browse_community_by_issue_date": "By Issue Date", - "menu.section.browse_community_by_issue_date": "Nach Erscheinungsdateum", + "menu.section.browse_community_by_issue_date": "Nach Erscheinungsdatum", // "menu.section.browse_community_by_title": "By Title", "menu.section.browse_community_by_title": "Nach Titel", @@ -3200,7 +3200,7 @@ "mydspace.messages.no-content": "Kein Inhalt.", // "mydspace.messages.no-messages": "No messages yet.", - "mydspace.messages.no-messages": "Noch keine Nachrichten.", + "mydspace.messages.no-messages": "Es sind noch keine Nachrichten vorhanden.", // "mydspace.messages.send-btn": "Send", "mydspace.messages.send-btn": "Senden", @@ -3359,7 +3359,7 @@ // "pagination.results-per-page": "Results Per Page", - "pagination.results-per-page": "Anzeige pro Seite", + "pagination.results-per-page": "Treffer pro Seite", // "pagination.showing.detail": "{{ range }} of {{ total }}", "pagination.showing.detail": "{{ range }} von {{ total }}", @@ -3587,10 +3587,10 @@ "profile.metadata.form.label.lastname": "Nachname", // "profile.metadata.form.label.phone": "Contact Telephone", - "profile.metadata.form.label.phone": "Kontakt Telefon", + "profile.metadata.form.label.phone": "Telefonnummer", // "profile.metadata.form.notifications.success.content": "Your changes to the profile were saved.", - "profile.metadata.form.notifications.success.content": "Ihre Änderungen am Profil wurden gespeichert.", + "profile.metadata.form.notifications.success.content": "Ihre Profiländerungen wurden gespeichert.", // "profile.metadata.form.notifications.success.title": "Profile saved", "profile.metadata.form.notifications.success.title": "Profil gespeichert", @@ -3617,10 +3617,10 @@ "profile.security.form.label.passwordrepeat": "Zum Bestätigen erneut eingeben", // "profile.security.form.notifications.success.content": "Your changes to the password were saved.", - "profile.security.form.notifications.success.content": "Ihre Änderungen am Passwort wurden gespeichert.", + "profile.security.form.notifications.success.content": "Ihr geändertes Passwort wurde gespeichert.", // "profile.security.form.notifications.success.title": "Password saved", - "profile.security.form.notifications.success.title": "Password gespeichert", + "profile.security.form.notifications.success.title": "Passwort gespeichert", // "profile.security.form.notifications.error.title": "Error changing passwords", "profile.security.form.notifications.error.title": "Fehler beim Ändern von Passwörtern", @@ -3727,7 +3727,7 @@ "register-page.create-profile.identification.last-name.error": "Bitte geben Sie einen Nachnamen ein", // "register-page.create-profile.identification.contact": "Contact Telephone", - "register-page.create-profile.identification.contact": "Kontakt Telefon", + "register-page.create-profile.identification.contact": "Telefonnummer", // "register-page.create-profile.identification.language": "Language", "register-page.create-profile.identification.language": "Sprache", @@ -3760,7 +3760,7 @@ "register-page.create-profile.submit.error.content": "Bei der Registrierung eines neuen Accounts ist ein Fehler aufgetreten.", // "register-page.create-profile.submit.error.head": "Registration failed", - "register-page.create-profile.submit.error.head": "Registrierung fehlgeschlagen.", + "register-page.create-profile.submit.error.head": "Registrierung fehlgeschlagen", // "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": "Die Registrierung war erfolgreich. Sie wurden als der:die angelegte Benutzer:in angemeldet.", @@ -3817,16 +3817,16 @@ "relationships.isAuthorOf": "Autor:innen", // "relationships.isAuthorOf.Person": "Authors (persons)", - "relationships.isAuthorOf.Person": "AutorInnen (Personen)", + "relationships.isAuthorOf.Person": "Autor*innen (Personen)", // "relationships.isAuthorOf.OrgUnit": "Authors (organizational units)", "relationships.isAuthorOf.OrgUnit": "Autor:innen (Organisationseinheiten)", // "relationships.isIssueOf": "Journal Issues", - "relationships.isIssueOf": "Zeitschriftenausgaben", + "relationships.isIssueOf": "Zeitschriftenhefte", // "relationships.isJournalIssueOf": "Journal Issue", - "relationships.isJournalIssueOf": "Zeitschriftenausgabe", + "relationships.isJournalIssueOf": "Zeitschriftenheft", // "relationships.isJournalOf": "Journals", "relationships.isJournalOf": "Zeitschriften", @@ -3894,7 +3894,7 @@ "resource-policies.create.page.title": "Eine neue Ressourcen-Richtlinie erstellen", // "resource-policies.delete.btn": "Delete selected", - "resource-policies.delete.btn": "Ausgewählte löschen", + "resource-policies.delete.btn": "Markierte löschen", // "resource-policies.delete.btn.title": "Delete selected resource policies", "resource-policies.delete.btn.title": "Ausgewählte Ressourcen-Richtlinien löschen", @@ -4040,13 +4040,13 @@ "search.filters.applied.f.dateSubmitted": "Datum der Einreichung", // "search.filters.applied.f.discoverable": "Private", - "search.filters.applied.f.discoverable": "Privat", + "search.filters.applied.f.discoverable": "Verborgen", // "search.filters.applied.f.entityType": "Item Type", "search.filters.applied.f.entityType": "Item-Typ", // "search.filters.applied.f.has_content_in_original_bundle": "Has files", - "search.filters.applied.f.has_content_in_original_bundle": "Hat Dateien", + "search.filters.applied.f.has_content_in_original_bundle": "Dateien dazu vorhanden?", // "search.filters.applied.f.itemtype": "Type", "search.filters.applied.f.itemtype": "Typ", @@ -4126,7 +4126,7 @@ "search.filters.filter.dateSubmitted.placeholder": "Datum der Einreichung", // "search.filters.filter.discoverable.head": "Private", - "search.filters.filter.discoverable.head": "Privat", + "search.filters.filter.discoverable.head": "Verborgen", // "search.filters.filter.withdrawn.head": "Withdrawn", "search.filters.filter.withdrawn.head": "Zurückgezogen", @@ -4215,7 +4215,7 @@ // "search.filters.entityType.JournalIssue": "Journal Issue", - "search.filters.entityType.JournalIssue": "Zeitschriftenausgabe", + "search.filters.entityType.JournalIssue": "Zeitschriftenheft", // "search.filters.entityType.JournalVolume": "Journal Volume", "search.filters.entityType.JournalVolume": "Zeitschriftenband", @@ -4302,10 +4302,10 @@ "search.view-switch.show-detail": "Detailanzeige", // "search.view-switch.show-grid": "Show as grid", - "search.view-switch.show-grid": "Anzeige als Raster", + "search.view-switch.show-grid": "Als Raster anzeigen", // "search.view-switch.show-list": "Show as list", - "search.view-switch.show-list": "Anzeige als Liste", + "search.view-switch.show-list": "Als Liste anzeigen", @@ -4465,7 +4465,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal": "Zeitschrift importieren", // "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": "Zeitschriftenausgabe importieren", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Issue": "Zeitschriftenheft importieren", // "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": "Zeitschriftenband importieren", @@ -4489,7 +4489,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-modal.cancel": "Abbrechen", // "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": "Wählen Sie eine Sammlung, um neue Einträge dorthin zu importieren", + "submission.sections.describe.relationship-lookup.external-source.import-modal.collection": "Wählen Sie eine Sammlung, um neue Einträge dorthin zu importieren.", // "submission.sections.describe.relationship-lookup.external-source.import-modal.entities": "Entities", "submission.sections.describe.relationship-lookup.external-source.import-modal.entities": "Entitäten", @@ -4519,7 +4519,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-modal.import": "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": "Zeitschrift von externer Quelle importieren", + "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.title": "Zeitschrift aus externer Quelle importieren", // "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": "Lokale Zeitschrift wurde erfolgreich zur Auswahl hinzugefügt", @@ -4549,7 +4549,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Wählen Sie einen lokalen Treffer aus:", // "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all", - "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Alle abwählen", + "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Nichts auswählen", // "submission.sections.describe.relationship-lookup.search-tab.deselect-page": "Deselect page", "submission.sections.describe.relationship-lookup.search-tab.deselect-page": "Seite abwählen", @@ -4642,9 +4642,9 @@ "submission.sections.describe.relationship-lookup.selection-tab.tab-title": "Aktuelle Auswahl ({{ count }})", // "submission.sections.describe.relationship-lookup.title.isJournalIssueOfPublication": "Journal Issues", - "submission.sections.describe.relationship-lookup.title.isJournalIssueOfPublication": "Zeitschriftenausgaben", + "submission.sections.describe.relationship-lookup.title.isJournalIssueOfPublication": "Zeitschriftenhefte", // "submission.sections.describe.relationship-lookup.title.JournalIssue": "Journal Issues", - "submission.sections.describe.relationship-lookup.title.JournalIssue": "Zeitschriftenausgaben", + "submission.sections.describe.relationship-lookup.title.JournalIssue": "Zeitschriftenhefte", // "submission.sections.describe.relationship-lookup.title.isJournalVolumeOfPublication": "Journal Volumes", "submission.sections.describe.relationship-lookup.title.isJournalVolumeOfPublication": "Zeitschriftenbände", @@ -4765,7 +4765,7 @@ "submission.sections.describe.relationship-lookup.selection-tab.title.arxiv": "Suchergebnisse", // "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": "Möchten Sie \"{{ value }}\" als Namesvariante für diese Person speichern? So können Sie und andere diese bei zukünftigen Veröffentlichungen verwenden. Wenn Sie das nicht tun, können Sie die Variante immer noch für diese Veröffentlichung verwenden.", + "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Möchten Sie die Namensvariante \"{{ value }}\" speichern, damit sie bei zukünftigen Veröffentlichungen erneut verwendet werden kann? Sie können die Variante bei dieser Veröffentlichung auch verwenden, ohne sie zu speichern.", // "submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant", "submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Als Namensvariante speichern", @@ -4813,7 +4813,7 @@ "submission.sections.general.discard_success_notice": "Einreichung erfolgreich verworfen.", // "submission.sections.general.metadata-extracted": "New metadata have been extracted and added to the {{sectionId}} section.", - "submission.sections.general.metadata-extracted": "Neue Metainformation wurden extrahier unt dem Bereich {{sectionId}} zugeordnet.", + "submission.sections.general.metadata-extracted": "Neue Metadaten wurden extrahiert und dem Bereich {{sectionId}} zugeordnet.", // "submission.sections.general.metadata-extracted-new-section": "New {{sectionId}} section has been added to submission.", "submission.sections.general.metadata-extracted-new-section": "Neuer Bereich {{sectionId}} wurde zur Einreichung hinzugefügt.", @@ -4854,7 +4854,7 @@ "submission.sections.submit.progressbar.describe.steptwo": "Beschreiben", // "submission.sections.submit.progressbar.detect-duplicate": "Potential duplicates", - "submission.sections.submit.progressbar.detect-duplicate": "Mögliche Dubletten", + "submission.sections.submit.progressbar.detect-duplicate": "Doppelter Eintrag?", // "submission.sections.submit.progressbar.license": "Deposit license", "submission.sections.submit.progressbar.license": "Einreichlizenz", @@ -4898,7 +4898,7 @@ "submission.sections.upload.form.group-label": "Gruppe", // "submission.sections.upload.form.group-required": "Group is required.", - "submission.sections.upload.form.group-required": "Gruppe ist erforderlich", + "submission.sections.upload.form.group-required": "Bitte geben Sie eine Gruppe an.", // "submission.sections.upload.form.until-label": "Grant access until", "submission.sections.upload.form.until-label": "Zugriff gewährt bis", @@ -4994,7 +4994,7 @@ "submission.workflow.tasks.claimed.return": "Zurück in den gemeinsamen Aufgabenbereich", // "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": "Aufgabe in den gemeinsamen Aufgabenbereich überführen, so dass ein anderer Bearbeiter die Aufgabe übernehmen kann.", + "submission.workflow.tasks.claimed.return_help": "Aufgabe in den Aufgabenpool zurückgeben, so dass jemand Anderes die Aufgabe übernehmen kann", @@ -5016,7 +5016,7 @@ "submission.workflow.tasks.pool.claim": "Übernehmen", // "submission.workflow.tasks.pool.claim_help": "Assign this task to yourself.", - "submission.workflow.tasks.pool.claim_help": "Aufgabe übernehmen.", + "submission.workflow.tasks.pool.claim_help": "Aufgabe übernehmen", // "submission.workflow.tasks.pool.hide-detail": "Hide detail", "submission.workflow.tasks.pool.hide-detail": "Details verbergen", @@ -5058,13 +5058,13 @@ "uploader.browse": "stöbern", // "uploader.drag-message": "Drag & Drop your files here", - "uploader.drag-message": "Ziehen Sie Ihre Dateien hierhin", + "uploader.drag-message": "Bitte ziehen Sie Ihre Dateien hierhin.", // "uploader.or": ", or ", "uploader.or": ", oder", // "uploader.processing": "Processing", - "uploader.processing": "Bearbeitung läuft", + "uploader.processing": "In Arbeit...", // "uploader.queue-length": "Queue length", "uploader.queue-length": "Länge der Warteschlange", From 304b6226abf64b47d698f3db711d5812fa4d395a Mon Sep 17 00:00:00 2001 From: louiserumpf <101128103+louiserumpf@users.noreply.github.com> Date: Tue, 22 Mar 2022 11:23:39 +0100 Subject: [PATCH 091/570] typo --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index bf7df7619a..1d7640ccf2 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -3817,7 +3817,7 @@ "relationships.isAuthorOf": "Autor:innen", // "relationships.isAuthorOf.Person": "Authors (persons)", - "relationships.isAuthorOf.Person": "Autor*innen (Personen)", + "relationships.isAuthorOf.Person": "Autor:innen (Personen)", // "relationships.isAuthorOf.OrgUnit": "Authors (organizational units)", "relationships.isAuthorOf.OrgUnit": "Autor:innen (Organisationseinheiten)", From 9600bacf46b1c92110e33e2382e01cfd0edf6368 Mon Sep 17 00:00:00 2001 From: raihantopu Date: Tue, 22 Mar 2022 16:29:42 +0600 Subject: [PATCH 092/570] adding bn.json5 for 'Bengali Language Transalation' and language code, label, active (all commented) to environment.test.ts file --- src/assets/i18n/bn.json5 | 6129 ++++++++++++++++++++++++++ src/environments/environment.test.ts | 8 +- 2 files changed, 6136 insertions(+), 1 deletion(-) create mode 100644 src/assets/i18n/bn.json5 diff --git a/src/assets/i18n/bn.json5 b/src/assets/i18n/bn.json5 new file mode 100644 index 0000000000..1bc26d2da4 --- /dev/null +++ b/src/assets/i18n/bn.json5 @@ -0,0 +1,6129 @@ +{ + + // "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": "নিষিদ্ধ", + + // "500.page-internal-server-error": "Service Unavailable", + "500.page-internal-server-error": "সেবা প্রদান করা যাচ্ছে না", + + // "500.help": "The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.", + "500.help": "সার্ভারটি অস্থায়ীভাবে রক্ষণাবেক্ষণ ডাউনটাইম বা ক্ষমতা সমস্যার কারণে আপনার অনুরোধ পরিসেবা করতে অক্ষম। পরে আবার চেষ্টা করুন.", + + // "500.link.home-page": "Take me to the home page", + "500.link.home-page": "আমাকে হোম পেজে নিয়ে যাও", + + + // "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": "", + // TODO New key - Add a translation + "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": "এই বিন্যাসের সাথে যুক্ত মিম টাইপ ইউনিক হতে হবে না।", + + // "admin.registries.bitstream-formats.edit.mimetype.label": "MIME Type", + "admin.registries.bitstream-formats.edit.mimetype.label": "মিম টাইপ", + + // "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": "এই বিন্যাসের জন্য একটি ইউনিক নাম (উদাহরণ সরূপঃ মাইক্রোসফ্ট ওয়ার্ড এক্সপি বা মাইক্রোসফ্ট ওয়ার্ড 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": "মিম টাইপ", + + // "admin.registries.bitstream-formats.table.name": "Name", + "admin.registries.bitstream-formats.table.name": "নাম", + + // "admin.registries.bitstream-formats.table.return": "Back", + "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": "Bitstream Format Registry", + "admin.registries.bitstream-formats.title": "বিটস্ট্রিম রেজিস্ট্রি ফরম্যাট", + + + + // "admin.registries.metadata.breadcrumbs": "Metadata registry", + "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": "মেটাডেটা রেজিস্ট্রি রিপোজিটরিতে উপলব্ধ সমস্ত মেটাডেটা ক্ষেত্রের একটি তালিকা বজায় রাখে। এই ক্ষেত্রগুলি একাধিক স্কিমাসের মধ্যে বিভক্ত করা যেতে পারে। তবে ডিস্পেস যোগ্য ডাবলিন কোর স্কিমা প্রয়োজন।", + + // "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": "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": "Back", + "admin.registries.schema.return": "পেছনে", + + // "admin.registries.schema.title": "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": "Reset password", + "admin.access-control.epeople.actions.reset": "পাসওয়ার্ড রিসেট করুন", + + // "admin.access-control.epeople.actions.stop-impersonating": "Stop impersonating EPerson", + "admin.access-control.epeople.actions.stop-impersonating": "ছদ্মবেশী ই-পারসন বন্ধ করুন", + + // "admin.access-control.epeople.breadcrumbs": "EPeople", + "admin.access-control.epeople.breadcrumbs": "ই-পারসন", + + // "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": "ই-মেইল (সঠিক)", + + // "admin.access-control.epeople.search.button": "Search", + "admin.access-control.epeople.search.button": "অনুসন্ধান করুন", + + // "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": "ই-পারসন যোগ করুন", + + // "admin.access-control.epeople.table.id": "ID", + "admin.access-control.epeople.table.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": "ই-মেইল (সঠিক)", + + // "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.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 }}\" মুছে ফেলুন", + + // "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": "ই-মেইল", + + // "admin.access-control.epeople.form.emailHint": "Must be valid e-mail address", + "admin.access-control.epeople.form.emailHint": "বৈধ ইমেইল ঠিকানা হতে হবে", + + // "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.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 }}\"।", + + // "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 }}\" তৈরি করতে ব্যর্থ। ইমেল \"{{ 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 }}\" সম্পাদনা করতে ব্যর্থ। ইমেল \"{{ 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": "আইডি", + + // "admin.access-control.epeople.form.table.name": "Name", + "admin.access-control.epeople.form.table.name": "নাম", + + // "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": "এই ই-পারসন কোন দলের সদস্য নয়", + + // "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": "Groups", + "admin.access-control.groups.title": "গ্রুপ", + + // "admin.access-control.groups.breadcrumbs": "Groups", + "admin.access-control.groups.breadcrumbs": "গ্রুপ", + + // "admin.access-control.groups.singleGroup.breadcrumbs": "Edit Group", + "admin.access-control.groups.singleGroup.breadcrumbs": "গ্রুপ সম্পাদনা করুন", + + // "admin.access-control.groups.title.singleGroup": "Edit Group", + "admin.access-control.groups.title.singleGroup": "গ্রুপ সম্পাদনা করুন", + + // "admin.access-control.groups.title.addGroup": "New Group", + "admin.access-control.groups.title.addGroup": "নতুন দল", + + // "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": "গ্রুপ", + + // "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.search.placeholder": "Search groups...", + "admin.access-control.groups.search.placeholder": "গ্রুপ অনুসন্ধান করুন...", + + // "admin.access-control.groups.table.id": "ID", + "admin.access-control.groups.table.id": "আইডি", + + // "admin.access-control.groups.table.name": "Name", + "admin.access-control.groups.table.name": "নাম", + + // "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": "সদস্যগণ", + + // "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": "তাদের নাম বা এই UUID হিসাবে কোন গ্রুপ পাওয়া যায় নি", + + // "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 }}\" -এ জমা এবং ওয়ার্কফ্লো প্রক্রিয়াতে ভূমিকা অনুসারে। আপনি এটি মুছে ফেলতে পারেন from the \"assign roles\" tab on the edit {{comcol}} page। আপনি এখনও এই পৃষ্ঠাটি ব্যবহার করে গ্রুপ সদস্যদের যোগ এবং অপসারণ করতে পারেন।", + + // "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.groupCommunity": "Community or Collection", + "admin.access-control.groups.form.groupCommunity": "সম্প্রদায় বা সংগ্রহ", + + // "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 \"{{name}}\" already in use!", + "admin.access-control.groups.form.notification.edited.failure.groupNameInUse": "নাম \"{{ name }}\" ইতিমধ্যে ব্যবহার হয়েছে!", + + // "admin.access-control.groups.form.notification.edited.success": "Successfully edited Group \"{{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": "ই-মেইল (সঠিক)", + + // "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": "আইডি", + + // "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.identity": "Identity", + "admin.access-control.groups.form.members-list.table.identity": "পরিচয়", + + // "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", + "admin.access-control.groups.form.members-list.table.netid": "নেটআইডি", + + // "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": "আইডি", + + // "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.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": "অপসারণ / যোগ করুন", + + // "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": "Back", + "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", + "admin.metadata-import.page.help": "আপনি এখানে ফাইলগুলিতে ব্যাচ মেটাডেটা অপারেশন ধারণ করে এমন সিএসভি ফাইলগুলি ড্রপ বা ব্রাউজ করতে পারেন৷", + + // "admin.metadata-import.page.dropMsg": "Drop a metadata CSV to import", + "admin.metadata-import.page.dropMsg": "আমদানি করতে একটি মেটাডেটা সিএসভি ড্রপ করুন", + + // "admin.metadata-import.page.dropMsgReplace": "Drop to replace the metadata CSV to import", + "admin.metadata-import.page.dropMsgReplace": "মেটাডেটা সিএসভি আমদানি করতে ড্রপ করুন", + + // "admin.metadata-import.page.button.return": "Back", + "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": "আপনার অধিবেশন মেয়াদ শেষ হয়ে গেছে। অনুগ্রহ পূর্বক আবার প্রবেশ করুন.", + + // "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}}..." , + "bitstream.download.page": "এখন ডাউনলোড হচ্ছে {{bitstream}}...", + + // "bitstream.download.page.back": "Back" , + "bitstream.download.page.back": "ফিরে যান" , + + + // "bitstream.edit.authorizations.link": "Edit bitstream's Policies", + "bitstream.edit.authorizations.link": "বিটস্ট্রিম এর নীতি সম্পাদনা করুন", + + // "bitstream.edit.authorizations.title": "Edit bitstream's Policies", + "bitstream.edit.authorizations.title": "বিটস্ট্রিম এর নীতি সম্পাদনা করুন", + + // "bitstream.edit.return": "Back", + "bitstream.edit.return": "পেছনে", + + // "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": "বিকল্পভাবে ফাইলটির সংক্ষিপ্ত বিবরণ প্রদান করুন উদাহরণস্বরূপ \" মূল নিবন্ধ \" অথবা \" পরীক্ষা ডেটা রিডিং \"।", + + // "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": "প্রথম দিন থেকে অ্যাক্সেস অনুমতি দেওয়া হয়। এই তারিখটি এই ফর্মটি সংশোধন করা যাবে না। বিটস্ট্রিমের জন্য একটি নিষেধাজ্ঞা নির্ধারণ করতে আইটেমের স্থিতি ট্যাবটিতে যান অনুমোদন ..., তৈরি করুন বা বিটস্ট্রিমের পড়ুন নীতিটি, এবং সম্পাদনা করুন শুরু তারিখ সেট করুন।", + + // "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": "বিটস্ট্রিমের জন্য ফাইলের নাম পরিবর্তন করুন। মনে রাখবেন যে এটি প্রদর্শন বিটস্ট্রিম URL টি পরিবর্তন করবে তবে পুরোনো লিঙ্কগুলি এখনও ক্রমটি সমাধান করবে না যতক্ষণ না ক্রম আইডিটি পরিবর্তন হয় না।", + + // "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 version 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.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.", + "bitstream.edit.form.iiifLabel.hint": "এই ছবির জন্য ক্যানভাস লেবেল। যদি প্রদান না হয় ডিফল্ট লেবেল ব্যবহার করা হবে।", + + // "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.", + "bitstream.edit.form.iiifToc.hint": "এখানে টেক্সট যোগ করা এটিকে বিষয়বস্তুর পরিসরের একটি নতুন টেবিলের সূচনা করে", + + // "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.", + "bitstream.edit.form.iiifWidth.hint": "ক্যানভাস প্রস্থ সাধারণত ইমেজ প্রস্থের সাথে মেলা উচিত।", + + // "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.", + "bitstream.edit.form.iiifHeight.hint": "ক্যানভাস উচ্চতা সাধারণত ইমেজের উচ্চতার সাথে মেলে উচিত।", + + + // "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": "বিটস্ট্রিম সম্পাদনা করুন", + + // "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", + "bitstream-request-a-copy.alert.canDownload2": "এখানে", + + // "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: ", + "bitstream-request-a-copy.intro": "নিম্নলিখিত আইটেমটির জন্য একটি অনুলিপি অনুরোধ করতে নিম্নলিখিত তথ্য লিখুন: ", + + // "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. ", + "bitstream-request-a-copy.intro.bitstream.all": "সব ফাইল অনুরোধ করা হচ্ছে।", + + // "bitstream-request-a-copy.name.label": "Name *", + "bitstream-request-a-copy.name.label": "নাম *", + + // "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 *", + "bitstream-request-a-copy.email.label": "আপনার ইমেইল ঠিকানা *", + + // "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.", + "bitstream-request-a-copy.email.error": "একটি বৈধ ইমেইল ঠিকানা লিখুন।", + + // "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", + "bitstream-request-a-copy.files-all-false.label": "শুধুমাত্র অনুরোধকৃত ফাইল", + + // "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", + "bitstream-request-a-copy.message.label": "বার্তা", + + // "bitstream-request-a-copy.return": "Back", + "bitstream-request-a-copy.return": "পেছনে", + + // "bitstream-request-a-copy.submit": "Request copy", + "bitstream-request-a-copy.submit": "অনুরোধ কপি", + + // "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.", + "bitstream-request-a-copy.submit.error": "আইটেমের অনুরোধ পাঠাতে দিয়ে গিয়ে কিছু ভুল হয়েছে।", + + + + // "browse.comcol.by.author": "By Author", + "browse.comcol.by.author": "লেখক দ্বারা", + + // "browse.comcol.by.dateissued": "By Issue Date", + "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.next.button": "Next", + "browse.next.button": "পরবর্তী", + + // "browse.previous.button": "Previous", + "browse.previous.button": "পূর্ববর্তী", + + // "browse.startsWith.choose_start": "(Choose start)", + "browse.startsWith.choose_start": "(শুরু নির্বাচন করুন)", + + // "browse.startsWith.choose_year": "(Choose year)", + "browse.startsWith.choose_year": "(বছর নির্বাচন করুন)", + + // "browse.startsWith.choose_year.label": "Choose the issue year", + "browse.startsWith.choose_year.label": "ইস্যুর বছর নির্বাচন করুন", + + // "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.none.label": "Choose the issue month", + "browse.startsWith.months.none.label": "ইস্যু মাস নির্বাচন করুন", + + // "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": "Browse", + "browse.startsWith.submit": "ব্রাউজ", + + // "browse.startsWith.type_date": "Or type in a date (year-month) and click 'Browse'", + "browse.startsWith.type_date": "অথবা একটি তারিখ (বছরের-মাস) টাইপ করুন এবং 'ব্রাউজ' ক্লিক করুন", + + // "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": "Type the first few letters and click on the Browse button", + "browse.startsWith.type_text": "প্রথম কয়েকটি অক্ষর টাইপ করুন এবং ব্রাউজ বোতামে ক্লিক করুন", + + // "browse.title": "Browsing {{ collection }} by {{ field }} {{ value }}", + "browse.title": "ব্রাউজিং {{collection}} এর {{field}} {{value}}", + + + // "chips.remove": "Remove chip", + "chips.remove": "চিপ সরান", + + + + // "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": "সম্প্রদায়ের জন্য একটি সংগ্রহ তৈরি করুন {{praent}}", + + // "collection.curate.header": "Curate Collection: {{collection}}", + "collection.curate.header": "কিউরেট সংগ্রহ: {{collection}}", + + // "collection.delete.cancel": "Cancel", + "collection.delete.cancel": "বাতিল করুন", + + // "collection.delete.confirm": "Confirm", + "collection.delete.confirm": "নিশ্চিত করুন", + + // "collection.delete.processing": "Deleting", + "collection.delete.processing": "মুছে ফেলা হচ্ছে", + + // "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": "আপনি কি আপনি সংগ্রহের মুছতে চান \"{{dos}}\"", + + + + // "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": "Item Mapper - 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}} আইটেমগুলি ম্যাপ করা হয়েছে।", + + // "collection.edit.item-mapper.notifications.map.success.head": "Mapping completed", + "collection.edit.item-mapper.notifications.map.success.head": "সমন্নয় সম্পন্ন", + + // "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": "ত্রুটি {{amount}} আইটেমগুলির mappings অপসারণের জন্য ঘটেছে।", + + // "collection.edit.item-mapper.notifications.unmap.error.head": "Remove mapping errors", + "collection.edit.item-mapper.notifications.unmap.error.head": "সমন্নয় ত্রুটি মুছে ফেলুন", + + // "collection.edit.item-mapper.notifications.unmap.success.content": "Successfully removed the mappings of {{amount}} items.", + "collection.edit.item-mapper.notifications.unmap.success.content": "সফলভাবে {{amount}} আইটেমগুলির সমন্বয় সরানো হয়েছে।", + + // "collection.edit.item-mapper.notifications.unmap.success.head": "Remove mapping completed", + "collection.edit.item-mapper.notifications.unmap.success.head": "সমন্বয় সরানো সম্পন্ন হয়েছে", + + // "collection.edit.item-mapper.remove": "Remove selected item mappings", + "collection.edit.item-mapper.remove": "নির্বাচিত আইটেমের সমন্নয় মুছে ফেলুন", + + // "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": "সমন্বিত আইটেম ব্রাউজ করুন", + + // "collection.edit.item-mapper.tabs.map": "Map new items", + "collection.edit.item-mapper.tabs.map": "নতুন আইটেম সমন্বয় করুন", + + + // "collection.edit.logo.delete.title": "Delete logo", + "collection.edit.logo.delete.title": "লোগো মুছে দিন", + + // "collection.edit.logo.delete-undo.title": "Undo delete", + "collection.edit.logo.delete-undo.title": "মুছে ফেলা মুছে ফেলুন", + + // "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": "Back", + "collection.edit.return": "পেছনে", + + + + // "collection.edit.tabs.curate.head": "Curate", + "collection.edit.tabs.curate.head": "কিউরেট", + + // "collection.edit.tabs.curate.title": "Collection Edit - Curate", + "collection.edit.tabs.curate.title": "সংগ্রহ সম্পাদনা - কিউরেট", + + // "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", + "collection.edit.tabs.source.form.oaiSetId": "OAI নির্দিষ্ট সেট আইডি", + + // "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": "Collection Edit - 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.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 }}\"", + + // "collection.edit.template.label": "Template item", + "collection.edit.template.label": "টেমপ্লেট আইটেম", + + // "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": "আইটেম টেমপ্লেট মুছে ফেলতে ব্যর্থ হয়েছে", + + // "collection.edit.template.notifications.delete.success": "Successfully deleted the item template", + "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": "প্রারম্ভিক টেক্সট (এইচটিএমএল)", + + // "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": "কপিরাইট টেক্সট (এইচটিএমএল)", + + // "collection.form.tableofcontents": "News (HTML)", + "collection.form.tableofcontents": "সংবাদ (এইচটিএমএল)", + + // "collection.form.title": "Name", + "collection.form.title": "নাম", + + // "collection.form.entityType": "Entity Type", + "collection.form.entityType": "এনটিটির (সত্তা) ধরন", + + + + // "collection.listelement.badge": "Collection", + "collection.listelement.badge": "সংগ্রহ", + + + + // "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": "এই সংগ্রহের জন্য স্থায়ী URI", + + // "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.controls.head": "Harvest Controls", + "collection.source.controls.head": "সংগ্রহের নিয়ন্ত্রণ", + // "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", + "collection.source.controls.test.failed": "সেটিংস পরীক্ষা করার জন্য স্ক্রিপ্ট ব্যর্থ হয়েছে", + // "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", + "collection.source.controls.test.submit": "পরীক্ষা কনফিগারেশন", + // "collection.source.controls.test.running": "Testing configuration...", + "collection.source.controls.test.running": "কনফিগারেশন পরীক্ষা করা হচ্ছে...", + // "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", + "collection.source.controls.import.submit.error": "আমদানি শুরু সময় কিছু ভুল হয়েছে", + // "collection.source.controls.import.submit": "Import now", + "collection.source.controls.import.submit": "এখন আমদানি করুন", + // "collection.source.controls.import.running": "Importing...", + "collection.source.controls.import.running": "আমদানি করা হচ্ছে ...", + // "collection.source.controls.import.failed": "An error occurred during the import", + "collection.source.controls.import.failed": "আমদানি সময় একটি ত্রুটি ঘটেছে", + // "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", + "collection.source.controls.reset.submit.success": "রিসেট এবং পুনরাই আমদানি সফলভাবে শুরু করা হয়েছে", + // "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", + "collection.source.controls.reset.failed": "রিসেট এবং পুনরাই আমদানি সময় কিছু ভুল হয়েছে", + // "collection.source.controls.reset.completed": "The reset and reimport completed", + "collection.source.controls.reset.completed": "রিসেট এবং পুনরাই আমদানি সম্পন্ন", + // "collection.source.controls.reset.submit": "Reset and reimport", + "collection.source.controls.reset.submit": "রিসেট এবং পুনরাই আমদানি সম্পন্ন.", + // "collection.source.controls.reset.running": "Resetting and reimporting...", + "collection.source.controls.reset.running": "রিসেটিং এবং পুনরাই আমদানি চলছে...", + // "collection.source.controls.harvest.status": "Harvest status:", + "collection.source.controls.harvest.status": "সংগ্রহের অবস্থা:", + // "collection.source.controls.harvest.start": "Harvest start time:", + "collection.source.controls.harvest.start": "সংগ্রহ শুরুর সময়:", + // "collection.source.controls.harvest.last": "Last time harvested:", + "collection.source.controls.harvest.last": "শেশবারের মত সংগ্রহীত:", + // "collection.source.controls.harvest.message": "Harvest info:", + "collection.source.controls.harvest.message": "সংগ্রহের তথ্য:", + // "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.", + "collection.source.update.notifications.error.content": "প্রদত্ত সেটিংস পরীক্ষা করা হয়েছে এবং কাজ করে না।", + + // "collection.source.update.notifications.error.title": "Server Error", + "collection.source.update.notifications.error.title": "সার্ভার সমস্যা", + + + + // "communityList.breadcrumbs": "Community List", + "communityList.breadcrumbs": "কমিউনিটির তালিকা", + + // "communityList.tabTitle": "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": "সম্প্রদায়ের জন্য একটি উপ-সম্প্রদায় তৈরি করুন {{praent}}", + + // "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.processing": "Deleting...", + "community.delete.processing": "মুছে ফেলা হচ্ছে ...", + + // "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.delete.title": "Delete logo", + "community.edit.logo.delete.title": "লোগো মুছে ফেলুন", + + // "community.edit.logo.delete-undo.title": "Undo delete", + "community.edit.logo.delete-undo.title": "মুছে ফেলা বাতিল করুন", + + // "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": "Back", + "community.edit.return": "পেছনে", + + + + // "community.edit.tabs.curate.head": "Curate", + "community.edit.tabs.curate.head": "কিউরেট", + + // "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).", + "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": "জমাকারীরা", + + // "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": "ই-ব্যাক্তি এবং গোষ্ঠী যা এই সংগ্রহে জমা দেওয়া নতুন আইটেমগুলি পড়তে পারে। এই ভূমিকা পরিবর্তন retroactive হয় না। সিস্টেমের বিদ্যমান আইটেমগুলি এখনও তাদের সংযোজনের সময় অ্যাক্সেস পড়তে যারা দ্বারা দর্শনীয় হবে।", + + // "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", + "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": "ইনকামিং বিটস্ট্রিমের জন্য ডিফল্ট রিড বর্তমানে বেনামীতে সেট করা আছে", + + + // "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.", + "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": "প্রারম্ভিক টেক্সট (এইচটিএমএল)", + + // "community.form.errors.title.required": "Please enter a community name", + "community.form.errors.title.required": "একটি সম্প্রদায়ের নাম লিখুন দয়া করে", + + // "community.form.rights": "Copyright text (HTML)", + "community.form.rights": "কপিরাইট টেক্সট (এইচটিএমএল)", + + // "community.form.tableofcontents": "News (HTML)", + "community.form.tableofcontents": "সংবাদ (এইচটিএমএল)", + + // "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": "এই সম্প্রদায়ের জন্য স্থায়ী URI", + + // "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": "To learn more, please read our {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": "গুগল বিশ্লেষক", + + // "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", + "curation-task.task.noop.label": "না", + + // "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": "মাইক্রোসফ্ট অনুবাদক", + + // "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": "হ্যান্ডেল:", + + // "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 লিখুন (সব কাজ এই ক্ষমতা সমর্থন করতে পারে না)", + + + + // "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", + "deny-request-copy.email.subject": "নথির কপি অনুরোধ", + + // "deny-request-copy.error": "An error occurred", + "deny-request-copy.error": "একটি ত্রুটি ঘটেছে", + + // "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", + "deny-request-copy.intro": "এই বার্তাটি অনুরোধের আবেদনকারীর কাছে পাঠানো হবে", + + // "deny-request-copy.success": "Successfully denied item request", + "deny-request-copy.success": "সফলভাবে আইটেম অনুরোধ অস্বীকার", + + + + // "dso.name.untitled": "Untitled", + "dso.name.untitled": "শিরোনামহীন", + + + + // "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", + "dso-selector.create.submission.head": "নতুন জমা", + + // "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.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": "রপ্তানি মেটাডেটা থেকে", + + // "dso-selector.no-results": "No {{ type }} found", + "dso-selector.no-results": "কোন {{type}} পাওয়া যায় নি", + + // "dso-selector.placeholder": "Search for a {{ type }}", + "dso-selector.placeholder": "একটি {{type}} এর জন্য অনুসন্ধান করুন", + + // "dso-selector.select.collection.head": "Select a collection", + "dso-selector.select.collection.head": "একটি সংগ্রহ নির্বাচন করুন", + + // "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", + "dso-selector.set-scope.community.button": "ডিস্পেস সব অনুসন্ধান করুন", + + // "dso-selector.set-scope.community.input-header": "Search for a community or collection", + "dso-selector.set-scope.community.input-header": "একটি সম্প্রদায় বা সংগ্রহের জন্য অনুসন্ধান করুন", + + + + // "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": "সাম্প্রতিক জমাগুলো আনতে ত্রুটি হচ্ছে", + + // "error.search-results": "Error fetching search results", + "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.", + "error.invalid-search-query": "অনুসন্ধান ক্যোয়ারী বৈধ নয়. আরও জানতে অনুগ্রহ করে সোলার কোয়েরি সিনট্যাক্স সর্বোত্তম অনুশীলন দেখুন এই ত্রুটি সম্পর্কে তথ্য।", + + // "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": "আপনি আপনার জমা সম্পূর্ণ করার জন্য এই লাইসেন্স প্রদান করতে হবে। আপনি যদি এই লাইসেন্সটি অনুগ্রহ করতে অক্ষম হন তবে আপনি আপনার কাজটি সংরক্ষণ করতে এবং পরে ফিরে বা জমাটি সরাতে পারেন।", + + // "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": "ফাইল আপলোড বাধ্যতামূলক", + + // "error.validation.required": "This field is required", + "error.validation.required": "ঘরটি অবশ্যই পূরণ করতে হবে", + + // "error.validation.NotValidEmail": "This E-mail is not a valid email", + "error.validation.NotValidEmail": "এই ইমেইল একটি বৈধ ইমেইল নয়", + + // "error.validation.emailTaken": "This E-mail is already taken", + "error.validation.emailTaken": "এই ইমেইলটি ইতিমধ্যে নেওয়া হয়েছে", + + // "error.validation.groupExists": "This group already exists", + "error.validation.groupExists": "এই গ্রুপ ইতিমধ্যে বিদ্যমান", + + + // "file-section.error.header": "Error obtaining files for this item", + "file-section.error.header": "এই আইটেমটি জন্য ফাইল প্রাপ্ত করার সময় ত্রুটি", + + + + // "footer.copyright": "copyright © 2002-{{ year }}", + "footer.copyright": "কপিরাইট © 2002 - {{year}}", + + // "footer.link.dspace": "DSpace software", + "footer.link.dspace": "ডিস্পেস সফটওয়্যার", + + // "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": "ব্যবহারকারী সাথে চুক্তি", + + // "footer.link.feedback":"Send Feedback", + "footer.link.feedback": "মতামত পাঠানো", + + + + // "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": "ইমেইল ঠিকানা *", + + // "forgot-email.form.email.error.required": "Please fill in an email address", + "forgot-email.form.email.error.required": "একটি ইমেইল ঠিকানা পূরণ করুন", + + // "forgot-email.form.email.error.pattern": "Please fill in a valid email address", + "forgot-email.form.email.error.pattern": "একটি বৈধ ইমেইল ঠিকানা পূরণ করুন", + + // "forgot-email.form.email.hint": "This address will be verified and used as your login name.", + "forgot-email.form.email.hint": "এই ঠিকানাটি যাচাই করা হবে এবং আপনার লগইন নাম হিসাবে ব্যবহার করা হবে।", + + // "forgot-email.form.submit": "Save", + "forgot-email.form.submit": "সংরক্ষণ", + + // "forgot-email.form.success.head": "Verification email sent", + "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": "একটি বিশেষ ইউআরএল এবং আরও নির্দেশাবলী ধারণকারী {{email}} এ একটি ইমেল পাঠানো হয়েছে।", + + // "forgot-email.form.error.head": "Error when trying to register email", + "forgot-email.form.error.head": "ইমেল নিবন্ধন করার চেষ্টা করার সময় ত্রুটি", + + // "forgot-email.form.error.content": "An error occured when registering the following email address: {{ email }}", + "forgot-email.form.error.content": "নিম্নলিখিত ইমেল ঠিকানা নিবন্ধন করার সময় একটি ত্রুটি ঘটেছে:{{ 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": "নীচের বাক্সে একটি নতুন পাসওয়ার্ড লিখুন এবং এটি আবার দ্বিতীয় বাক্সে টাইপ করে এটি নিশ্চিত করুন। এটি অন্তত ছয় অক্ষর দীর্ঘ হতে হবে।", + + // "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": "ইমেইলের ঠিকানা: ", + + // "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": "পাসওয়ার্ড অন্তত ৬ অক্ষর দীর্ঘ হওয়া উচিত।", + + // "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 more", + "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.discard": "Discard", + "form.discard": "বাতিল করা", + + // "form.drag": "Drag", + "form.drag": "টেনে আনুন", + + // "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": "Save", + "form.submit": "সংরক্ষণ", + + // "form.repeatable.sort.tip": "Drop the item in the new position", + "form.repeatable.sort.tip": "নতুন অবস্থানে আইটেম ড্রপ", + + + + // "grant-deny-request-copy.deny": "Don't send copy", + "grant-deny-request-copy.deny": "কপি পাঠান না", + + // "grant-deny-request-copy.email.back": "Back", + "grant-deny-request-copy.email.back": "পেছনে", + + // "grant-deny-request-copy.email.message": "Message", + "grant-deny-request-copy.email.message": "বার্তা", + + // "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.", + "grant-deny-request-copy.email.permissions.info": "আপনি এই অনুরোধগুলি এই অনুরোধগুলি সাড়া দেওয়ার জন্য নথিতে অ্যাক্সেস সীমাবদ্ধতাগুলি পুনর্বিবেচনার জন্য ব্যবহার করতে পারেন। আপনি যদি এই সীমাবদ্ধতাগুলি সরানোর জন্য রিপোজিটরি প্রশাসকদের জিজ্ঞাসা করতে চান তবে নীচের বাক্সটি চেক করুন।", + + // "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", + "grant-deny-request-copy.email.send": "পাঠান", + + // "grant-deny-request-copy.email.subject": "Subject", + "grant-deny-request-copy.email.subject": "বিষয়", + + // "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", + "grant-deny-request-copy.grant": "কপি পাঠান", + + // "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", + "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.", + "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.", + "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.", + "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 }}>", + "grant-request-copy.email.message": "প্রিয় {{ recipientName }},\nআপনার অনুরোধের জবাবে আমি আপনাকে নথি সংক্রান্ত ফাইল(গুলি) এর একটি অনুলিপি সংযুক্তিতে পাঠাতে পেরে আনন্দিত: \"{{ itemUrl }}\" ({{ itemName }}) , যার মধ্যে আমি একজন লেখক।\n\nশুভেচ্ছা,\n{{ authorName }} <{{ authorEmail }}>", + + // "grant-request-copy.email.subject": "Request copy of document", + "grant-request-copy.email.subject": "নথির কপি অনুরোধ", + + // "grant-request-copy.error": "An error occurred", + "grant-request-copy.error": "একটি ত্রুটি ঘটেছে", + + // "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.", + "grant-request-copy.intro": "এই বার্তাটি অনুরোধের আবেদনকারীর কাছে পাঠানো হবে। অনুরোধকৃত নথি (গুলি) সংযুক্ত করা হবে।", + + // "grant-request-copy.success": "Successfully granted item request", + "grant-request-copy.success": "সফলভাবে আইটেম অনুরোধ দেওয়া", + + + + // "home.description": "", + "home.description": "অপসারণ করা আবশ্যক", + + // "home.breadcrumbs": "Home", + "home.breadcrumbs": "হোম", + + // "home.search-form.placeholder": "Search the repository ...", + "home.search-form.placeholder": "সংগ্রহস্থল অনুসন্ধান করুন ...", + + // "home.title": "Home", + "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": "গোপনীয়তা বিবৃতি", + + // "info.feedback.breadcrumbs": "Feedback", + "info.feedback.breadcrumbs": "প্রতিক্রিয়া", + + // "info.feedback.head": "Feedback", + "info.feedback.head": "প্রতিক্রিয়া", + + // "info.feedback.title": "Feedback", + "info.feedback.title": "প্রতিক্রিয়া", + + // "info.feedback.info": "Thanks for sharing your feedback about the DSpace system. Your comments are appreciated!", + "info.feedback.info": "ডিস্পেস সিস্টেম সম্পর্কে আপনার প্রতিক্রিয়া ভাগ করার জন্য ধন্যবাদ। আপনার মন্তব্য প্রশংসা করা হয়!", + + // "info.feedback.email_help": "This address will be used to follow up on your feedback.", + "info.feedback.email_help": "এই ঠিকানাটি আপনার প্রতিক্রিয়া অনুসরণ করতে ব্যবহার করা হবে।", + + // "info.feedback.send": "Send Feedback", + "info.feedback.send": "মতামত পাঠানো", + + // "info.feedback.comments": "Comments", + "info.feedback.comments": "মন্তব্য", + + // "info.feedback.email-label": "Your Email", + "info.feedback.email-label": "তোমার ইমেইল", + + // "info.feedback.create.success" : "Feedback Sent Successfully!", + "info.feedback.create.success" : "প্রতিক্রিয়া সফলভাবে পাঠানো হয়েছে!", + + // "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", + "info.feedback.error.message.required" : "একটি মন্তব্য প্রয়োজন", + + // "info.feedback.page-label" : "Page", + "info.feedback.page-label" : "পৃষ্ঠা", + + // "info.feedback.page_help" : "Tha page related to your feedback", + "info.feedback.page_help" : "আপনার মতামত সম্পর্কিত পৃষ্ঠা", + + + + // "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": "এই আইটেমটি একটি বিটস্ট্রিম আপলোড করার জন্য কোন bundles থাকে না।", + + // "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": "BUNDLE: {{ 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": "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": "Actions", + + // "item.edit.bitstreams.headers.bundle": "Bundle", + "item.edit.bitstreams.headers.bundle": "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": "আপনার পদক্ষেপ এই আইটেমটি এর বিটস্ট্রিম এবং bundles পরিবর্তন করা হয়েছে।", + + // "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.disabled.tooltip": "You're not authorized to access this tab", + "item.edit.tabs.disabled.tooltip": "আপনি এই ট্যাবটি অ্যাক্সেস করার জন্য অনুমোদিত নন", + + + // "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.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": "সমন্বিত সংগ্রহ ব্রাউজ করুন", + + // "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": "Back", + "item.edit.move.cancel": "পেছনে", + + // "item.edit.move.save-button": "Save", + "item.edit.move.save-button": "সংরক্ষণ", + + // "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": "আপনি এই আইটেমটি সরানো করতে চান সংগ্রহ করুন নির্বাচন করুন। প্রদর্শিত সংগ্রহগুলির তালিকাটি সংকীর্ণ করার জন্য আপনি বাক্সে একটি অনুসন্ধান অনুসন্ধান করতে পারেন।", + + // "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.return": "Back", + "item.edit.return": "পেছনে", + + + // "item.edit.tabs.bitstreams.head": "Bitstreams", + "item.edit.tabs.bitstreams.head": "বিটস্ট্রিমস", + + // "item.edit.tabs.bitstreams.title": "Item Edit - Bitstreams", + "item.edit.tabs.bitstreams.title": "আইটেম সম্পাদনা - বিটস্ট্রিমস", + + // "item.edit.tabs.curate.head": "Curate", + "item.edit.tabs.curate.head": "কিউরেট.", + + // "item.edit.tabs.curate.title": "Item Edit - Curate", + "item.edit.tabs.curate.title": "আইটেম সম্পাদনা - কিউরেট", + + // "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 - Relationships", + "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.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": "প্রত্যাহার ...", + + // "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": "হাতল", + + // "item.edit.tabs.status.labels.id": "Item Internal ID", + "item.edit.tabs.status.labels.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.journal-issn": "Journal ISSN", + "item.page.journal-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", + "item.search.results.head": "আইটেম অনুসন্ধান ফলাফল", + + // "item.search.title": "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.collections.loading": "Loading...", + "item.page.collections.loading": "লোড হচ্ছে ...", + + // "item.page.collections.load-more": "Load more", + "item.page.collections.load-more": "আর ঢুকাও", + + // "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": "ঘৃণা", + + // "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.page.return": "Back", + "item.page.return": "পেছনে", + + // "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", + "item.page.version.hasDraft": "একটি নতুন সংস্করণ তৈরি করা যাচ্ছে না কারণ ভার্সন হিস্ট্রিতে একটি জমা ইনপ্রগ্রেসস অবস্থাই রয়েছে", + + // "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": "ওআরসিআইডি:", + + // "item.preview.project.funder.name": "Funder:", + "item.preview.project.funder.name": "ফান্ডার:", + + // "item.preview.project.funder.identifier": "Funder Identifier:", + "item.preview.project.funder.identifier": "ফান্ডার শনাক্তকারী:", + + // "item.preview.oaire.awardNumber": "Funding ID:", + "item.preview.oaire.awardNumber": "ফান্ডিং আইডি:", + + // "item.preview.dc.title.alternative": "Acronym:", + "item.preview.dc.title.alternative": "সংক্ষিপ্ত রূপ:", + + // "item.preview.dc.coverage.spatial": "Jurisdiction:", + "item.preview.dc.coverage.spatial": "এখতিয়ার:", + + // "item.preview.oaire.fundingStream": "Funding Stream:", + "item.preview.oaire.fundingStream": "ফান্ডিং স্ট্রীম:", + + + + // "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": "Back", + "item.version.history.return": "পেছনে", + + // "item.version.history.selected": "Selected version", + "item.version.history.selected": "নির্বাচিত সংস্করণ", + + // "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": "সংস্করণ.", + + // "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": "সারসংক্ষেপ", + + // "item.version.history.table.workspaceItem": "Workspace item", + "item.version.history.table.workspaceItem": "ওয়ার্কস্পেস আইটেম", + + // "item.version.history.table.workflowItem": "Workflow item", + "item.version.history.table.workflowItem": "ওয়ার্কফ্লো আইটেম", + + // "item.version.history.table.actions": "Action", + "item.version.history.table.actions": "অ্যাকশন", + + // "item.version.history.table.action.editWorkspaceItem": "Edit workspace item", + "item.version.history.table.action.editWorkspaceItem": "ওয়ার্কস্পেস আইটেম সম্পাদনা করুন", + + // "item.version.history.table.action.editSummary": "Edit summary", + "item.version.history.table.action.editSummary": "Sumary সম্পাদনা করুন", + + // "item.version.history.table.action.saveSummary": "Save summary edits", + "item.version.history.table.action.saveSummary": "সারাংশ সম্পাদনা সংরক্ষণ করুন", + + // "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", + "item.version.history.table.action.newVersion": "এই এক থেকে নতুন সংস্করণ তৈরি করুন", + + // "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", + "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.", + "item.version.notice": "এটি এই আইটেমটির সর্বশেষ সংস্করণ নয়। সর্বশেষ সংস্করণটি পাওয়া যাবে ।", + + + // "item.version.create.modal.header": "New version", + "item.version.create.modal.header": "নতুন সংস্করণ", + + // "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}}", + "item.version.create.modal.text.startingFrom": "সংস্করণ থেকে শুরু হচ্ছে {{version}}", + + // "item.version.create.modal.button.confirm": "Create", + "item.version.create.modal.button.confirm": "সৃষ্টি", + + // "item.version.create.modal.button.confirm.tooltip": "Create new version", + "item.version.create.modal.button.confirm.tooltip": "নতুন সংস্করণ তৈরি করুন", + + // "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", + "item.version.create.modal.button.cancel.tooltip": "নতুন সংস্করণ তৈরি করবেন না", + + // "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", + "item.version.create.modal.form.summary.placeholder": "নতুন সংস্করণের জন্য সারাংশ সন্নিবেশ করান", + + // "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", + "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", + "item.version.create.notification.inProgress" : "একটি নতুন সংস্করণ তৈরি করা যাবে না কারণ ভার্সন হিস্ট্রিতে একটি জমা ইনপ্রগ্রেসস অবস্থাই রয়েছে", + + + // "item.version.delete.modal.header": "Delete version", + "item.version.delete.modal.header": "সংস্করণ মুছে ফেলুন", + + // "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", + "item.version.delete.modal.button.confirm": "মুছে ফেলা", + + // "item.version.delete.modal.button.confirm.tooltip": "Delete this version", + "item.version.delete.modal.button.confirm.tooltip": "এই সংস্করণ মুছে দিন", + + // "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", + "item.version.delete.modal.button.cancel.tooltip": "এই সংস্করণ মুছে ফেলবেন না", + + // "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", + "item.version.delete.notification.failure" : "সংস্করণ নম্বর {{version}} মুছে ফেলা হয়নি", + + + // "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", + "item.version.edit.notification.failure" : "সংস্করণ নম্বর {{version}}-এর সারাংশ পরিবর্তন করা হয়নি", + + + + // "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": "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": "জার্নাল ইশান", + + // "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": "ভলিউম", + + + // "iiifsearchable.listelement.badge": "Document Media", + "iiifsearchable.listelement.badge": "ডকুমেন্ট মিডিয়া", + + // "iiifsearchable.page.titleprefix": "Document: ", + "iiifsearchable.page.titleprefix": "দলিল: ", + + // "iiifsearchable.page.doi": "Permanent Link: ", + "iiifsearchable.page.doi": "স্থায়ী লিঙ্ক: ", + + // "iiifsearchable.page.issue": "Issue: ", + "iiifsearchable.page.issue": "সমস্যা: ", + + // "iiifsearchable.page.description": "Description: ", + "iiifsearchable.page.description": "বর্ণনা: ", + + // "iiifviewer.fullscreen.notice": "Use full screen for better viewing.", + "iiifviewer.fullscreen.notice": "ভাল দেখার জন্য পূর্ণ পর্দা ব্যবহার করুন।", + + // "iiif.listelement.badge": "Image Media", + "iiif.listelement.badge": "ছবি মিডিয়া", + + // "iiif.page.titleprefix": "Image: ", + "iiif.page.titleprefix": "ছবি: ", + + // "iiif.page.doi": "Permanent Link: ", + "iiif.page.doi": "স্থায়ী লিঙ্ক: ", + + // "iiif.page.issue": "Issue: ", + "iiif.page.issue": "সমস্যা: ", + + // "iiif.page.description": "Description: ", + "iiif.page.description": "বর্ণনা: ", + + + // "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": "ইমেইল ঠিকানা", + + // "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.oidc": "Log in with OIDC", + "login.form.oidc": "OIDC সঙ্গে লগ ইন করুন", + + // "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": "Management", + "menu.header.admin": "ব্যবস্থাপনা", + + // "menu.header.image.logo": "Repository logo", + "menu.header.image.logo": "রিপোজিটরি লোগো", + + // "menu.header.admin.description": "Management menu", + "menu.header.admin.description": "ম্যানেজমেন্ট মেনু", + + + + // "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_tasks": "Curation Task menu section", + "menu.section.icon.curation_tasks": "নিরাময় টাস্ক মেনু বিভাগ", + + // "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.workflow": "Administer workflow menu section", + "menu.section.icon.workflow": "প্রশাসনিক কর্মধারা মেনু সেকশন", + + // "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.breadcrumbs": "MyDSpace", + "mydspace.breadcrumbs": "আমার ডিস্পেস", + + // "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": "কোন ইউ আর আই (uri) নেই", + + // "mydspace.search-form.placeholder": "Search in mydspace...", + "mydspace.search-form.placeholder": "আমার ডিস্পেস এর মধ্যে অনুসন্ধান করুন ...", + + // "mydspace.show.workflow": "Workflow 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": "User profile menu and Log Out", + "nav.logout": "ব্যবহারকারী প্রোফাইল মেনু এবং লগ আউট", + + // "nav.main.description": "Main navigation bar", + "nav.main.description": "প্রধান ন্যাভিগেশন বার", + + // "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": "ই-পারসন ছদ্মবেশী থামান", + + // "nav.toggle" : "Toggle navigation", + "nav.toggle" : "নেভিগেশন টগল করুন", + + // "nav.user.description" : "User profile bar", + "nav.user.description" : "ব্যবহারকারীর প্রোফাইল বার", + + // "none.listelement.badge": "Item", + "none.listelement.badge": "আইটেম", + + + // "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": "আইডি", + + // "orgunit.page.titleprefix": "Organizational Unit: ", + "orgunit.page.titleprefix": "প্রাতিষ্ঠানিক একক অংশ: ", + + + + // "pagination.options.description": "Pagination options", + "pagination.options.description": "পেজিনেশনের বিকল্পগুলো", + + // "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": "ইমেইল ঠিকানা", + + // "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": "স্টাফ আইডি", + + // "person.page.titleprefix": "Person: ", + "person.page.titleprefix": "ব্যক্তি: ", + + // "person.search.results.head": "Person Search Results", + "person.search.results.head": "ব্যক্তি অনুসন্ধান ফলাফল", + + // "person.search.title": "Person Search", + "person.search.title": "ব্যক্তি অনুসন্ধান", + + + + // "process.new.select-parameters": "Parameters", + "process.new.select-parameters": "পরামিতি", + + // "process.new.cancel": "Cancel", + "process.new.cancel": "বাতিল করুন", + + // "process.new.submit": "Save", + "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 (UTC)", + "process.overview.table.finish" : "শেষ সময় (UTC)", + + // "process.overview.table.id" : "Process ID", + "process.overview.table.id" : "প্রসেস আইডি", + + // "process.overview.table.name" : "Name", + "process.overview.table.name" : "নাম", + + // "process.overview.table.start" : "Start time (UTC)", + "process.overview.table.start" : "শুরুর সময় (UTC)", + + // "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": "Save", + "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": "ইমেইল ঠিকানা", + + // "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": "পাসওয়ার্ড অন্তত ৬ অক্ষর দীর্ঘ হওয়া উচিত।", + + // "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": "পাসওয়ার্ড অন্তত ৬ অক্ষর দীর্ঘ হতে হবে।", + + // "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": "আইডি", + + // "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": "Publication Search", + "publication.search.title": "প্রকাশনার অনুসন্ধান", + + + // "media-viewer.next": "Next", + "media-viewer.next": "পরবর্তী", + + // "media-viewer.previous": "Previous", + "media-viewer.previous": "পূর্ববর্তী", + + // "media-viewer.playlist": "Playlist", + "media-viewer.playlist": "প্লেলিস্ট", + + + // "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": "ইমেইল ঠিকানা", + + // "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": "পাসওয়ার্ড অন্তত ৬ অক্ষর দীর্ঘ হওয়া উচিত।", + + // "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": "ইমেইল ঠিকানা *", + + // "register-page.registration.email.error.required": "Please fill in an email address", + "register-page.registration.email.error.required": "একটি ইমেইল ঠিকানা পূরণ করুন", + + // "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": "যাচাই ইমেইল পাঠানো হয়েছে", + + // "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}} এ একটি ইমেল পাঠানো হয়েছে।", + + // "register-page.registration.error.head": "Error when trying to register email", + "register-page.registration.error.head": "ইমেল নিবন্ধনের চেষ্টা করার সময় ত্রুটি", + + // "register-page.registration.error.content": "An error occured when registering the following email address: {{ email }}", + "register-page.registration.error.content": "নিম্নলিখিত ইমেল ঠিকানা নিবন্ধন করার সময় একটি ত্রুটি ঘটেছে: {{ email }}", + + + + // "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": "দুটি আইটেমের মধ্যে সম্পর্কের প্রকার {{ 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": "অবদানকারী", + + // "relationships.isContributorOf.OrgUnit": "Contributor (Organizational Unit)", + "relationships.isContributorOf.OrgUnit": "অবদানকারী (সাংগঠনিক ইউনিট)", + + // "relationships.isContributorOf.Person": "Contributor", + "relationships.isContributorOf.Person": "অবদানকারী", + + // "relationships.isFundingAgencyOf.OrgUnit": "Funder", + "relationships.isFundingAgencyOf.OrgUnit": "তহবিল.", + + + // "repository.image.logo": "Repository logo", + "repository.image.logo": "রিপোজিটরি লোগো", + + // "repository.title.prefix": "DSpace Angular :: ", + "repository.title.prefix": "ডিস্পেস অ্যাংগুলার :: ", + + // "repository.title.prefixDSpace": "DSpace Angular ::", + "repository.title.prefixDSpace": "ডিস্পেস অ্যাংগুলার ::", + + + // "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": "একটি নতুন বান্ডিল নীতি যোগ করুন", + + // "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": "Operation successful", + "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", + "resource-policies.form.eperson-group-list.table.headers.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": "আইডি", + + // "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": "Search", + "search.title": "অনুসন্ধান করুন", + + // "search.breadcrumbs": "Search", + "search.breadcrumbs": "অনুসন্ধান করুন", + + // "search.search-form.placeholder": "Search the repository ...", + "search.search-form.placeholder": "সংগ্রহস্থল অনুসন্ধান করুন ...", + + + // "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": "শেষ জন্ম তারিখ", + + // "search.filters.applied.f.birthDate.min": "Start birth date", + "search.filters.applied.f.birthDate.min": "জন্ম তারিখ শুরু করুন", + + // "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.author.label": "Search author name", + "search.filters.filter.author.label": "অনুসন্ধান লেখক নাম", + + // "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.birthDate.label": "Search birth date", + "search.filters.filter.birthDate.label": "জন্ম তারিখ অনুসন্ধান করুন", + + // "search.filters.filter.collapse": "Collapse filter", + "search.filters.filter.collapse": "ফিল্টার সঙ্কুচিত করুন", + + // "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.creativeDatePublished.label": "Search date published", + "search.filters.filter.creativeDatePublished.label": "অনুসন্ধান তারিখ প্রকাশিত", + + // "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.creativeWorkEditor.label": "Search editor", + "search.filters.filter.creativeWorkEditor.label": "অনুসন্ধান সম্পাদক", + + // "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.creativeWorkKeywords.label": "Search subject", + "search.filters.filter.creativeWorkKeywords.label": "অনুসন্ধান বিষয়", + + // "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.creativeWorkPublisher.label": "Search publisher", + "search.filters.filter.creativeWorkPublisher.label": "অনুসন্ধান প্রকাশক", + + // "search.filters.filter.dateIssued.head": "Date", + "search.filters.filter.dateIssued.head": "তারিখ", + + // "search.filters.filter.dateIssued.max.placeholder": "Maximum Date", + "search.filters.filter.dateIssued.max.placeholder": "সর্বোচ্চ তারিখ", + + // "search.filters.filter.dateIssued.max.label": "End", + "search.filters.filter.dateIssued.max.label": "শেষ", + + // "search.filters.filter.dateIssued.min.placeholder": "Minimum Date", + "search.filters.filter.dateIssued.min.placeholder": "নূন্যতম তারিখ", + + // "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": "জমাদানের তারিখ", + + // "search.filters.filter.dateSubmitted.placeholder": "Date submitted", + "search.filters.filter.dateSubmitted.placeholder": "জমাদানের তারিখ", + + // "search.filters.filter.dateSubmitted.label": "Search date submitted", + "search.filters.filter.dateSubmitted.label": "অনুসন্ধান তারিখ জমা হয়েছে", + + // "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.entityType.label": "Search item type", + "search.filters.filter.entityType.label": "আইটেম টাইপ অনুসন্ধান করুন", + + // "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": "ফাইল আছে", + + // "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.itemtype.label": "Search type", + "search.filters.filter.itemtype.label": "ধরন অনুসন্ধান করুন", + + // "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.jobTitle.label": "Search job title", + "search.filters.filter.jobTitle.label": "কাজের শিরোনাম অনুসন্ধান করুন", + + // "search.filters.filter.knowsLanguage.head": "Known language", + "search.filters.filter.knowsLanguage.head": "পরিচিত ভাষা", + + // "search.filters.filter.knowsLanguage.placeholder": "Known language", + "search.filters.filter.knowsLanguage.placeholder": "পরিচিত ভাষা", + + // "search.filters.filter.knowsLanguage.label": "Search known language", + "search.filters.filter.knowsLanguage.label": "পরিচিত ভাষা অনুসন্ধান করুন", + + // "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.namedresourcetype.label": "Search status", + "search.filters.filter.namedresourcetype.label": "অবস্থা অনুসন্ধান করুন", + + // "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.objectpeople.label": "Search people", + "search.filters.filter.objectpeople.label": "ব্যাক্তি অনুসন্ধান করুন", + + // "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.organizationAddressCountry.label": "Search country", + "search.filters.filter.organizationAddressCountry.label": "দেশ অনুসন্ধান করুন", + + // "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.organizationAddressLocality.label": "Search city", + "search.filters.filter.organizationAddressLocality.label": "অনুসন্ধান শহর", + + // "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.organizationFoundingDate.label": "Search date founded", + "search.filters.filter.organizationFoundingDate.label": "প্রতিষ্ঠার তারিখ অনুসন্ধান করুন", + + // "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.scope.label": "Search scope filter", + "search.filters.filter.scope.label": "স্কোপ ফিল্টার অনুসন্ধান করুন", + + // "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.subject.label": "Search subject", + "search.filters.filter.subject.label": "বিষয় অনুসন্ধান করুন", + + // "search.filters.filter.submitter.head": "Submitter", + "search.filters.filter.submitter.head": "জমাকারী", + + // "search.filters.filter.submitter.placeholder": "Submitter", + "search.filters.filter.submitter.placeholder": "জমাকারী", + + // "search.filters.filter.submitter.label": "Search submitter", + "search.filters.filter.submitter.label": "জমাকারী অনুসন্ধান করুন", + + + + // "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.filters.search.submit": "Submit", + "search.filters.search.submit": "জমা দিন", + + + + // "search.form.search": "Search", + "search.form.search": "অনুসন্ধান করুন", + + // "search.form.search_dspace": "All repository", + "search.form.search_dspace": "সব সংগ্রহস্থল", + + // "search.form.scope.all": "All of DSpace", + "search.form.scope.all": "ডিস্পেসের সবকিছু", + + + + // "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": "আপনার অনুসন্ধানের কোন ফলাফল পাওয়া যাই নি", + + + // "default.search.results.head": "Search Results", + "default.search.results.head": "অনুসন্ধানের ফলাফল", + + + // "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.ASC": "Least Relevant", + "sorting.score.ASC": "ন্যুনতম প্রাসঙ্গিক", + + // "sorting.score.DESC": "Most Relevant", + "sorting.score.DESC": "সবচেয়ে প্রাসঙ্গিক", + + // "sorting.dc.date.issued.ASC": "Date Issued Ascending", + "sorting.dc.date.issued.ASC": "ইস্যু করার তারিখ উর্দ্ধক্রমানুসারে", + + // "sorting.dc.date.issued.DESC": "Date Issued Descending", + "sorting.dc.date.issued.DESC": "ইস্যু করার তারিখ নিম্নক্রমানুসারে", + + // "sorting.dc.date.accessioned.ASC": "Accessioned Date Ascending", + "sorting.dc.date.accessioned.ASC": "যোগদানের তারিখ উর্দ্ধক্রমানুসারে", + + // "sorting.dc.date.accessioned.DESC": "Accessioned Date Descending", + "sorting.dc.date.accessioned.DESC": "যোগদানের তারিখ নিম্নক্রমানুসারে", + + // "sorting.lastModified.ASC": "Last modified Ascending", + "sorting.lastModified.ASC": "সর্বশেষ সংশোধিত উর্দ্ধক্রমানুসারে", + + // "sorting.lastModified.DESC": "Last modified Descending", + "sorting.lastModified.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.breadcrumbs": "Edit Submission", + "submission.edit.breadcrumbs": "সম্পাদনা সম্পাদনা করুন", + + // "submission.edit.title": "Edit Submission", + "submission.edit.title": "জমা সম্পাদনা করুন", + + // "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": "আপনার নতুন জমা দেওয়ার বিশেষাধিকার নেই।", + + // "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.info.saved": "Saved", + "submission.general.info.saved": "সংরক্ষিত", + + // "submission.general.info.pending-changes": "Unsaved changes", + "submission.general.info.pending-changes": "অসংরক্ষিত পরিবর্তন", + + // "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.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", + "submission.import-external.title.JournalIssue": "একটি বহিরাগত উৎস থেকে একটি জার্নাল ইস্যু আমদানি করুন", + + // "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", + "submission.import-external.title.OrgUnit": "একটি বহিরাগত উৎস থেকে একটি প্রকাশক আমদানি করুন", + + // "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", + "submission.import-external.title.Project": "একটি বহিরাগত উৎস থেকে একটি প্রকল্প আমদানি করুন", + + // "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", + "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": "ডিস্পেস এ আমদানি করার জন্য ওয়েব থেকে আইটেমগুলি খুঁজে পেতে উপরের একটি প্রশ্নের উপরে প্রবেশ করুন।", + + // "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.sherpaJournalIssn": "SHERPA Journals by ISSN", + "submission.import-external.source.sherpaJournalIssn": "SHERPA জার্নাল দ্বারা ISSN", + + // "submission.import-external.source.sherpaPublisher": "SHERPA Publishers", + "submission.import-external.source.sherpaPublisher": "SHERPA পাবলিশার্স", + + // "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": "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": "লাইব্রেরি অব কংগ্রেসের নামগুলো", + + // "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-button-title.isProjectOfPublication": "Project", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.isProjectOfPublication": "প্রকল্প", + + // "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", + "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", + "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": "দূরবর্তী লেখক আমদানি করুন", + + // "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": "কর্তৃপক্ষ", + + // "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": "একটি নতুন স্থানীয় কর্তৃপক্ষ এন্ট্রি হিসাবে আমদানি করুন", + + // "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": "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": "এলসি নাম থেকে আমদানি করুন", + + // "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": "শেরপা জার্নাল থেকে আমদানি করা হচ্ছে", + + // "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": "শেরপা প্রকাশক থেকে আমদানি করা হচ্ছে", + + // "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.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": "সব নির্বাচন করুন", + + // "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": "শেরপা জার্নাল ({{count}})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa Publishers ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "শেরপা পাবলিশার্স ({{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": "এলসি নাম ({{count}})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.pubmed": "PubMed ({{ count }})", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.pubmed": "PubMed ({{count}})", + + // "submission.sections.describe.relationship-lookup.search-tab.tab-title.arxiv": "arXiv ({{ count }})", + "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.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", + "submission.sections.describe.relationship-lookup.search-tab.tab-title.isProjectOfPublication": "প্রকল্প", + + // "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", + "submission.sections.describe.relationship-lookup.selection-tab.title.openAIREFunding": "OpenAIRE এ পি আই (API) কে আর্থিক ভাবে সাহায্য করা", + + // "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", + "submission.sections.describe.relationship-lookup.title.isProjectOfPublication": "প্রকল্প", + + // "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...", + "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": "জার্নাল ভলিউম", + + // "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.accessCondition": "Item access conditions", + "submission.sections.submit.progressbar.accessCondition": "আইটেম অ্যাক্সেসের শর্তাবলী", + + // "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.status.errors.title": "Errors", + "submission.sections.status.errors.title": "ত্রুটি", + + // "submission.sections.status.valid.title": "Valid", + "submission.sections.status.valid.title": "বৈধ", + + // "submission.sections.status.warnings.title": "Warnings", + "submission.sections.status.warnings.title": "সতর্কতা", + + // "submission.sections.status.errors.aria": "has errors", + "submission.sections.status.errors.aria": "ত্রুটি আছে", + + // "submission.sections.status.valid.aria": "is valid", + "submission.sections.status.valid.aria": "বৈধ", + + // "submission.sections.status.warnings.aria": "has warnings", + "submission.sections.status.warnings.aria": "সতর্কতা আছে", + + // "submission.sections.toggle.open": "Open section", + "submission.sections.toggle.open": "বিভাগ খুলুন", + + // "submission.sections.toggle.close": "Close section", + "submission.sections.toggle.close": "বিভাগ বন্ধ করুন", + + // "submission.sections.toggle.aria.open": "Expand {{sectionHeader}} section", + "submission.sections.toggle.aria.open": "বিস্তৃত করুন {{sectionHeader}} বিভাগ", + + // "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": "বাতিল করুন", + + // "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.download.title": "Download bitstream", + "submission.sections.upload.download.title": "বিটস্ট্রিম ডাউনলোড করুন", + + // "submission.sections.upload.drop-message": "Drop files to attach them to the item", + "submission.sections.upload.drop-message": "ফাইলগুলিকে আইটেমের সাথে সংযুক্ত করতে ড্রপ করুন৷", + + // "submission.sections.upload.edit.title": "Edit bitstream", + "submission.sections.upload.edit.title": "বিটস্ট্রিম সম্পাদনা করুন", + + // "submission.sections.upload.form.access-condition-label": "Access condition type", + "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", + "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.", + "submission.sections.upload.form.date-required-from": "যে তারিখ থেকে প্রবেশাধিকার প্রয়োজন", + + // "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", + "submission.sections.upload.form.from-hint": "যে তারিখ থেকে সম্পর্কিত অ্যাক্সেস শর্ত প্রয়োগ করা হয়েছে তা নির্বাচন করুন", + + // "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-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.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.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": "চেক যখন এই আইটেমটি অনুসন্ধান / ব্রাউজ আবিষ্কারযোগ্য হবে। আইটেমটি unchecked যখন শুধুমাত্র সরাসরি লিঙ্কের মাধ্যমে উপলব্ধ হবে এবং অনুসন্ধান / ব্রাউজ প্রদর্শিত হবে না।", + + // "submission.sections.accesses.form.discoverable-label": "Discoverable", + "submission.sections.accesses.form.discoverable-label": "আবিষ্কারযোগ্য", + + // "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", + "submission.sections.accesses.form.access-condition-hint": "আইটেমটি জমা হওয়ার পরে আবেদন করার জন্য একটি অ্যাক্সেস শর্ত নির্বাচন করুন", + + // "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.", + "submission.sections.accesses.form.date-required-from": "যে তারিখ থেকে প্রবেশাধিকার প্রয়োজন", + + // "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", + "submission.sections.accesses.form.from-label": "যখন থেকে অ্যাক্সেস মঞ্জুর করবেন", + + // "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", + "submission.sections.accesses.form.from-placeholder": "থেকে", + + // "submission.sections.accesses.form.group-label": "Group", + "submission.sections.accesses.form.group-label": "গ্রুপ", + + // "submission.sections.accesses.form.group-required": "Group is required.", + "submission.sections.accesses.form.group-required": "গ্রুপ প্রয়োজন।", + + // "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", + "submission.sections.accesses.form.until-hint": "সংশ্লিষ্ট অ্যাক্সেস শর্ত প্রযোজ্য হওয়া পর্যন্ত তারিখটি নির্বাচন করুন", + + // "submission.sections.accesses.form.until-placeholder": "Until", + "submission.sections.accesses.form.until-placeholder": "পর্যন্ত", + + + // "submission.submit.breadcrumbs": "New submission", + "submission.submit.breadcrumbs": "নতুন নথি/জমা", + + // "submission.submit.title": "New 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": "বিস্তারিত দেখাও", + + + + // "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": "প্রকল্পের লোগো", + + // "thumbnail.project.placeholder": "Project Placeholder Image", + "thumbnail.project.placeholder": "প্রকল্প স্থানধারক ছবি", + + // "thumbnail.orgunit.alt": "OrgUnit Logo", + "thumbnail.orgunit.alt": "অরগ ইউনিট লোগো", + + // "thumbnail.orgunit.placeholder": "OrgUnit Placeholder Image", + "thumbnail.orgunit.placeholder": "অরগ ইউনিট স্থানধারক ছবি", + + // "thumbnail.person.alt": "Profile Picture", + "thumbnail.person.alt": "প্রোফাইল ছবি", + + // "thumbnail.person.placeholder": "No Profile Picture Available", + "thumbnail.person.placeholder": "কোন প্রোফাইল ছবি পাওয়া যাই নি", + + + + // "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": "নরওয়েজিয়ান বিজ্ঞান সূচক", + + // "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.delete.btn-title": "Delete", + "uploader.delete.btn-title": "মুছুন", + + // "uploader.or": ", or ", + "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": "রিয়েল মেটাডেটা হিসাবে আপনি ভার্চুয়াল মেটাডেটা সংরক্ষণ করতে চান এমন আইটেমগুলি নির্বাচন করুন", + + + + // "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", + "workflow.search.results.head": "ওয়ার্কফ্লো কাজ", + + + + // "workflow-item.edit.breadcrumbs": "Edit workflowitem", + "workflow-item.edit.breadcrumbs": "ওয়ার্কফ্লিউটেম সম্পাদনা করুন", + + // "workflow-item.edit.title": "Edit workflowitem", + "workflow-item.edit.title": "ওয়ার্কফ্লিউটেম সম্পাদনা করুন", + + // "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": "ফেরত পাঠাও", + + // "workflow-item.view.breadcrumbs": "Workflow View", + "workflow-item.view.breadcrumbs": "ওয়ার্কফ্লো ভিউ", + + + // "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?", + "idle-modal.info": "নিরাপত্তার কারণে ব্যবহারকারী সেশন {{timeToExpire}}}} কয়েক মিনিটের পরে মেয়াদ শেষ হয়। আপনার অধিবেশন শীঘ্রই মেয়াদ শেষ হবে। আপনি এটি প্রসারিত বা লগ আউট করতে চান?", + + // "idle-modal.log-out": "Log out", + "idle-modal.log-out": "প্রস্থান", + + // "idle-modal.extend-session": "Extend session" + "idle-modal.extend-session": "সেশন প্রসারিত করুন" + + +} \ No newline at end of file diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 1cbdca762e..b6b75158d6 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -178,7 +178,13 @@ export const environment: BuildConfig = { code: 'lv', label: 'Latviešu', active: true, - }], + } + // { + // code: 'bn', + // label: 'বাংলা', + // active: true, + // } + ], // Browse-By Pages browseBy: { From 716cea376da914d56f1f96a7aab3d7bc69a63f07 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 22 Mar 2022 13:29:23 +0100 Subject: [PATCH 093/570] fix circular dependency issues relating to request models --- .../epeople-registry.component.spec.ts | 2 +- .../eperson-form.component.spec.ts | 2 +- .../bitstream-formats.component.spec.ts | 2 +- .../bitstream-formats.component.ts | 2 +- .../metadata-registry.component.spec.ts | 2 +- .../metadata-schema.component.spec.ts | 2 +- .../browse-by-date-page.component.spec.ts | 2 +- .../browse-by-title-page.component.spec.ts | 2 +- .../community-list-datasource.ts | 2 +- .../community-list-service.spec.ts | 2 +- .../community-list-service.ts | 2 +- .../community-list.component.ts | 2 +- ...page-sub-collection-list.component.spec.ts | 2 +- ...-page-sub-community-list.component.spec.ts | 2 +- src/app/core/auth/auth-request.service.ts | 3 +- src/app/core/auth/authenticated.guard.ts | 2 +- .../auth/token-response-parsing.service.ts | 2 +- .../browse-definition-data.service.spec.ts | 2 +- .../browse/browse-definition-data.service.ts | 4 +- src/app/core/browse/browse.service.spec.ts | 2 +- .../core/cache/builders/link.service.spec.ts | 2 +- .../remote-data-build.service.spec.ts | 2 +- .../builders/remote-data-build.service.ts | 6 +-- .../core/cache/object-cache.service.spec.ts | 3 +- src/app/core/cache/object-cache.service.ts | 2 +- .../core/cache/server-sync-buffer.effects.ts | 2 +- src/app/core/config/config.service.spec.ts | 3 +- src/app/core/config/config.service.ts | 2 +- .../config/submission-forms-config.service.ts | 2 +- .../submission-uploads-config.service.ts | 2 +- src/app/core/core-state.model.ts | 30 ++++++++++++ src/app/core/core.module.ts | 3 +- src/app/core/core.reducers.ts | 38 +++++---------- src/app/core/core.selectors.ts | 2 +- .../base-response-parsing.service.spec.ts | 3 +- .../data/base-response-parsing.service.ts | 2 +- src/app/core/data/bitstream-data.service.ts | 5 +- .../bitstream-format-data.service.spec.ts | 4 +- .../data/bitstream-format-data.service.ts | 2 +- src/app/core/data/bundle-data.service.spec.ts | 2 +- src/app/core/data/bundle-data.service.ts | 5 +- src/app/core/data/collection-data.service.ts | 8 ++-- src/app/core/data/comcol-data.service.spec.ts | 4 +- src/app/core/data/comcol-data.service.ts | 2 +- src/app/core/data/community-data.service.ts | 4 +- .../core/data/configuration-data.service.ts | 2 +- ...content-source-response-parsing.service.ts | 2 +- src/app/core/data/data.service.spec.ts | 5 +- src/app/core/data/data.service.ts | 4 +- .../data/debug-response-parsing.service.ts | 2 +- .../data/dso-redirect-data.service.spec.ts | 2 +- .../core/data/dso-redirect-data.service.ts | 2 +- .../core/data/dso-response-parsing.service.ts | 2 +- .../core/data/dspace-object-data.service.ts | 4 +- .../dspace-rest-response-parsing.service.ts | 2 +- .../endpoint-map-response-parsing.service.ts | 2 +- src/app/core/data/entity-type.service.ts | 2 +- .../data/eperson-registration.service.spec.ts | 2 +- src/app/core/data/external-source.service.ts | 4 +- .../facet-config-response-parsing.service.ts | 2 +- .../facet-value-response-parsing.service.ts | 2 +- .../authorization-data.service.spec.ts | 2 +- .../authorization-data.service.ts | 4 +- .../feature-data.service.ts | 2 +- ...discovery-page-response-parsing.service.ts | 2 +- src/app/core/data/find-list-options.model.ts | 14 ++++++ .../core/data/href-only-data.service.spec.ts | 2 +- src/app/core/data/href-only-data.service.ts | 4 +- src/app/core/data/item-data.service.spec.ts | 7 +-- src/app/core/data/item-data.service.ts | 6 ++- .../data/item-template-data.service.spec.ts | 6 +-- .../core/data/item-template-data.service.ts | 2 +- .../data/metadata-field-data.service.spec.ts | 2 +- .../core/data/metadata-field-data.service.ts | 4 +- .../core/data/metadata-schema-data.service.ts | 2 +- .../data/mydspace-response-parsing.service.ts | 2 +- .../object-updates.service.spec.ts | 2 +- .../object-updates/object-updates.service.ts | 2 +- src/app/core/data/parsing.service.ts | 2 +- .../data/processes/process-data.service.ts | 2 +- .../data/processes/script-data.service.ts | 5 +- .../registration-response-parsing.service.ts | 2 +- .../core/data/relationship-type.service.ts | 2 +- .../core/data/relationship.service.spec.ts | 5 +- src/app/core/data/relationship.service.ts | 6 ++- src/app/core/data/request-entry.model.ts | 13 +++++ src/app/core/data/request-state.model.ts | 8 ++++ src/app/core/data/request.actions.ts | 2 +- src/app/core/data/request.effects.ts | 7 +-- src/app/core/data/request.models.ts | 48 ++++++------------- src/app/core/data/request.reducer.spec.ts | 3 +- src/app/core/data/request.reducer.ts | 24 +--------- src/app/core/data/request.service.spec.ts | 9 ++-- src/app/core/data/request.service.ts | 14 +++--- src/app/core/data/response-state.model.ts | 13 +++++ ...rest-request-with-response-parser.model.ts | 14 ++++++ src/app/core/data/rest-request.model.ts | 20 ++++++++ src/app/core/data/root-data.service.ts | 4 +- .../data/search-response-parsing.service.ts | 2 +- src/app/core/data/site-data.service.spec.ts | 4 +- src/app/core/data/site-data.service.ts | 2 +- ...atus-code-only-response-parsing.service.ts | 2 +- src/app/core/data/version-data.service.ts | 4 +- .../core/data/version-history-data.service.ts | 4 +- .../core/data/workflow-action-data.service.ts | 4 +- .../core/eperson/eperson-data.service.spec.ts | 5 +- src/app/core/eperson/eperson-data.service.ts | 3 +- .../core/eperson/group-data.service.spec.ts | 5 +- src/app/core/eperson/group-data.service.ts | 3 +- src/app/core/history/selectors.ts | 2 +- src/app/core/index/index.effects.ts | 2 +- src/app/core/index/index.selectors.ts | 2 +- .../builder/json-patch-operations-builder.ts | 2 +- .../json-patch-operations.service.spec.ts | 4 +- .../json-patch-operations.service.ts | 2 +- src/app/core/json-patch/selectors.ts | 2 +- src/app/core/metadata/metadata.service.ts | 2 +- .../pagination/pagination.service.spec.ts | 2 +- src/app/core/pagination/pagination.service.ts | 2 +- .../core/registry/registry.service.spec.ts | 2 +- src/app/core/registry/registry.service.ts | 2 +- .../resource-policy.service.spec.ts | 4 +- .../resource-policy.service.ts | 4 +- src/app/core/services/route.service.ts | 2 +- src/app/core/shared/operators.spec.ts | 2 +- src/app/core/shared/request.operators.ts | 5 +- .../search-configuration.service.spec.ts | 2 +- .../search/search-configuration.service.ts | 2 +- .../core/shared/search/search.service.spec.ts | 4 +- src/app/core/shared/search/search.service.ts | 5 +- .../statistics/usage-report-data.service.ts | 2 +- .../submission-cc-license-data.service.ts | 2 +- .../submission-cc-license-url-data.service.ts | 2 +- ...sion-json-patch-operations.service.spec.ts | 2 +- ...ubmission-json-patch-operations.service.ts | 2 +- .../submission-response-parsing.service.ts | 2 +- .../submission/submission-rest.service.ts | 2 +- .../models/vocabulary-find-options.model.ts | 2 +- .../vocabularies/vocabulary.service.spec.ts | 2 +- .../vocabularies/vocabulary.service.ts | 4 +- .../submission/workflowitem-data.service.ts | 2 +- .../submission/workspaceitem-data.service.ts | 2 +- .../tasks/claimed-task-data.service.spec.ts | 4 +- .../core/tasks/claimed-task-data.service.ts | 4 +- .../core/tasks/pool-task-data.service.spec.ts | 4 +- src/app/core/tasks/pool-task-data.service.ts | 4 +- .../tasks/task-response-parsing.service.ts | 2 +- src/app/core/tasks/tasks.service.spec.ts | 5 +- src/app/core/tasks/tasks.service.ts | 2 +- .../forgot-password-form.component.spec.ts | 2 +- .../forgot-password-form.component.ts | 2 +- ...top-level-community-list.component.spec.ts | 2 +- ...-and-drop-bitstream-list.component.spec.ts | 2 +- .../full-file-section.component.spec.ts | 2 +- .../related-items/related-items-component.ts | 2 +- .../collection-selector.component.spec.ts | 2 +- .../my-dspace-page.component.spec.ts | 2 +- .../process-overview.component.spec.ts | 2 +- .../overview/process-overview.component.ts | 2 +- .../create-profile.component.spec.ts | 2 +- .../create-profile.component.ts | 2 +- .../browse-by/browse-by.component.spec.ts | 2 +- .../collection-dropdown.component.spec.ts | 2 +- .../collection-dropdown.component.ts | 2 +- src/app/shared/log-in/log-in.component.ts | 2 +- .../password/log-in-password.component.ts | 2 +- .../shibboleth/log-in-shibboleth.component.ts | 2 +- .../mocks/remote-data-build.service.mock.ts | 2 +- src/app/shared/mocks/request.service.mock.ts | 2 +- .../item-detail-preview.component.spec.ts | 2 +- .../pagination/pagination.component.spec.ts | 2 +- src/app/shared/pagination/pagination.utils.ts | 2 +- .../eperson-group-list.component.spec.ts | 2 +- .../eperson-group-list.component.ts | 2 +- .../search-form/search-form.component.spec.ts | 2 +- .../search-facet-option.component.spec.ts | 2 +- ...earch-facet-range-option.component.spec.ts | 2 +- ...ch-facet-selected-option.component.spec.ts | 2 +- .../search-label.component.spec.ts | 2 +- .../date/starts-with-date.component.spec.ts | 2 +- .../text/starts-with-text.component.spec.ts | 2 +- .../shared/testing/pagination-service.stub.ts | 2 +- .../testing/submission-rest-service.stub.ts | 2 +- src/app/shared/testing/utils.test.ts | 2 +- .../shared/utils/follow-link-config.model.ts | 2 +- .../vocabulary-treeview.component.ts | 2 +- src/app/statistics/statistics.service.spec.ts | 8 ++-- src/app/statistics/statistics.service.ts | 2 +- ...mport-external-searchbar.component.spec.ts | 2 +- ...ion-import-external-searchbar.component.ts | 2 +- 190 files changed, 403 insertions(+), 322 deletions(-) create mode 100644 src/app/core/core-state.model.ts create mode 100644 src/app/core/data/find-list-options.model.ts create mode 100644 src/app/core/data/request-entry.model.ts create mode 100644 src/app/core/data/request-state.model.ts create mode 100644 src/app/core/data/response-state.model.ts create mode 100644 src/app/core/data/rest-request-with-response-parser.model.ts create mode 100644 src/app/core/data/rest-request.model.ts 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 bcf7e8f1d9..c0d70fd0b2 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 @@ -9,7 +9,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { RemoteData } from '../../core/data/remote-data'; -import { FindListOptions } from '../../core/data/request.models'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { PageInfo } from '../../core/shared/page-info.model'; @@ -27,6 +26,7 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/ 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'; describe('EPeopleRegistryComponent', () => { let component: EPeopleRegistryComponent; 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 832f4f6ce5..163f3d308a 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 @@ -8,7 +8,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model'; import { RemoteData } from '../../../core/data/remote-data'; -import { FindListOptions } from '../../../core/data/request.models'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { EPerson } from '../../../core/eperson/models/eperson.model'; import { PageInfo } from '../../../core/shared/page-info.model'; @@ -28,6 +27,7 @@ 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 { FindListOptions } from '../../../core/data/find-list-options.model'; describe('EPersonFormComponent', () => { let component: EPersonFormComponent; 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 8cfba1d37b..7d3a726eec 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 @@ -25,9 +25,9 @@ import { import { createPaginatedList } from '../../../shared/testing/utils.test'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../core/data/request.models'; 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('BitstreamFormatsComponent', () => { let comp: BitstreamFormatsComponent; 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 cbbcbe07a4..89d8ac29f3 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 @@ -5,7 +5,6 @@ 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 { FindListOptions } from '../../../core/data/request.models'; import { map, switchMap, take } from 'rxjs/operators'; import { hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -13,6 +12,7 @@ import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { NoContent } from '../../../core/shared/NoContent.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; /** * This component renders a list of bitstream formats 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 0253725cb9..af92bbd1ce 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 @@ -21,8 +21,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u 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 { FindListOptions } from '../../../core/data/request.models'; 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 6eb3c5b1a4..8b2abe577c 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,9 +25,9 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u 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 { FindListOptions } from '../../../core/data/request.models'; 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/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts index 7b0ddcb18e..15ec9d78db 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts @@ -20,9 +20,9 @@ import { VarDirective } from '../../shared/utils/var.directive'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../core/data/request.models'; 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('BrowseByDatePageComponent', () => { let comp: BrowseByDatePageComponent; diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts index 584da1c45a..554b059ac5 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts @@ -20,9 +20,9 @@ import { VarDirective } from '../../shared/utils/var.directive'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../core/data/request.models'; 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('BrowseByTitlePageComponent', () => { let comp: BrowseByTitlePageComponent; diff --git a/src/app/community-list-page/community-list-datasource.ts b/src/app/community-list-page/community-list-datasource.ts index f234125373..7f4d9be2b7 100644 --- a/src/app/community-list-page/community-list-datasource.ts +++ b/src/app/community-list-page/community-list-datasource.ts @@ -1,11 +1,11 @@ import { Subscription } from 'rxjs/internal/Subscription'; -import { FindListOptions } from '../core/data/request.models'; import { hasValue } from '../shared/empty.util'; import { CommunityListService} from './community-list-service'; import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { BehaviorSubject, Observable, } from 'rxjs'; import { finalize } from 'rxjs/operators'; import { FlatNode } from './flat-node.model'; +import { FindListOptions } from '../core/data/find-list-options.model'; /** * DataSource object needed by a CDK Tree to render its nodes. diff --git a/src/app/community-list-page/community-list-service.spec.ts b/src/app/community-list-page/community-list-service.spec.ts index f0e6912826..401ffe0b11 100644 --- a/src/app/community-list-page/community-list-service.spec.ts +++ b/src/app/community-list-page/community-list-service.spec.ts @@ -12,9 +12,9 @@ import { CollectionDataService } from '../core/data/collection-data.service'; import { CommunityDataService } from '../core/data/community-data.service'; import { Community } from '../core/shared/community.model'; import { Collection } from '../core/shared/collection.model'; -import { FindListOptions } from '../core/data/request.models'; import { PageInfo } from '../core/shared/page-info.model'; import { FlatNode } from './flat-node.model'; +import { FindListOptions } from '../core/data/find-list-options.model'; describe('CommunityListService', () => { let store: StoreMock; diff --git a/src/app/community-list-page/community-list-service.ts b/src/app/community-list-page/community-list-service.ts index b5b6ffa3f5..d09da8964b 100644 --- a/src/app/community-list-page/community-list-service.ts +++ b/src/app/community-list-page/community-list-service.ts @@ -6,7 +6,6 @@ import { filter, map, switchMap } from 'rxjs/operators'; import { AppState } from '../app.reducer'; import { CommunityDataService } from '../core/data/community-data.service'; -import { FindListOptions } from '../core/data/request.models'; import { Community } from '../core/shared/community.model'; import { Collection } from '../core/shared/collection.model'; import { PageInfo } from '../core/shared/page-info.model'; @@ -22,6 +21,7 @@ import { getFirstCompletedRemoteData, getFirstSucceededRemoteData } from '../cor import { followLink } from '../shared/utils/follow-link-config.model'; import { FlatNode } from './flat-node.model'; import { ShowMoreFlatNode } from './show-more-flat-node.model'; +import { FindListOptions } from '../core/data/find-list-options.model'; // Helper method to combine an flatten an array of observables of flatNode arrays export const combineAndFlatten = (obsList: Observable[]): Observable => diff --git a/src/app/community-list-page/community-list/community-list.component.ts b/src/app/community-list-page/community-list/community-list.component.ts index d92c1c3860..556387da25 100644 --- a/src/app/community-list-page/community-list/community-list.component.ts +++ b/src/app/community-list-page/community-list/community-list.component.ts @@ -1,12 +1,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { take } from 'rxjs/operators'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../core/data/request.models'; import { CommunityListService} from '../community-list-service'; import { CommunityListDatasource } from '../community-list-datasource'; import { FlatTreeControl } from '@angular/cdk/tree'; import { isEmpty } from '../../shared/empty.util'; import { FlatNode } from '../flat-node.model'; +import { FindListOptions } from '../../core/data/find-list-options.model'; /** * A tree-structured list of nodes representing the communities, their subCommunities and collections. 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 93a6c6fbb1..ec61fac613 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 @@ -11,7 +11,6 @@ import { CommunityPageSubCollectionListComponent } from './community-page-sub-co import { Community } from '../../core/shared/community.model'; import { SharedModule } from '../../shared/shared.module'; import { CollectionDataService } from '../../core/data/collection-data.service'; -import { FindListOptions } from '../../core/data/request.models'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { buildPaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; @@ -25,6 +24,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('CommunityPageSubCollectionList Component', () => { let comp: CommunityPageSubCollectionListComponent; 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 e573259b63..2bc829a3b0 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 @@ -13,7 +13,6 @@ import { buildPaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { SharedModule } from '../../shared/shared.module'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { FindListOptions } from '../../core/data/request.models'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { CommunityDataService } from '../../core/data/community-data.service'; @@ -25,6 +24,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('CommunityPageSubCommunityListComponent Component', () => { let comp: CommunityPageSubCommunityListComponent; diff --git a/src/app/core/auth/auth-request.service.ts b/src/app/core/auth/auth-request.service.ts index 00a94822d3..da38d730a5 100644 --- a/src/app/core/auth/auth-request.service.ts +++ b/src/app/core/auth/auth-request.service.ts @@ -3,7 +3,7 @@ import { distinctUntilChanged, filter, map, mergeMap, switchMap, tap } from 'rxj import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RequestService } from '../data/request.service'; import { isNotEmpty } from '../../shared/empty.util'; -import { GetRequest, PostRequest, RestRequest, } from '../data/request.models'; +import { GetRequest, PostRequest, } from '../data/request.models'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { getFirstCompletedRemoteData } from '../shared/operators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; @@ -11,6 +11,7 @@ import { RemoteData } from '../data/remote-data'; import { AuthStatus } from './models/auth-status.model'; import { ShortLivedToken } from './models/short-lived-token.model'; import { URLCombiner } from '../url-combiner/url-combiner'; +import { RestRequest } from '../data/rest-request.model'; /** * Abstract service to send authentication requests diff --git a/src/app/core/auth/authenticated.guard.ts b/src/app/core/auth/authenticated.guard.ts index 0b9eeec509..1ab1d2e0a5 100644 --- a/src/app/core/auth/authenticated.guard.ts +++ b/src/app/core/auth/authenticated.guard.ts @@ -11,9 +11,9 @@ import { Observable } from 'rxjs'; import { map, find, switchMap } from 'rxjs/operators'; import { select, Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { isAuthenticated, isAuthenticationLoading } from './selectors'; import { AuthService, LOGIN_ROUTE } from './auth.service'; +import { CoreState } from '../core-state.model'; /** * Prevent unauthorized activating and loading of routes diff --git a/src/app/core/auth/token-response-parsing.service.ts b/src/app/core/auth/token-response-parsing.service.ts index d39b3cc33d..1ba7a16b14 100644 --- a/src/app/core/auth/token-response-parsing.service.ts +++ b/src/app/core/auth/token-response-parsing.service.ts @@ -1,9 +1,9 @@ import { ResponseParsingService } from '../data/parsing.service'; -import { RestRequest } from '../data/request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { RestResponse, TokenResponse } from '../cache/response.models'; import { isNotEmpty } from '../../shared/empty.util'; import { Injectable } from '@angular/core'; +import { RestRequest } from '../data/rest-request.model'; @Injectable() /** diff --git a/src/app/core/browse/browse-definition-data.service.spec.ts b/src/app/core/browse/browse-definition-data.service.spec.ts index 1127748ca9..92de3e1e29 100644 --- a/src/app/core/browse/browse-definition-data.service.spec.ts +++ b/src/app/core/browse/browse-definition-data.service.spec.ts @@ -1,7 +1,7 @@ import { BrowseDefinitionDataService } from './browse-definition-data.service'; -import { FindListOptions } from '../data/request.models'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { EMPTY } from 'rxjs'; +import { FindListOptions } from '../data/find-list-options.model'; describe(`BrowseDefinitionDataService`, () => { let service: BrowseDefinitionDataService; diff --git a/src/app/core/browse/browse-definition-data.service.ts b/src/app/core/browse/browse-definition-data.service.ts index 31338417ca..1930947f76 100644 --- a/src/app/core/browse/browse-definition-data.service.ts +++ b/src/app/core/browse/browse-definition-data.service.ts @@ -6,7 +6,6 @@ import { BrowseDefinition } from '../shared/browse-definition.model'; import { RequestService } from '../data/request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -15,8 +14,9 @@ import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { Observable } from 'rxjs'; import { RemoteData } from '../data/remote-data'; -import { FindListOptions } from '../data/request.models'; import { PaginatedList } from '../data/paginated-list.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/browse/browse.service.spec.ts b/src/app/core/browse/browse.service.spec.ts index a28add2e30..634bdbff54 100644 --- a/src/app/core/browse/browse.service.spec.ts +++ b/src/app/core/browse/browse.service.spec.ts @@ -5,7 +5,6 @@ import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-bu import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { RequestEntry } from '../data/request.reducer'; import { RequestService } from '../data/request.service'; import { BrowseDefinition } from '../shared/browse-definition.model'; import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; @@ -13,6 +12,7 @@ import { BrowseService } from './browse.service'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList, getFirstUsedArgumentOfSpyMethod } from '../../shared/testing/utils.test'; import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock'; +import { RequestEntry } from '../data/request-entry.model'; describe('BrowseService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/cache/builders/link.service.spec.ts b/src/app/core/cache/builders/link.service.spec.ts index f567c39314..9ec3f9356b 100644 --- a/src/app/core/cache/builders/link.service.spec.ts +++ b/src/app/core/cache/builders/link.service.spec.ts @@ -1,13 +1,13 @@ import { Injectable } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; -import { FindListOptions } from '../../data/request.models'; import { HALLink } from '../../shared/hal-link.model'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; import { LinkService } from './link.service'; import { DATA_SERVICE_FACTORY, LINK_DEFINITION_FACTORY, LINK_DEFINITION_MAP_FACTORY } from './build-decorators'; import { isEmpty } from 'rxjs/operators'; +import { FindListOptions } from '../../data/find-list-options.model'; const TEST_MODEL = new ResourceType('testmodel'); let result: any; diff --git a/src/app/core/cache/builders/remote-data-build.service.spec.ts b/src/app/core/cache/builders/remote-data-build.service.spec.ts index adda461718..1d22da494f 100644 --- a/src/app/core/cache/builders/remote-data-build.service.spec.ts +++ b/src/app/core/cache/builders/remote-data-build.service.spec.ts @@ -13,11 +13,11 @@ import { RequestService } from '../../data/request.service'; import { UnCacheableObject } from '../../shared/uncacheable-object.model'; import { RemoteData } from '../../data/remote-data'; import { Observable, of as observableOf } from 'rxjs'; -import { RequestEntry} from '../../data/request.reducer'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { take } from 'rxjs/operators'; import { HALLink } from '../../shared/hal-link.model'; import { RequestEntryState } from '../../data/request-entry-state.model'; +import { RequestEntry } from '../../data/request-entry.model'; describe('RemoteDataBuildService', () => { let service: RemoteDataBuildService; diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 1ae2ef961e..016f6b16f6 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -11,10 +11,6 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link-config.model'; import { PaginatedList } from '../../data/paginated-list.model'; import { RemoteData } from '../../data/remote-data'; -import { - RequestEntry, - ResponseState -} from '../../data/request.reducer'; import { RequestService } from '../../data/request.service'; import { ObjectCacheService } from '../object-cache.service'; import { LinkService } from './link.service'; @@ -27,6 +23,8 @@ import { getUrlWithoutEmbedParams } from '../../index/index.selectors'; import { getResourceTypeValueFor } from '../object-cache.reducer'; import { hasSucceeded, RequestEntryState } from '../../data/request-entry-state.model'; import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/request.operators'; +import { RequestEntry } from '../../data/request-entry.model'; +import { ResponseState } from '../../data/response-state.model'; @Injectable() export class RemoteDataBuildService { diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts index e9cc7694a7..bde6831967 100644 --- a/src/app/core/cache/object-cache.service.spec.ts +++ b/src/app/core/cache/object-cache.service.spec.ts @@ -7,7 +7,7 @@ import { Operation } from 'fast-json-patch'; import { empty, of as observableOf } from 'rxjs'; import { first } from 'rxjs/operators'; -import { coreReducers, CoreState } from '../core.reducers'; +import { coreReducers} from '../core.reducers'; import { RestRequestMethod } from '../data/rest-request-method'; import { Item } from '../shared/item.model'; import { @@ -24,6 +24,7 @@ import { HALLink } from '../shared/hal-link.model'; import { storeModuleConfig } from '../../app.reducer'; import { TestColdObservable } from 'jasmine-marbles/src/test-observables'; import { IndexName } from '../index/index-name.model'; +import { CoreState } from '../core-state.model'; describe('ObjectCacheService', () => { let service: ObjectCacheService; diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index 8b9660a668..6d48242178 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -5,7 +5,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO import { distinctUntilChanged, filter, map, mergeMap, switchMap, take } from 'rxjs/operators'; import { hasValue, isNotEmpty, isEmpty } from '../../shared/empty.util'; -import { CoreState } from '../core.reducers'; +import { CoreState } from '../core-state.model'; import { coreSelector } from '../core.selectors'; import { RestRequestMethod } from '../data/rest-request-method'; import { diff --git a/src/app/core/cache/server-sync-buffer.effects.ts b/src/app/core/cache/server-sync-buffer.effects.ts index d8ed88e12c..10eedac302 100644 --- a/src/app/core/cache/server-sync-buffer.effects.ts +++ b/src/app/core/cache/server-sync-buffer.effects.ts @@ -8,7 +8,6 @@ import { EmptySSBAction, ServerSyncBufferActionTypes } from './server-sync-buffer.actions'; -import { CoreState } from '../core.reducers'; import { Action, createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { ServerSyncBufferEntry, ServerSyncBufferState } from './server-sync-buffer.reducer'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; @@ -22,6 +21,7 @@ import { environment } from '../../../environments/environment'; import { ObjectCacheEntry } from './object-cache.reducer'; import { Operation } from 'fast-json-patch'; import { NoOpAction } from '../../shared/ngrx/no-op.action'; +import { CoreState } from '../core-state.model'; @Injectable() export class ServerSyncBufferEffects { diff --git a/src/app/core/config/config.service.spec.ts b/src/app/core/config/config.service.spec.ts index 1eca35d223..be354ddc6f 100644 --- a/src/app/core/config/config.service.spec.ts +++ b/src/app/core/config/config.service.spec.ts @@ -3,11 +3,12 @@ import { TestScheduler } from 'rxjs/testing'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { ConfigService } from './config.service'; import { RequestService } from '../data/request.service'; -import { FindListOptions, GetRequest } from '../data/request.models'; +import { GetRequest } from '../data/request.models'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; +import { FindListOptions } from '../data/find-list-options.model'; const LINK_NAME = 'test'; const BROWSE = 'search/findByCollection'; diff --git a/src/app/core/config/config.service.ts b/src/app/core/config/config.service.ts index ddf909b5b0..e52f9482f4 100644 --- a/src/app/core/config/config.service.ts +++ b/src/app/core/config/config.service.ts @@ -6,7 +6,6 @@ import { ConfigObject } from './models/config.model'; import { RemoteData } from '../data/remote-data'; import { DataService } from '../data/data.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; @@ -14,6 +13,7 @@ import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { getFirstCompletedRemoteData } from '../shared/operators'; import { map } from 'rxjs/operators'; +import { CoreState } from '../core-state.model'; class DataServiceImpl extends DataService { constructor( diff --git a/src/app/core/config/submission-forms-config.service.ts b/src/app/core/config/submission-forms-config.service.ts index a5c3f98060..1db5c2fa01 100644 --- a/src/app/core/config/submission-forms-config.service.ts +++ b/src/app/core/config/submission-forms-config.service.ts @@ -5,7 +5,6 @@ import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; @@ -17,6 +16,7 @@ import { SubmissionFormsModel } from './models/config-submission-forms.model'; import { RemoteData } from '../data/remote-data'; import { Observable } from 'rxjs'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CoreState } from '../core-state.model'; @Injectable() @dataService(SUBMISSION_FORMS_TYPE) diff --git a/src/app/core/config/submission-uploads-config.service.ts b/src/app/core/config/submission-uploads-config.service.ts index a9e35a3183..8ad17749bd 100644 --- a/src/app/core/config/submission-uploads-config.service.ts +++ b/src/app/core/config/submission-uploads-config.service.ts @@ -7,7 +7,6 @@ import { dataService } from '../cache/builders/build-decorators'; import { SUBMISSION_UPLOADS_TYPE } from './models/config-type'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; @@ -16,6 +15,7 @@ import { SubmissionUploadsModel } from './models/config-submission-uploads.model import { RemoteData } from '../data/remote-data'; import { Observable } from 'rxjs'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CoreState } from '../core-state.model'; /** * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. diff --git a/src/app/core/core-state.model.ts b/src/app/core/core-state.model.ts new file mode 100644 index 0000000000..b8211fdb55 --- /dev/null +++ b/src/app/core/core-state.model.ts @@ -0,0 +1,30 @@ +import { + BitstreamFormatRegistryState +} from '../admin/admin-registries/bitstream-formats/bitstream-format.reducers'; +import { ObjectCacheState } from './cache/object-cache.reducer'; +import { ServerSyncBufferState } from './cache/server-sync-buffer.reducer'; +import { ObjectUpdatesState } from './data/object-updates/object-updates.reducer'; +import { HistoryState } from './history/history.reducer'; +import { MetaIndexState } from './index/index.reducer'; +import { AuthState } from './auth/auth.reducer'; +import { JsonPatchOperationsState } from './json-patch/json-patch-operations.reducer'; +import { MetaTagState } from './metadata/meta-tag.reducer'; +import { RouteState } from './services/route.reducer'; +import { RequestState } from './data/request-state.model'; + +/** + * The core sub-state in the NgRx store + */ +export interface CoreState { + 'bitstreamFormats': BitstreamFormatRegistryState; + 'cache/object': ObjectCacheState; + 'cache/syncbuffer': ServerSyncBufferState; + 'cache/object-updates': ObjectUpdatesState; + 'data/request': RequestState; + 'history': HistoryState; + 'index': MetaIndexState; + 'auth': AuthState; + 'json/patch': JsonPatchOperationsState; + 'metaTag': MetaTagState; + 'route': RouteState; +} diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 026c87be9d..4ffc22355c 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -42,7 +42,7 @@ import { SubmissionSectionModel } from './config/models/config-submission-sectio import { SubmissionUploadsModel } from './config/models/config-submission-uploads.model'; import { SubmissionFormsConfigService } from './config/submission-forms-config.service'; import { coreEffects } from './core.effects'; -import { coreReducers, CoreState } from './core.reducers'; +import { coreReducers} from './core.reducers'; import { BitstreamFormatDataService } from './data/bitstream-format-data.service'; import { CollectionDataService } from './data/collection-data.service'; import { CommunityDataService } from './data/community-data.service'; @@ -163,6 +163,7 @@ import { RootDataService } from './data/root-data.service'; import { Root } from './data/root.model'; import { SearchConfig } from './shared/search/search-filters/search-config.model'; import { SequenceService } from './shared/sequence.service'; +import { CoreState } from './core-state.model'; /** * When not in production, endpoint responses can be mocked for testing purposes diff --git a/src/app/core/core.reducers.ts b/src/app/core/core.reducers.ts index 8b3ec32b46..c0165c5384 100644 --- a/src/app/core/core.reducers.ts +++ b/src/app/core/core.reducers.ts @@ -1,33 +1,19 @@ import { ActionReducerMap, } from '@ngrx/store'; -import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer'; -import { indexReducer, MetaIndexState } from './index/index.reducer'; -import { requestReducer, RequestState } from './data/request.reducer'; -import { authReducer, AuthState } from './auth/auth.reducer'; -import { jsonPatchOperationsReducer, JsonPatchOperationsState } from './json-patch/json-patch-operations.reducer'; -import { serverSyncBufferReducer, ServerSyncBufferState } from './cache/server-sync-buffer.reducer'; -import { objectUpdatesReducer, ObjectUpdatesState } from './data/object-updates/object-updates.reducer'; -import { routeReducer, RouteState } from './services/route.reducer'; +import { objectCacheReducer } from './cache/object-cache.reducer'; +import { indexReducer } from './index/index.reducer'; +import { requestReducer } from './data/request.reducer'; +import { authReducer } from './auth/auth.reducer'; +import { jsonPatchOperationsReducer } from './json-patch/json-patch-operations.reducer'; +import { serverSyncBufferReducer } from './cache/server-sync-buffer.reducer'; +import { objectUpdatesReducer } from './data/object-updates/object-updates.reducer'; +import { routeReducer } from './services/route.reducer'; import { - bitstreamFormatReducer, - BitstreamFormatRegistryState + bitstreamFormatReducer } from '../admin/admin-registries/bitstream-formats/bitstream-format.reducers'; -import { historyReducer, HistoryState } from './history/history.reducer'; -import { metaTagReducer, MetaTagState } from './metadata/meta-tag.reducer'; - -export interface CoreState { - 'bitstreamFormats': BitstreamFormatRegistryState; - 'cache/object': ObjectCacheState; - 'cache/syncbuffer': ServerSyncBufferState; - 'cache/object-updates': ObjectUpdatesState; - 'data/request': RequestState; - 'history': HistoryState; - 'index': MetaIndexState; - 'auth': AuthState; - 'json/patch': JsonPatchOperationsState; - 'metaTag': MetaTagState; - 'route': RouteState; -} +import { historyReducer } from './history/history.reducer'; +import { metaTagReducer } from './metadata/meta-tag.reducer'; +import { CoreState } from './core-state.model'; export const coreReducers: ActionReducerMap = { 'bitstreamFormats': bitstreamFormatReducer, diff --git a/src/app/core/core.selectors.ts b/src/app/core/core.selectors.ts index 60365be7c2..77c7974de2 100644 --- a/src/app/core/core.selectors.ts +++ b/src/app/core/core.selectors.ts @@ -1,5 +1,5 @@ import { createFeatureSelector } from '@ngrx/store'; -import { CoreState } from './core.reducers'; +import { CoreState } from './core-state.model'; /** * Base selector to select the core state from the store diff --git a/src/app/core/data/base-response-parsing.service.spec.ts b/src/app/core/data/base-response-parsing.service.spec.ts index c8a9683854..1ad584eab8 100644 --- a/src/app/core/data/base-response-parsing.service.spec.ts +++ b/src/app/core/data/base-response-parsing.service.spec.ts @@ -1,8 +1,9 @@ import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { GetRequest, RestRequest } from './request.models'; +import { GetRequest} from './request.models'; import { DSpaceObject } from '../shared/dspace-object.model'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; /* tslint:disable:max-classes-per-file */ class TestService extends BaseResponseParsingService { diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 61945c3161..c8f416b894 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -6,9 +6,9 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { GenericConstructor } from '../shared/generic-constructor'; import { PaginatedList, buildPaginatedList } from './paginated-list.model'; import { getClassForType } from '../cache/builders/build-decorators'; -import { RestRequest } from './request.models'; import { environment } from '../../../environments/environment'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/data/bitstream-data.service.ts b/src/app/core/data/bitstream-data.service.ts index ca5e2761d8..16f2cc16c2 100644 --- a/src/app/core/data/bitstream-data.service.ts +++ b/src/app/core/data/bitstream-data.service.ts @@ -9,7 +9,6 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Bitstream } from '../shared/bitstream.model'; import { BITSTREAM } from '../shared/bitstream.resource-type'; import { Bundle } from '../shared/bundle.model'; @@ -20,7 +19,7 @@ import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { buildPaginatedList, PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { FindListOptions, PutRequest } from './request.models'; +import { PutRequest } from './request.models'; import { RequestService } from './request.service'; import { BitstreamFormatDataService } from './bitstream-format-data.service'; import { BitstreamFormat } from '../shared/bitstream-format.model'; @@ -29,6 +28,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.util import { PageInfo } from '../shared/page-info.model'; import { RequestParam } from '../cache/models/request-param.model'; import { sendRequest } from '../shared/request.operators'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service to retrieve {@link Bitstream}s from the REST API diff --git a/src/app/core/data/bitstream-format-data.service.spec.ts b/src/app/core/data/bitstream-format-data.service.spec.ts index c072803c83..c1ebf90a47 100644 --- a/src/app/core/data/bitstream-format-data.service.spec.ts +++ b/src/app/core/data/bitstream-format-data.service.spec.ts @@ -1,5 +1,4 @@ import { BitstreamFormatDataService } from './bitstream-format-data.service'; -import { RequestEntry } from './request.reducer'; import { RestResponse } from '../cache/response.models'; import { Observable, of as observableOf } from 'rxjs'; import { Action, Store } from '@ngrx/store'; @@ -17,8 +16,9 @@ import { BitstreamFormatsRegistrySelectAction } from '../../admin/admin-registries/bitstream-formats/bitstream-format.actions'; import { TestScheduler } from 'rxjs/testing'; -import { CoreState } from '../core.reducers'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from './request-entry.model'; describe('BitstreamFormatDataService', () => { let service: BitstreamFormatDataService; diff --git a/src/app/core/data/bitstream-format-data.service.ts b/src/app/core/data/bitstream-format-data.service.ts index 1f787f4bbc..1af3db8103 100644 --- a/src/app/core/data/bitstream-format-data.service.ts +++ b/src/app/core/data/bitstream-format-data.service.ts @@ -13,7 +13,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; import { BitstreamFormat } from '../shared/bitstream-format.model'; import { BITSTREAM_FORMAT } from '../shared/bitstream-format.resource-type'; @@ -25,6 +24,7 @@ import { RemoteData } from './remote-data'; import { PostRequest, PutRequest } from './request.models'; import { RequestService } from './request.service'; import { sendRequest } from '../shared/request.operators'; +import { CoreState } from '../core-state.model'; const bitstreamFormatsStateSelector = createSelector( coreSelector, diff --git a/src/app/core/data/bundle-data.service.spec.ts b/src/app/core/data/bundle-data.service.spec.ts index ed149a624f..12eec9e33d 100644 --- a/src/app/core/data/bundle-data.service.spec.ts +++ b/src/app/core/data/bundle-data.service.spec.ts @@ -3,7 +3,6 @@ import { Store } from '@ngrx/store'; import { compare, Operation } from 'fast-json-patch'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { CoreState } from '../core.reducers'; import { Item } from '../shared/item.model'; import { ChangeAnalyzer } from './change-analyzer'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; @@ -13,6 +12,7 @@ import { HALLink } from '../shared/hal-link.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { Bundle } from '../shared/bundle.model'; +import { CoreState } from '../core-state.model'; class DummyChangeAnalyzer implements ChangeAnalyzer { diff(object1: Item, object2: Item): Operation[] { diff --git a/src/app/core/data/bundle-data.service.ts b/src/app/core/data/bundle-data.service.ts index 3aa4efda24..aedf509193 100644 --- a/src/app/core/data/bundle-data.service.ts +++ b/src/app/core/data/bundle-data.service.ts @@ -9,7 +9,6 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Bundle } from '../shared/bundle.model'; import { BUNDLE } from '../shared/bundle.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -18,11 +17,13 @@ import { DataService } from './data.service'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { FindListOptions, GetRequest } from './request.models'; +import { GetRequest } from './request.models'; import { RequestService } from './request.service'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { Bitstream } from '../shared/bitstream.model'; import { RequestEntryState } from './request-entry-state.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service to retrieve {@link Bundle}s from the REST API diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts index e6255144b6..c3a27f87a4 100644 --- a/src/app/core/data/collection-data.service.ts +++ b/src/app/core/data/collection-data.service.ts @@ -13,7 +13,6 @@ import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RequestParam } from '../cache/models/request-param.model'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { Collection } from '../shared/collection.model'; @@ -29,12 +28,13 @@ import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { ContentSourceRequest, - FindListOptions, - UpdateContentSourceRequest, - RestRequest + UpdateContentSourceRequest } from './request.models'; import { RequestService } from './request.service'; import { BitstreamDataService } from './bitstream-data.service'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; @Injectable() @dataService(COLLECTION) diff --git a/src/app/core/data/comcol-data.service.spec.ts b/src/app/core/data/comcol-data.service.spec.ts index 3570487875..49c7d4620f 100644 --- a/src/app/core/data/comcol-data.service.spec.ts +++ b/src/app/core/data/comcol-data.service.spec.ts @@ -7,13 +7,11 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Community } from '../shared/community.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ComColDataService } from './comcol-data.service'; import { CommunityDataService } from './community-data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; -import { FindListOptions } from './request.models'; import { RequestService } from './request.service'; import { createFailedRemoteDataObject$, @@ -22,6 +20,8 @@ import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { BitstreamDataService } from './bitstream-data.service'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; const LINK_NAME = 'test'; diff --git a/src/app/core/data/comcol-data.service.ts b/src/app/core/data/comcol-data.service.ts index cf59c7ac74..01cd18df0c 100644 --- a/src/app/core/data/comcol-data.service.ts +++ b/src/app/core/data/comcol-data.service.ts @@ -5,7 +5,6 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { Community } from '../shared/community.model'; import { HALLink } from '../shared/hal-link.model'; import { DataService } from './data.service'; -import { FindListOptions } from './request.models'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -17,6 +16,7 @@ import { NoContent } from '../shared/NoContent.model'; import { createFailedRemoteDataObject$ } from '../../shared/remote-data.utils'; import { URLCombiner } from '../url-combiner/url-combiner'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { FindListOptions } from './find-list-options.model'; export abstract class ComColDataService extends DataService { protected abstract objectCache: ObjectCacheService; diff --git a/src/app/core/data/community-data.service.ts b/src/app/core/data/community-data.service.ts index 82afe561a9..903d9bc79c 100644 --- a/src/app/core/data/community-data.service.ts +++ b/src/app/core/data/community-data.service.ts @@ -8,7 +8,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Community } from '../shared/community.model'; import { COMMUNITY } from '../shared/community.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -16,11 +15,12 @@ import { ComColDataService } from './comcol-data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { FindListOptions } from './request.models'; import { RequestService } from './request.service'; import { BitstreamDataService } from './bitstream-data.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { isNotEmpty } from '../../shared/empty.util'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; @Injectable() @dataService(COMMUNITY) diff --git a/src/app/core/data/configuration-data.service.ts b/src/app/core/data/configuration-data.service.ts index 91d5af6ecc..711eb6562f 100644 --- a/src/app/core/data/configuration-data.service.ts +++ b/src/app/core/data/configuration-data.service.ts @@ -6,7 +6,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DataService } from './data.service'; import { RemoteData } from './remote-data'; @@ -14,6 +13,7 @@ import { RequestService } from './request.service'; import { ConfigurationProperty } from '../shared/configuration-property.model'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { CONFIG_PROPERTY } from '../shared/config-property.resource-type'; +import { CoreState } from '../core-state.model'; /* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { diff --git a/src/app/core/data/content-source-response-parsing.service.ts b/src/app/core/data/content-source-response-parsing.service.ts index 42b8f85c42..066ccf28c9 100644 --- a/src/app/core/data/content-source-response-parsing.service.ts +++ b/src/app/core/data/content-source-response-parsing.service.ts @@ -4,8 +4,8 @@ import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { ContentSource } from '../shared/content-source.model'; import { MetadataConfig } from '../shared/metadata-config.model'; -import { RestRequest } from './request.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; +import { RestRequest } from './rest-request.model'; @Injectable() /** diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts index 56ef753a79..ee4810d79a 100644 --- a/src/app/core/data/data.service.spec.ts +++ b/src/app/core/data/data.service.spec.ts @@ -7,14 +7,13 @@ import { followLink } from '../../shared/utils/follow-link-config.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { SortDirection, SortOptions } from '../cache/models/sort-options.model'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSpaceObject } from '../shared/dspace-object.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { Item } from '../shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { ChangeAnalyzer } from './change-analyzer'; import { DataService } from './data.service'; -import { FindListOptions, PatchRequest } from './request.models'; +import { PatchRequest } from './request.models'; import { RequestService } from './request.service'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; @@ -23,6 +22,8 @@ import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-bu import { TestScheduler } from 'rxjs/testing'; import { RemoteData } from './remote-data'; import { RequestEntryState } from './request-entry-state.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; const endpoint = 'https://rest.api/core'; diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index 12a66e5092..310ad704ec 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -22,7 +22,6 @@ import { getClassForType } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RequestParam } from '../cache/models/request-param.model'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { DSpaceObject } from '../shared/dspace-object.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -34,7 +33,6 @@ import { RemoteData } from './remote-data'; import { CreateRequest, GetRequest, - FindListOptions, PatchRequest, PutRequest, DeleteRequest @@ -45,6 +43,8 @@ import { UpdateDataService } from './update-data.service'; import { GenericConstructor } from '../shared/generic-constructor'; import { NoContent } from '../shared/NoContent.model'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; export abstract class DataService implements UpdateDataService { protected abstract requestService: RequestService; diff --git a/src/app/core/data/debug-response-parsing.service.ts b/src/app/core/data/debug-response-parsing.service.ts index fbc07cbb39..992a29e4b8 100644 --- a/src/app/core/data/debug-response-parsing.service.ts +++ b/src/app/core/data/debug-response-parsing.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { RestResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ResponseParsingService } from './parsing.service'; -import { RestRequest } from './request.models'; +import { RestRequest } from './rest-request.model'; @Injectable() export class DebugResponseParsingService implements ResponseParsingService { diff --git a/src/app/core/data/dso-redirect-data.service.spec.ts b/src/app/core/data/dso-redirect-data.service.spec.ts index bcd25487c2..3f3a799e45 100644 --- a/src/app/core/data/dso-redirect-data.service.spec.ts +++ b/src/app/core/data/dso-redirect-data.service.spec.ts @@ -6,13 +6,13 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { followLink } from '../../shared/utils/follow-link-config.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DsoRedirectDataService } from './dso-redirect-data.service'; import { GetRequest, IdentifierType } from './request.models'; import { RequestService } from './request.service'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { Item } from '../shared/item.model'; +import { CoreState } from '../core-state.model'; describe('DsoRedirectDataService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/data/dso-redirect-data.service.ts b/src/app/core/data/dso-redirect-data.service.ts index 83395d4719..6270689f03 100644 --- a/src/app/core/data/dso-redirect-data.service.ts +++ b/src/app/core/data/dso-redirect-data.service.ts @@ -9,7 +9,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; @@ -20,6 +19,7 @@ import { getFirstCompletedRemoteData } from '../shared/operators'; import { DSpaceObject } from '../shared/dspace-object.model'; import { Item } from '../shared/item.model'; import { getItemPageRoute } from '../../item-page/item-page-routing-paths'; +import { CoreState } from '../core-state.model'; @Injectable() export class DsoRedirectDataService extends DataService { diff --git a/src/app/core/data/dso-response-parsing.service.ts b/src/app/core/data/dso-response-parsing.service.ts index 7dde1f53a1..fd5a22fae9 100644 --- a/src/app/core/data/dso-response-parsing.service.ts +++ b/src/app/core/data/dso-response-parsing.service.ts @@ -3,12 +3,12 @@ import { Injectable } from '@angular/core'; import { ObjectCacheService } from '../cache/object-cache.service'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { RestResponse, DSOSuccessResponse } from '../cache/response.models'; -import { RestRequest } from './request.models'; import { ResponseParsingService } from './parsing.service'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { hasNoValue, hasValue } from '../../shared/empty.util'; import { DSpaceObject } from '../shared/dspace-object.model'; +import { RestRequest } from './rest-request.model'; @Injectable() export class DSOResponseParsingService extends BaseResponseParsingService implements ResponseParsingService { diff --git a/src/app/core/data/dspace-object-data.service.ts b/src/app/core/data/dspace-object-data.service.ts index eb230e2f54..f78debf9cd 100644 --- a/src/app/core/data/dspace-object-data.service.ts +++ b/src/app/core/data/dspace-object-data.service.ts @@ -7,7 +7,6 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSpaceObject } from '../shared/dspace-object.model'; import { DSPACE_OBJECT } from '../shared/dspace-object.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -15,8 +14,9 @@ import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { RemoteData } from './remote-data'; import { RequestService } from './request.service'; -import { FindListOptions } from './request.models'; import { PaginatedList } from './paginated-list.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { diff --git a/src/app/core/data/dspace-rest-response-parsing.service.ts b/src/app/core/data/dspace-rest-response-parsing.service.ts index 27af802ce8..22506b7d7d 100644 --- a/src/app/core/data/dspace-rest-response-parsing.service.ts +++ b/src/app/core/data/dspace-rest-response-parsing.service.ts @@ -6,7 +6,6 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { GenericConstructor } from '../shared/generic-constructor'; import { PaginatedList, buildPaginatedList } from './paginated-list.model'; import { getClassForType } from '../cache/builders/build-decorators'; -import { RestRequest } from './request.models'; import { environment } from '../../../environments/environment'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceObject } from '../shared/dspace-object.model'; @@ -17,6 +16,7 @@ import { RestRequestMethod } from './rest-request-method'; import { getUrlWithoutEmbedParams, getEmbedSizeParams } from '../index/index.selectors'; import { URLCombiner } from '../url-combiner/url-combiner'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/data/endpoint-map-response-parsing.service.ts b/src/app/core/data/endpoint-map-response-parsing.service.ts index 73396e78bb..728714876c 100644 --- a/src/app/core/data/endpoint-map-response-parsing.service.ts +++ b/src/app/core/data/endpoint-map-response-parsing.service.ts @@ -7,12 +7,12 @@ import { import { hasValue } from '../../shared/empty.util'; import { getClassForType } from '../cache/builders/build-decorators'; import { GenericConstructor } from '../shared/generic-constructor'; -import { RestRequest } from './request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ParsedResponse } from '../cache/response.models'; import { DSpaceObject } from '../shared/dspace-object.model'; import { environment } from '../../../environments/environment'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; /** * ResponseParsingService able to deal with HAL Endpoints that are only needed as steps diff --git a/src/app/core/data/entity-type.service.ts b/src/app/core/data/entity-type.service.ts index ca9ea15bc6..cedad93a14 100644 --- a/src/app/core/data/entity-type.service.ts +++ b/src/app/core/data/entity-type.service.ts @@ -3,7 +3,6 @@ import { DataService } from './data.service'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -18,6 +17,7 @@ import { PaginatedList } from './paginated-list.model'; import { ItemType } from '../shared/item-relationships/item-type.model'; import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../shared/operators'; import { RelationshipTypeService } from './relationship-type.service'; +import { CoreState } from '../core-state.model'; /** * Service handling all ItemType requests diff --git a/src/app/core/data/eperson-registration.service.spec.ts b/src/app/core/data/eperson-registration.service.spec.ts index 768d83c024..6f73813bd4 100644 --- a/src/app/core/data/eperson-registration.service.spec.ts +++ b/src/app/core/data/eperson-registration.service.spec.ts @@ -1,7 +1,6 @@ import { RequestService } from './request.service'; import { EpersonRegistrationService } from './eperson-registration.service'; import { RestResponse } from '../cache/response.models'; -import { RequestEntry } from './request.reducer'; import { cold } from 'jasmine-marbles'; import { PostRequest } from './request.models'; import { Registration } from '../shared/registration.model'; @@ -9,6 +8,7 @@ import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-servic import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { of as observableOf } from 'rxjs/internal/observable/of'; import { TestScheduler } from 'rxjs/testing'; +import { RequestEntry } from './request-entry.model'; describe('EpersonRegistrationService', () => { let testScheduler; diff --git a/src/app/core/data/external-source.service.ts b/src/app/core/data/external-source.service.ts index a3a0a532ec..9c6f75e6b8 100644 --- a/src/app/core/data/external-source.service.ts +++ b/src/app/core/data/external-source.service.ts @@ -4,12 +4,10 @@ import { ExternalSource } from '../shared/external-source.model'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; -import { FindListOptions } from './request.models'; import { Observable } from 'rxjs'; import { distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; @@ -19,6 +17,8 @@ import { PaginatedList } from './paginated-list.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service handling all external source requests diff --git a/src/app/core/data/facet-config-response-parsing.service.ts b/src/app/core/data/facet-config-response-parsing.service.ts index fc543c9072..1fb3848f2d 100644 --- a/src/app/core/data/facet-config-response-parsing.service.ts +++ b/src/app/core/data/facet-config-response-parsing.service.ts @@ -3,9 +3,9 @@ import { SearchFilterConfig } from '../../shared/search/search-filter-config.mod import { ParsedResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { RestRequest } from './request.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; import { FacetConfigResponse } from '../../shared/search/facet-config-response.model'; +import { RestRequest } from './rest-request.model'; @Injectable() export class FacetConfigResponseParsingService extends DspaceRestResponseParsingService { diff --git a/src/app/core/data/facet-value-response-parsing.service.ts b/src/app/core/data/facet-value-response-parsing.service.ts index 6b9e832685..8053e97cf6 100644 --- a/src/app/core/data/facet-value-response-parsing.service.ts +++ b/src/app/core/data/facet-value-response-parsing.service.ts @@ -3,9 +3,9 @@ import { FacetValue } from '../../shared/search/facet-value.model'; import { ParsedResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { RestRequest } from './request.models'; import { FacetValues } from '../../shared/search/facet-values.model'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; +import { RestRequest } from './rest-request.model'; @Injectable() export class FacetValueResponseParsingService extends DspaceRestResponseParsingService { diff --git a/src/app/core/data/feature-authorization/authorization-data.service.spec.ts b/src/app/core/data/feature-authorization/authorization-data.service.spec.ts index 01bd23d7c7..df46d3f0a1 100644 --- a/src/app/core/data/feature-authorization/authorization-data.service.spec.ts +++ b/src/app/core/data/feature-authorization/authorization-data.service.spec.ts @@ -4,7 +4,6 @@ import { AuthService } from '../../auth/auth.service'; import { Site } from '../../shared/site.model'; import { EPerson } from '../../eperson/models/eperson.model'; import { of as observableOf } from 'rxjs'; -import { FindListOptions } from '../request.models'; import { FeatureID } from './feature-id'; import { hasValue } from '../../../shared/empty.util'; import { RequestParam } from '../../cache/models/request-param.model'; @@ -12,6 +11,7 @@ import { Authorization } from '../../shared/authorization.model'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { createPaginatedList } from '../../../shared/testing/utils.test'; import { Feature } from '../../shared/feature.model'; +import { FindListOptions } from '../find-list-options.model'; describe('AuthorizationDataService', () => { let service: AuthorizationDataService; diff --git a/src/app/core/data/feature-authorization/authorization-data.service.ts b/src/app/core/data/feature-authorization/authorization-data.service.ts index b9812cdbb3..1f8c8b2284 100644 --- a/src/app/core/data/feature-authorization/authorization-data.service.ts +++ b/src/app/core/data/feature-authorization/authorization-data.service.ts @@ -7,7 +7,6 @@ import { Authorization } from '../../shared/authorization.model'; import { RequestService } from '../request.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -15,7 +14,6 @@ import { HttpClient } from '@angular/common/http'; import { DSOChangeAnalyzer } from '../dso-change-analyzer.service'; import { AuthService } from '../../auth/auth.service'; import { SiteDataService } from '../site-data.service'; -import { FindListOptions } from '../request.models'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { RemoteData } from '../remote-data'; import { PaginatedList } from '../paginated-list.model'; @@ -26,6 +24,8 @@ import { AuthorizationSearchParams } from './authorization-search-params'; import { addSiteObjectUrlIfEmpty, oneAuthorizationMatchesFeature } from './authorization-utils'; import { FeatureID } from './feature-id'; import { getFirstCompletedRemoteData } from '../../shared/operators'; +import { CoreState } from '../../core-state.model'; +import { FindListOptions } from '../find-list-options.model'; /** * A service to retrieve {@link Authorization}s from the REST API diff --git a/src/app/core/data/feature-authorization/feature-data.service.ts b/src/app/core/data/feature-authorization/feature-data.service.ts index 12be6f8452..cbe8356660 100644 --- a/src/app/core/data/feature-authorization/feature-data.service.ts +++ b/src/app/core/data/feature-authorization/feature-data.service.ts @@ -6,12 +6,12 @@ import { Feature } from '../../shared/feature.model'; import { RequestService } from '../request.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DSOChangeAnalyzer } from '../dso-change-analyzer.service'; +import { CoreState } from '../../core-state.model'; /** * A service to retrieve {@link Feature}s from the REST API diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts index 7a2ff7962d..4f28a16318 100644 --- a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { ResponseParsingService } from './parsing.service'; -import { RestRequest } from './request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { FilteredDiscoveryQueryResponse, RestResponse } from '../cache/response.models'; +import { RestRequest } from './rest-request.model'; /** * A ResponseParsingService used to parse RawRestResponse coming from the REST API to a discovery query (string) diff --git a/src/app/core/data/find-list-options.model.ts b/src/app/core/data/find-list-options.model.ts new file mode 100644 index 0000000000..52a527d9e0 --- /dev/null +++ b/src/app/core/data/find-list-options.model.ts @@ -0,0 +1,14 @@ +import { SortOptions } from '../cache/models/sort-options.model'; +import { RequestParam } from '../cache/models/request-param.model'; + +/** + * The options for a find list request + */ +export class FindListOptions { + scopeID?: string; + elementsPerPage?: number; + currentPage?: number; + sort?: SortOptions; + searchParams?: RequestParam[]; + startsWith?: string; +} diff --git a/src/app/core/data/href-only-data.service.spec.ts b/src/app/core/data/href-only-data.service.spec.ts index dd4be83203..64c451837d 100644 --- a/src/app/core/data/href-only-data.service.spec.ts +++ b/src/app/core/data/href-only-data.service.spec.ts @@ -1,8 +1,8 @@ import { HrefOnlyDataService } from './href-only-data.service'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { FindListOptions } from './request.models'; import { DataService } from './data.service'; +import { FindListOptions } from './find-list-options.model'; describe(`HrefOnlyDataService`, () => { let service: HrefOnlyDataService; diff --git a/src/app/core/data/href-only-data.service.ts b/src/app/core/data/href-only-data.service.ts index 4baffea545..d5656e0eb6 100644 --- a/src/app/core/data/href-only-data.service.ts +++ b/src/app/core/data/href-only-data.service.ts @@ -2,7 +2,6 @@ import { DataService } from './data.service'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -10,7 +9,6 @@ import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { Injectable } from '@angular/core'; import { VOCABULARY_ENTRY } from '../submission/vocabularies/models/vocabularies.resource-type'; -import { FindListOptions } from './request.models'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteData } from './remote-data'; @@ -19,6 +17,8 @@ import { PaginatedList } from './paginated-list.model'; import { ITEM_TYPE } from '../shared/item-relationships/item-type.resource-type'; import { LICENSE } from '../shared/license.resource-type'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { diff --git a/src/app/core/data/item-data.service.spec.ts b/src/app/core/data/item-data.service.spec.ts index 30a132aeae..846d0e72eb 100644 --- a/src/app/core/data/item-data.service.spec.ts +++ b/src/app/core/data/item-data.service.spec.ts @@ -8,13 +8,14 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { BrowseService } from '../browse/browse.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { RestResponse } from '../cache/response.models'; -import { CoreState } from '../core.reducers'; import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { ItemDataService } from './item-data.service'; -import { DeleteRequest, FindListOptions, PostRequest } from './request.models'; -import { RequestEntry } from './request.reducer'; +import { DeleteRequest, PostRequest } from './request.models'; import { RequestService } from './request.service'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from './request-entry.model'; +import { FindListOptions } from './find-list-options.model'; describe('ItemDataService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index 1c152701a1..db7c982303 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -9,7 +9,6 @@ import { BrowseService } from '../browse/browse.service'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { Collection } from '../shared/collection.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model'; @@ -22,7 +21,7 @@ import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { DeleteRequest, FindListOptions, GetRequest, PostRequest, PutRequest, RestRequest } from './request.models'; +import { DeleteRequest, GetRequest, PostRequest, PutRequest} from './request.models'; import { RequestService } from './request.service'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { Bundle } from '../shared/bundle.model'; @@ -34,6 +33,9 @@ import { GenericConstructor } from '../shared/generic-constructor'; import { ResponseParsingService } from './parsing.service'; import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service'; import { sendRequest } from '../shared/request.operators'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; @Injectable() @dataService(ITEM) diff --git a/src/app/core/data/item-template-data.service.spec.ts b/src/app/core/data/item-template-data.service.spec.ts index 1458527506..4b8aa362ba 100644 --- a/src/app/core/data/item-template-data.service.spec.ts +++ b/src/app/core/data/item-template-data.service.spec.ts @@ -1,12 +1,9 @@ import { ItemTemplateDataService } from './item-template-data.service'; -import { RestRequest } from './request.models'; -import { RequestEntry } from './request.reducer'; import { RestResponse } from '../cache/response.models'; import { RequestService } from './request.service'; import { Observable, of as observableOf } from 'rxjs'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { BrowseService } from '../browse/browse.service'; import { cold } from 'jasmine-marbles'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -15,6 +12,9 @@ import { HttpClient } from '@angular/common/http'; import { CollectionDataService } from './collection-data.service'; import { RestRequestMethod } from './rest-request-method'; import { Item } from '../shared/item.model'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from './request-entry.model'; describe('ItemTemplateDataService', () => { let service: ItemTemplateDataService; diff --git a/src/app/core/data/item-template-data.service.ts b/src/app/core/data/item-template-data.service.ts index 19e6941385..c391ce1a72 100644 --- a/src/app/core/data/item-template-data.service.ts +++ b/src/app/core/data/item-template-data.service.ts @@ -9,7 +9,6 @@ import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -23,6 +22,7 @@ import { NoContent } from '../shared/NoContent.model'; import { hasValue } from '../../shared/empty.util'; import { Operation } from 'fast-json-patch'; import { getFirstCompletedRemoteData } from '../shared/operators'; +import { CoreState } from '../core-state.model'; /* tslint:disable:max-classes-per-file */ /** diff --git a/src/app/core/data/metadata-field-data.service.spec.ts b/src/app/core/data/metadata-field-data.service.spec.ts index bb621f74b3..54a174e365 100644 --- a/src/app/core/data/metadata-field-data.service.spec.ts +++ b/src/app/core/data/metadata-field-data.service.spec.ts @@ -4,12 +4,12 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { of as observableOf } from 'rxjs'; import { RestResponse } from '../cache/response.models'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; -import { FindListOptions } from './request.models'; import { MetadataFieldDataService } from './metadata-field-data.service'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { RequestParam } from '../cache/models/request-param.model'; +import { FindListOptions } from './find-list-options.model'; describe('MetadataFieldDataService', () => { let metadataFieldService: MetadataFieldDataService; diff --git a/src/app/core/data/metadata-field-data.service.ts b/src/app/core/data/metadata-field-data.service.ts index 3b11859361..5a78213c84 100644 --- a/src/app/core/data/metadata-field-data.service.ts +++ b/src/app/core/data/metadata-field-data.service.ts @@ -7,7 +7,6 @@ import { RemoteData } from './remote-data'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; @@ -16,11 +15,12 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { METADATA_FIELD } from '../metadata/metadata-field.resource-type'; import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataSchema } from '../metadata/metadata-schema.model'; -import { FindListOptions } from './request.models'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { RequestParam } from '../cache/models/request-param.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service responsible for fetching/sending data from/to the REST API on the metadatafields endpoint diff --git a/src/app/core/data/metadata-schema-data.service.ts b/src/app/core/data/metadata-schema-data.service.ts index ff1796313e..f277f6cab6 100644 --- a/src/app/core/data/metadata-schema-data.service.ts +++ b/src/app/core/data/metadata-schema-data.service.ts @@ -5,7 +5,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { METADATA_SCHEMA } from '../metadata/metadata-schema.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -16,6 +15,7 @@ import { Observable } from 'rxjs'; import { hasValue } from '../../shared/empty.util'; import { tap } from 'rxjs/operators'; import { RemoteData } from './remote-data'; +import { CoreState } from '../core-state.model'; /** * A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint diff --git a/src/app/core/data/mydspace-response-parsing.service.ts b/src/app/core/data/mydspace-response-parsing.service.ts index f71eaeb811..edb9e3c80f 100644 --- a/src/app/core/data/mydspace-response-parsing.service.ts +++ b/src/app/core/data/mydspace-response-parsing.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; import { ParsedResponse } from '../cache/response.models'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { RestRequest } from './request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { hasValue } from '../../shared/empty.util'; import { SearchObjects } from '../../shared/search/search-objects.model'; import { MetadataMap, MetadataValue } from '../shared/metadata.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; +import { RestRequest } from './rest-request.model'; @Injectable() export class MyDSpaceResponseParsingService extends DspaceRestResponseParsingService { diff --git a/src/app/core/data/object-updates/object-updates.service.spec.ts b/src/app/core/data/object-updates/object-updates.service.spec.ts index b0c63851e3..9cf856f03a 100644 --- a/src/app/core/data/object-updates/object-updates.service.spec.ts +++ b/src/app/core/data/object-updates/object-updates.service.spec.ts @@ -1,5 +1,4 @@ import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectUpdatesService } from './object-updates.service'; import { DiscardObjectUpdatesAction, @@ -16,6 +15,7 @@ import { OBJECT_UPDATES_TRASH_PATH } from './object-updates.reducer'; import { Relationship } from '../../shared/item-relationships/relationship.model'; import { Injector } from '@angular/core'; import { FieldChangeType } from './field-change-type.model'; +import { CoreState } from '../../core-state.model'; describe('ObjectUpdatesService', () => { let service: ObjectUpdatesService; diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts index 0bdae06ce2..2fb6d47d31 100644 --- a/src/app/core/data/object-updates/object-updates.service.ts +++ b/src/app/core/data/object-updates/object-updates.service.ts @@ -1,6 +1,5 @@ import { Injectable, Injector } from '@angular/core'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { coreSelector } from '../../core.selectors'; import { FieldState, @@ -35,6 +34,7 @@ import { GenericConstructor } from '../../shared/generic-constructor'; import { Identifiable } from './identifiable.model'; import { FieldUpdates } from './field-updates.model'; import { FieldChangeType } from './field-change-type.model'; +import { CoreState } from '../../core-state.model'; function objectUpdatesStateSelector(): MemoizedSelector { return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']); diff --git a/src/app/core/data/parsing.service.ts b/src/app/core/data/parsing.service.ts index bebbd63fd7..fbebe75b2b 100644 --- a/src/app/core/data/parsing.service.ts +++ b/src/app/core/data/parsing.service.ts @@ -1,6 +1,6 @@ import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; -import { RestRequest } from './request.models'; import { ParsedResponse } from '../cache/response.models'; +import { RestRequest } from './rest-request.model'; export interface ResponseParsingService { parse(request: RestRequest, data: RawRestResponse): ParsedResponse; diff --git a/src/app/core/data/processes/process-data.service.ts b/src/app/core/data/processes/process-data.service.ts index cadcdb3bfe..81b4cbd503 100644 --- a/src/app/core/data/processes/process-data.service.ts +++ b/src/app/core/data/processes/process-data.service.ts @@ -3,7 +3,6 @@ import { DataService } from '../data.service'; import { RequestService } from '../request.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -18,6 +17,7 @@ import { PaginatedList } from '../paginated-list.model'; import { Bitstream } from '../../shared/bitstream.model'; import { RemoteData } from '../remote-data'; import { BitstreamDataService } from '../bitstream-data.service'; +import { CoreState } from '../../core-state.model'; @Injectable() @dataService(PROCESS) diff --git a/src/app/core/data/processes/script-data.service.ts b/src/app/core/data/processes/script-data.service.ts index 69b4270173..bf51fadea1 100644 --- a/src/app/core/data/processes/script-data.service.ts +++ b/src/app/core/data/processes/script-data.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core'; import { DataService } from '../data.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -13,12 +12,14 @@ import { ProcessParameter } from '../../../process-page/processes/process-parame import { map, take } from 'rxjs/operators'; import { URLCombiner } from '../../url-combiner/url-combiner'; import { RemoteData } from '../remote-data'; -import { MultipartPostRequest, RestRequest } from '../request.models'; +import { MultipartPostRequest} from '../request.models'; import { RequestService } from '../request.service'; import { Observable } from 'rxjs'; import { dataService } from '../../cache/builders/build-decorators'; import { SCRIPT } from '../../../process-page/scripts/script.resource-type'; import { Process } from '../../../process-page/processes/process.model'; +import { RestRequest } from '../rest-request.model'; +import { CoreState } from '../../core-state.model'; export const METADATA_IMPORT_SCRIPT_NAME = 'metadata-import'; export const METADATA_EXPORT_SCRIPT_NAME = 'metadata-export'; diff --git a/src/app/core/data/registration-response-parsing.service.ts b/src/app/core/data/registration-response-parsing.service.ts index e11838e0ac..067deea557 100644 --- a/src/app/core/data/registration-response-parsing.service.ts +++ b/src/app/core/data/registration-response-parsing.service.ts @@ -4,8 +4,8 @@ import { } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ResponseParsingService } from './parsing.service'; -import { RestRequest } from './request.models'; import { Registration } from '../shared/registration.model'; +import { RestRequest } from './rest-request.model'; @Injectable({ providedIn: 'root', diff --git a/src/app/core/data/relationship-type.service.ts b/src/app/core/data/relationship-type.service.ts index 4dac044090..07608b8cf9 100644 --- a/src/app/core/data/relationship-type.service.ts +++ b/src/app/core/data/relationship-type.service.ts @@ -10,7 +10,6 @@ import { followLink } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ItemType } from '../shared/item-relationships/item-type.model'; import { RelationshipType } from '../shared/item-relationships/relationship-type.model'; @@ -22,6 +21,7 @@ import { ItemDataService } from './item-data.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { RequestService } from './request.service'; +import { CoreState } from '../core-state.model'; /** * Check if one side of a RelationshipType is the ItemType with the given label diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts index 0f7dd319c3..d27c8dd6b2 100644 --- a/src/app/core/data/relationship.service.spec.ts +++ b/src/app/core/data/relationship.service.spec.ts @@ -6,15 +6,16 @@ import { Relationship } from '../shared/item-relationships/relationship.model'; import { Item } from '../shared/item.model'; import { PageInfo } from '../shared/page-info.model'; import { buildPaginatedList } from './paginated-list.model'; -import { DeleteRequest, FindListOptions } from './request.models'; +import { DeleteRequest} from './request.models'; import { RelationshipService } from './relationship.service'; import { RequestService } from './request.service'; -import { RequestEntry } from './request.reducer'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { getMockRemoteDataBuildServiceHrefMap } from '../../shared/mocks/remote-data-build.service.mock'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { RequestEntry } from './request-entry.model'; +import { FindListOptions } from './find-list-options.model'; describe('RelationshipService', () => { let service: RelationshipService; diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 7bd36d0cec..e9e9931408 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -21,7 +21,6 @@ import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RequestParam } from '../cache/models/request-param.model'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RelationshipType } from '../shared/item-relationships/relationship-type.model'; @@ -39,11 +38,14 @@ import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { ItemDataService } from './item-data.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models'; +import { DeleteRequest, PostRequest} from './request.models'; import { RequestService } from './request.service'; import { NoContent } from '../shared/NoContent.model'; import { RequestEntryState } from './request-entry-state.model'; import { sendRequest } from '../shared/request.operators'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; diff --git a/src/app/core/data/request-entry.model.ts b/src/app/core/data/request-entry.model.ts new file mode 100644 index 0000000000..1555c99ed4 --- /dev/null +++ b/src/app/core/data/request-entry.model.ts @@ -0,0 +1,13 @@ +import { RestRequestWithResponseParser } from './rest-request-with-response-parser.model'; +import { RequestEntryState } from './request-entry-state.model'; +import { ResponseState } from './response-state.model'; + +/** + * An entry for a request in the NgRx store + */ +export class RequestEntry { + request: RestRequestWithResponseParser; + state: RequestEntryState; + response: ResponseState; + lastUpdated: number; +} diff --git a/src/app/core/data/request-state.model.ts b/src/app/core/data/request-state.model.ts new file mode 100644 index 0000000000..5872dab294 --- /dev/null +++ b/src/app/core/data/request-state.model.ts @@ -0,0 +1,8 @@ +import { RequestEntry } from './request-entry.model'; + +/** + * The request sub-state of the NgRx store + */ +export interface RequestState { + [uuid: string]: RequestEntry; +} diff --git a/src/app/core/data/request.actions.ts b/src/app/core/data/request.actions.ts index 8b3511d3e4..cc5415afbe 100644 --- a/src/app/core/data/request.actions.ts +++ b/src/app/core/data/request.actions.ts @@ -1,8 +1,8 @@ import { Action } from '@ngrx/store'; import { type } from '../../shared/ngrx/type'; -import { RestRequest } from './request.models'; import { HALLink } from '../shared/hal-link.model'; import { UnCacheableObject } from '../shared/uncacheable-object.model'; +import { RestRequest } from './rest-request.model'; /** * The list of RequestAction type definitions diff --git a/src/app/core/data/request.effects.ts b/src/app/core/data/request.effects.ts index e5a4fc7df2..b297ca6692 100644 --- a/src/app/core/data/request.effects.ts +++ b/src/app/core/data/request.effects.ts @@ -16,11 +16,12 @@ import { RequestSuccessAction, ResetResponseTimestampsAction } from './request.actions'; -import { RestRequest } from './request.models'; -import { RequestEntry } from './request.reducer'; import { RequestService } from './request.service'; import { ParsedResponse } from '../cache/response.models'; import { RequestError } from './request-error.model'; +import { RestRequest } from './rest-request.model'; +import { RestRequestWithResponseParser } from './rest-request-with-response-parser.model'; +import { RequestEntry } from './request-entry.model'; @Injectable() export class RequestEffects { @@ -34,7 +35,7 @@ export class RequestEffects { }), filter((entry: RequestEntry) => hasValue(entry)), map((entry: RequestEntry) => entry.request), - mergeMap((request: RestRequest) => { + mergeMap((request: RestRequestWithResponseParser) => { let body = request.body; if (isNotEmpty(request.body) && !request.isMultipart) { const serializer = new DSpaceSerializer(getClassForType(request.body.type)); diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index 990c37d192..45b9b4143d 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -1,15 +1,16 @@ -import { SortOptions } from '../cache/models/sort-options.model'; import { GenericConstructor } from '../shared/generic-constructor'; import { ResponseParsingService } from './parsing.service'; import { EndpointMapResponseParsingService } from './endpoint-map-response-parsing.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; -import { SubmissionResponseParsingService } from '../submission/submission-response-parsing.service'; +import { + SubmissionResponseParsingService +} from '../submission/submission-response-parsing.service'; import { RestRequestMethod } from './rest-request-method'; -import { RequestParam } from '../cache/models/request-param.model'; import { TaskResponseParsingService } from '../tasks/task-response-parsing.service'; import { ContentSourceResponseParsingService } from './content-source-response-parsing.service'; +import { RestRequestWithResponseParser } from './rest-request-with-response-parser.model'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; -import { environment } from '../../../environments/environment'; +import { FindListOptions } from './find-list-options.model'; /* tslint:disable:max-classes-per-file */ @@ -19,25 +20,13 @@ export enum IdentifierType { HANDLE = 'handle' } -export abstract class RestRequest { - public responseMsToLive = environment.cache.msToLive.default; - public isMultipart = false; - - constructor( - public uuid: string, - public href: string, - public method: RestRequestMethod = RestRequestMethod.GET, - public body?: any, - public options?: HttpOptions, - ) { - } - +class DSpaceRestRequest extends RestRequestWithResponseParser { getResponseParser(): GenericConstructor { return DspaceRestResponseParsingService; } } -export class GetRequest extends RestRequest { +export class GetRequest extends DSpaceRestRequest { constructor( public uuid: string, public href: string, @@ -48,7 +37,7 @@ export class GetRequest extends RestRequest { } } -export class PostRequest extends RestRequest { +export class PostRequest extends DSpaceRestRequest { constructor( public uuid: string, public href: string, @@ -62,7 +51,7 @@ export class PostRequest extends RestRequest { /** * Request representing a multipart post request */ -export class MultipartPostRequest extends RestRequest { +export class MultipartPostRequest extends DSpaceRestRequest { public isMultipart = true; constructor( public uuid: string, @@ -74,7 +63,7 @@ export class MultipartPostRequest extends RestRequest { } } -export class PutRequest extends RestRequest { +export class PutRequest extends DSpaceRestRequest { constructor( public uuid: string, public href: string, @@ -85,7 +74,7 @@ export class PutRequest extends RestRequest { } } -export class DeleteRequest extends RestRequest { +export class DeleteRequest extends DSpaceRestRequest { constructor( public uuid: string, public href: string, @@ -96,7 +85,7 @@ export class DeleteRequest extends RestRequest { } } -export class OptionsRequest extends RestRequest { +export class OptionsRequest extends DSpaceRestRequest { constructor( public uuid: string, public href: string, @@ -107,7 +96,7 @@ export class OptionsRequest extends RestRequest { } } -export class HeadRequest extends RestRequest { +export class HeadRequest extends DSpaceRestRequest { constructor( public uuid: string, public href: string, @@ -118,7 +107,7 @@ export class HeadRequest extends RestRequest { } } -export class PatchRequest extends RestRequest { +export class PatchRequest extends DSpaceRestRequest { constructor( public uuid: string, public href: string, @@ -129,15 +118,6 @@ export class PatchRequest extends RestRequest { } } -export class FindListOptions { - scopeID?: string; - elementsPerPage?: number; - currentPage?: number; - sort?: SortOptions; - searchParams?: RequestParam[]; - startsWith?: string; -} - export class FindListRequest extends GetRequest { constructor( uuid: string, diff --git a/src/app/core/data/request.reducer.spec.ts b/src/app/core/data/request.reducer.spec.ts index 2073b4faa1..f6f557ad8f 100644 --- a/src/app/core/data/request.reducer.spec.ts +++ b/src/app/core/data/request.reducer.spec.ts @@ -9,8 +9,9 @@ import { ResetResponseTimestampsAction } from './request.actions'; import { GetRequest } from './request.models'; -import { requestReducer, RequestState } from './request.reducer'; +import { requestReducer} from './request.reducer'; import { RequestEntryState } from './request-entry-state.model'; +import { RequestState } from './request-state.model'; class NullAction extends RequestSuccessAction { type = null; diff --git a/src/app/core/data/request.reducer.ts b/src/app/core/data/request.reducer.ts index 8b6f50dda8..5019fc2231 100644 --- a/src/app/core/data/request.reducer.ts +++ b/src/app/core/data/request.reducer.ts @@ -9,31 +9,9 @@ import { RequestSuccessAction, ResetResponseTimestampsAction } from './request.actions'; -import { RestRequest } from './request.models'; -import { HALLink } from '../shared/hal-link.model'; -import { UnCacheableObject } from '../shared/uncacheable-object.model'; import { isNull } from '../../shared/empty.util'; import { hasSucceeded, isStale, RequestEntryState } from './request-entry-state.model'; - -export class ResponseState { - timeCompleted: number; - statusCode: number; - errorMessage?: string; - payloadLink?: HALLink; - unCacheableObject?: UnCacheableObject; -} - -// tslint:disable-next-line:max-classes-per-file -export class RequestEntry { - request: RestRequest; - state: RequestEntryState; - response: ResponseState; - lastUpdated: number; -} - -export interface RequestState { - [uuid: string]: RequestEntry; -} +import { RequestState } from './request-state.model'; // Object.create(null) ensures the object has no default js properties (e.g. `__proto__`) const initialState = Object.create(null); diff --git a/src/app/core/data/request.service.spec.ts b/src/app/core/data/request.service.spec.ts index 50d753ab45..a49761ae5d 100644 --- a/src/app/core/data/request.service.spec.ts +++ b/src/app/core/data/request.service.spec.ts @@ -6,7 +6,7 @@ import { TestScheduler } from 'rxjs/testing'; import { getMockObjectCacheService } from '../../shared/mocks/object-cache.service.mock'; import { defaultUUID, getMockUUIDService } from '../../shared/mocks/uuid.service.mock'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { coreReducers, CoreState } from '../core.reducers'; +import { coreReducers} from '../core.reducers'; import { UUIDService } from '../shared/uuid.service'; import { RequestConfigureAction, RequestExecuteAction } from './request.actions'; import { @@ -16,15 +16,16 @@ import { OptionsRequest, PatchRequest, PostRequest, - PutRequest, - RestRequest + PutRequest } from './request.models'; -import { RequestEntry} from './request.reducer'; import { RequestService } from './request.service'; import { TestBed, waitForAsync } from '@angular/core/testing'; import { storeModuleConfig } from '../../app.reducer'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { RequestEntryState } from './request-entry-state.model'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from './request-entry.model'; describe('RequestService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index b381fdd1fd..3903bcfc99 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -8,7 +8,6 @@ import { cloneDeep } from 'lodash'; import { hasValue, isEmpty, isNotEmpty, hasNoValue } from '../../shared/empty.util'; import { ObjectCacheEntry } from '../cache/object-cache.reducer'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { IndexState, MetaIndexState } from '../index/index.reducer'; import { requestIndexSelector, getUrlWithoutEmbedParams } from '../index/index.selectors'; import { UUIDService } from '../shared/uuid.service'; @@ -17,12 +16,15 @@ import { RequestExecuteAction, RequestStaleAction } from './request.actions'; -import { GetRequest, RestRequest } from './request.models'; -import { RequestEntry, RequestState} from './request.reducer'; +import { GetRequest} from './request.models'; import { CommitSSBAction } from '../cache/server-sync-buffer.actions'; import { RestRequestMethod } from './rest-request-method'; import { coreSelector } from '../core.selectors'; import { isLoading, isStale } from './request-entry-state.model'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { RequestState } from './request-state.model'; +import { RequestEntry } from './request-entry.model'; /** * The base selector function to select the request state in the store @@ -145,7 +147,7 @@ export class RequestService { /** * Check if a GET request is currently pending */ - isPending(request: GetRequest): boolean { + isPending(request: RestRequest): boolean { // If the request is not a GET request, it will never be considered pending, because you may // want to execute the exact same e.g. POST multiple times if (request.method !== RestRequestMethod.GET) { @@ -242,7 +244,7 @@ export class RequestService { if (this.shouldDispatchRequest(request, useCachedVersionIfAvailable)) { this.dispatchRequest(request); if (request.method === RestRequestMethod.GET) { - this.trackRequestsOnTheirWayToTheStore(request); + this.trackRequestsOnTheirWayToTheStore(request as GetRequest); } return true; } else { @@ -315,7 +317,7 @@ export class RequestService { * @param {boolean} useCachedVersionIfAvailable Whether or not to allow the use of a cached version * @returns {boolean} True if the request is cached or still pending */ - public shouldDispatchRequest(request: GetRequest, useCachedVersionIfAvailable: boolean): boolean { + public shouldDispatchRequest(request: RestRequest, useCachedVersionIfAvailable: boolean): boolean { // if it's not a GET request if (request.method !== RestRequestMethod.GET) { return true; diff --git a/src/app/core/data/response-state.model.ts b/src/app/core/data/response-state.model.ts new file mode 100644 index 0000000000..97203fcdd5 --- /dev/null +++ b/src/app/core/data/response-state.model.ts @@ -0,0 +1,13 @@ +import { HALLink } from '../shared/hal-link.model'; +import { UnCacheableObject } from '../shared/uncacheable-object.model'; + +/** + * The response substate in the NgRx store + */ +export class ResponseState { + timeCompleted: number; + statusCode: number; + errorMessage?: string; + payloadLink?: HALLink; + unCacheableObject?: UnCacheableObject; +} diff --git a/src/app/core/data/rest-request-with-response-parser.model.ts b/src/app/core/data/rest-request-with-response-parser.model.ts new file mode 100644 index 0000000000..1c9677ebb1 --- /dev/null +++ b/src/app/core/data/rest-request-with-response-parser.model.ts @@ -0,0 +1,14 @@ +import { RestRequest } from './rest-request.model'; +import { GenericConstructor } from '../shared/generic-constructor'; +import { ResponseParsingService } from './parsing.service'; + +/** + * A RestRequest with a method to retrieve the ResponseParsingService needed for its response + */ +export abstract class RestRequestWithResponseParser extends RestRequest { + + /** + * Get the ResponseParsingService needed to parse the response to this request + */ + abstract getResponseParser(): GenericConstructor; +} diff --git a/src/app/core/data/rest-request.model.ts b/src/app/core/data/rest-request.model.ts new file mode 100644 index 0000000000..05b583639b --- /dev/null +++ b/src/app/core/data/rest-request.model.ts @@ -0,0 +1,20 @@ +import { environment } from '../../../environments/environment'; +import { RestRequestMethod } from './rest-request-method'; +import { HttpOptions } from '../dspace-rest/dspace-rest.service'; + +/** + * A request to the DSpace REST API + */ +export abstract class RestRequest { + public responseMsToLive = environment.cache.msToLive.default; + public isMultipart = false; + + constructor( + public uuid: string, + public href: string, + public method: RestRequestMethod = RestRequestMethod.GET, + public body?: any, + public options?: HttpOptions, + ) { + } +} diff --git a/src/app/core/data/root-data.service.ts b/src/app/core/data/root-data.service.ts index 8b4e836671..b5245c8d48 100644 --- a/src/app/core/data/root-data.service.ts +++ b/src/app/core/data/root-data.service.ts @@ -6,7 +6,6 @@ import { dataService } from '../cache/builders/build-decorators'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -15,8 +14,9 @@ import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { Observable } from 'rxjs'; import { RemoteData } from './remote-data'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; -import { FindListOptions } from './request.models'; import { PaginatedList } from './paginated-list.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts index be2fbe90fc..e5100aba66 100644 --- a/src/app/core/data/search-response-parsing.service.ts +++ b/src/app/core/data/search-response-parsing.service.ts @@ -5,8 +5,8 @@ import { ParsedResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { MetadataMap, MetadataValue } from '../shared/metadata.models'; -import { RestRequest } from './request.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; +import { RestRequest } from './rest-request.model'; @Injectable() export class SearchResponseParsingService extends DspaceRestResponseParsingService { diff --git a/src/app/core/data/site-data.service.spec.ts b/src/app/core/data/site-data.service.spec.ts index c6b6991488..1e8b135db8 100644 --- a/src/app/core/data/site-data.service.spec.ts +++ b/src/app/core/data/site-data.service.spec.ts @@ -5,14 +5,14 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { ObjectCacheService } from '../cache/object-cache.service'; import { Site } from '../shared/site.model'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { FindListOptions } from './request.models'; import { TestScheduler } from 'rxjs/testing'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; describe('SiteDataService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/data/site-data.service.ts b/src/app/core/data/site-data.service.ts index a91b599e58..b2ba9d6dfb 100644 --- a/src/app/core/data/site-data.service.ts +++ b/src/app/core/data/site-data.service.ts @@ -7,7 +7,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { getFirstSucceededRemoteData } from '../shared/operators'; import { Site } from '../shared/site.model'; @@ -17,6 +16,7 @@ import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { RequestService } from './request.service'; +import { CoreState } from '../core-state.model'; /** * Service responsible for handling requests related to the Site object diff --git a/src/app/core/data/status-code-only-response-parsing.service.ts b/src/app/core/data/status-code-only-response-parsing.service.ts index 4f0030371f..ad6a01429d 100644 --- a/src/app/core/data/status-code-only-response-parsing.service.ts +++ b/src/app/core/data/status-code-only-response-parsing.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { RestResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ResponseParsingService } from './parsing.service'; -import { RestRequest } from './request.models'; +import { RestRequest } from './rest-request.model'; /** * A responseparser that will only look at the status code and status diff --git a/src/app/core/data/version-data.service.ts b/src/app/core/data/version-data.service.ts index 11a3838eb0..14dd94c774 100644 --- a/src/app/core/data/version-data.service.ts +++ b/src/app/core/data/version-data.service.ts @@ -4,16 +4,16 @@ import { Version } from '../shared/version.model'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; -import { FindListOptions } from './request.models'; import { Observable } from 'rxjs'; import { dataService } from '../cache/builders/build-decorators'; import { VERSION } from '../shared/version.resource-type'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * Service responsible for handling requests related to the Version object diff --git a/src/app/core/data/version-history-data.service.ts b/src/app/core/data/version-history-data.service.ts index 8f148f168d..42977aa67e 100644 --- a/src/app/core/data/version-history-data.service.ts +++ b/src/app/core/data/version-history-data.service.ts @@ -4,13 +4,11 @@ import { Injectable } from '@angular/core'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; -import { FindListOptions } from './request.models'; import { Observable } from 'rxjs'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { RemoteData } from './remote-data'; @@ -21,6 +19,8 @@ import { dataService } from '../cache/builders/build-decorators'; import { VERSION_HISTORY } from '../shared/version-history.resource-type'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { VersionDataService } from './version-data.service'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * Service responsible for handling requests related to the VersionHistory object diff --git a/src/app/core/data/workflow-action-data.service.ts b/src/app/core/data/workflow-action-data.service.ts index c5a2980d9f..ab8f187b2f 100644 --- a/src/app/core/data/workflow-action-data.service.ts +++ b/src/app/core/data/workflow-action-data.service.ts @@ -3,17 +3,17 @@ import { WorkflowAction } from '../tasks/models/workflow-action-object.model'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; -import { FindListOptions } from './request.models'; import { Observable } from 'rxjs'; import { Injectable } from '@angular/core'; import { dataService } from '../cache/builders/build-decorators'; import { WORKFLOW_ACTION } from '../tasks/models/workflow-action-object.resource-type'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service responsible for fetching/sending data from/to the REST API on the workflowactions endpoint diff --git a/src/app/core/eperson/eperson-data.service.spec.ts b/src/app/core/eperson/eperson-data.service.spec.ts index cd7b664379..2cf849b831 100644 --- a/src/app/core/eperson/eperson-data.service.spec.ts +++ b/src/app/core/eperson/eperson-data.service.spec.ts @@ -12,9 +12,8 @@ import { EPeopleRegistryEditEPersonAction } from '../../access-control/epeople-registry/epeople-registry.actions'; import { RequestParam } from '../cache/models/request-param.model'; -import { CoreState } from '../core.reducers'; import { ChangeAnalyzer } from '../data/change-analyzer'; -import { DeleteRequest, FindListOptions, PatchRequest, PostRequest } from '../data/request.models'; +import { DeleteRequest, PatchRequest, PostRequest } from '../data/request.models'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { Item } from '../shared/item.model'; @@ -27,6 +26,8 @@ import { getMockRemoteDataBuildServiceHrefMap } from '../../shared/mocks/remote- import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; 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'; describe('EPersonDataService', () => { let service: EPersonDataService; diff --git a/src/app/core/eperson/eperson-data.service.ts b/src/app/core/eperson/eperson-data.service.ts index bceb38f163..d801ab6fa4 100644 --- a/src/app/core/eperson/eperson-data.service.ts +++ b/src/app/core/eperson/eperson-data.service.ts @@ -21,7 +21,7 @@ import { DataService } from '../data/data.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { PaginatedList, buildPaginatedList } from '../data/paginated-list.model'; import { RemoteData } from '../data/remote-data'; -import { FindListOptions, PatchRequest, PostRequest, } from '../data/request.models'; +import { PatchRequest, PostRequest, } from '../data/request.models'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { getRemoteDataPayload, getFirstSucceededRemoteData, } from '../shared/operators'; @@ -29,6 +29,7 @@ import { EPerson } from './models/eperson.model'; import { EPERSON } from './models/eperson.resource-type'; import { NoContent } from '../shared/NoContent.model'; import { PageInfo } from '../shared/page-info.model'; +import { FindListOptions } from '../data/find-list-options.model'; const ePeopleRegistryStateSelector = (state: AppState) => state.epeopleRegistry; const editEPersonSelector = createSelector(ePeopleRegistryStateSelector, (ePeopleRegistryState: EPeopleRegistryState) => ePeopleRegistryState.editEPerson); diff --git a/src/app/core/eperson/group-data.service.spec.ts b/src/app/core/eperson/group-data.service.spec.ts index 378c3c9667..c30b74e966 100644 --- a/src/app/core/eperson/group-data.service.spec.ts +++ b/src/app/core/eperson/group-data.service.spec.ts @@ -11,9 +11,8 @@ import { } from '../../access-control/group-registry/group-registry.actions'; import { GroupMock, GroupMock2 } from '../../shared/testing/group-mock'; import { RequestParam } from '../cache/models/request-param.model'; -import { CoreState } from '../core.reducers'; import { ChangeAnalyzer } from '../data/change-analyzer'; -import { DeleteRequest, FindListOptions, PostRequest } from '../data/request.models'; +import { DeleteRequest, PostRequest } from '../data/request.models'; import { RequestService } from '../data/request.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { Item } from '../shared/item.model'; @@ -25,6 +24,8 @@ import { TranslateLoaderMock } from '../../shared/testing/translate-loader.mock' import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { EPersonMock, EPersonMock2 } from '../../shared/testing/eperson.mock'; import { createPaginatedList, createRequestEntry$ } from '../../shared/testing/utils.test'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; describe('GroupDataService', () => { let service: GroupDataService; diff --git a/src/app/core/eperson/group-data.service.ts b/src/app/core/eperson/group-data.service.ts index 5b8f474d1a..bc09c5b973 100644 --- a/src/app/core/eperson/group-data.service.ts +++ b/src/app/core/eperson/group-data.service.ts @@ -19,7 +19,7 @@ import { DataService } from '../data/data.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { PaginatedList } from '../data/paginated-list.model'; import { RemoteData } from '../data/remote-data'; -import { CreateRequest, DeleteRequest, FindListOptions, PostRequest } from '../data/request.models'; +import { CreateRequest, DeleteRequest, PostRequest } from '../data/request.models'; import { RequestService } from '../data/request.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; @@ -33,6 +33,7 @@ import { DSONameService } from '../breadcrumbs/dso-name.service'; import { Community } from '../shared/community.model'; import { Collection } from '../shared/collection.model'; import { NoContent } from '../shared/NoContent.model'; +import { FindListOptions } from '../data/find-list-options.model'; const groupRegistryStateSelector = (state: AppState) => state.groupRegistry; const editGroupSelector = createSelector(groupRegistryStateSelector, (groupRegistryState: GroupRegistryState) => groupRegistryState.editGroup); diff --git a/src/app/core/history/selectors.ts b/src/app/core/history/selectors.ts index 5c77cd65f0..925b56c795 100644 --- a/src/app/core/history/selectors.ts +++ b/src/app/core/history/selectors.ts @@ -1,6 +1,6 @@ -import { CoreState } from '../core.reducers'; import { createSelector } from '@ngrx/store'; import { coreSelector } from '../core.selectors'; +import { CoreState } from '../core-state.model'; export const historySelector = createSelector( coreSelector, diff --git a/src/app/core/index/index.effects.ts b/src/app/core/index/index.effects.ts index 58acc08acc..60e9af6129 100644 --- a/src/app/core/index/index.effects.ts +++ b/src/app/core/index/index.effects.ts @@ -17,9 +17,9 @@ import { hasValue } from '../../shared/empty.util'; import { RestRequestMethod } from '../data/rest-request-method'; import { getUrlWithoutEmbedParams, uuidFromHrefSelector } from './index.selectors'; import { Store, select } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { NoOpAction } from '../../shared/ngrx/no-op.action'; import { IndexName } from './index-name.model'; +import { CoreState } from '../core-state.model'; @Injectable() export class UUIDIndexEffects { diff --git a/src/app/core/index/index.selectors.ts b/src/app/core/index/index.selectors.ts index 435398d0a4..697616c321 100644 --- a/src/app/core/index/index.selectors.ts +++ b/src/app/core/index/index.selectors.ts @@ -1,11 +1,11 @@ import { createSelector, MemoizedSelector } from '@ngrx/store'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; -import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; import { URLCombiner } from '../url-combiner/url-combiner'; import { IndexState, MetaIndexState } from './index.reducer'; import * as parse from 'url-parse'; import { IndexName } from './index-name.model'; +import { CoreState } from '../core-state.model'; /** * Return the given url without `embed` params. diff --git a/src/app/core/json-patch/builder/json-patch-operations-builder.ts b/src/app/core/json-patch/builder/json-patch-operations-builder.ts index d3896c4a6c..f5a584fd3d 100644 --- a/src/app/core/json-patch/builder/json-patch-operations-builder.ts +++ b/src/app/core/json-patch/builder/json-patch-operations-builder.ts @@ -1,5 +1,4 @@ import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { NewPatchAddOperationAction, NewPatchMoveOperationAction, @@ -13,6 +12,7 @@ import { dateToISOFormat, dateToString, isNgbDateStruct } from '../../../shared/ import { VocabularyEntry } from '../../submission/vocabularies/models/vocabulary-entry.model'; import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; import { FormFieldLanguageValueObject } from '../../../shared/form/builder/models/form-field-language-value.model'; +import { CoreState } from '../../core-state.model'; /** * Provides methods to dispatch JsonPatch Operations Actions diff --git a/src/app/core/json-patch/json-patch-operations.service.spec.ts b/src/app/core/json-patch/json-patch-operations.service.spec.ts index d1b2948777..10fc2f2ed8 100644 --- a/src/app/core/json-patch/json-patch-operations.service.spec.ts +++ b/src/app/core/json-patch/json-patch-operations.service.spec.ts @@ -12,7 +12,6 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; import { JsonPatchOperationsService } from './json-patch-operations.service'; import { SubmitDataResponseDefinitionObject } from '../shared/submit-data-response-definition.model'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { JsonPatchOperationsEntry, JsonPatchOperationsResourceEntry } from './json-patch-operations.reducer'; import { @@ -21,9 +20,10 @@ import { RollbacktPatchOperationsAction, StartTransactionPatchOperationsAction } from './json-patch-operations.actions'; -import { RequestEntry } from '../data/request.reducer'; import { createFailedRemoteDataObject, createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { deepClone } from 'fast-json-patch'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from '../data/request-entry.model'; class TestService extends JsonPatchOperationsService { diff --git a/src/app/core/json-patch/json-patch-operations.service.ts b/src/app/core/json-patch/json-patch-operations.service.ts index c3363f4db4..1dfbd404cc 100644 --- a/src/app/core/json-patch/json-patch-operations.service.ts +++ b/src/app/core/json-patch/json-patch-operations.service.ts @@ -6,7 +6,6 @@ import { hasValue, isEmpty, isNotEmpty, isNotUndefined, isUndefined } from '../. import { PatchRequest } from '../data/request.models'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { CoreState } from '../core.reducers'; import { jsonPatchOperationsByResourceType } from './selectors'; import { JsonPatchOperationsResourceEntry } from './json-patch-operations.reducer'; import { @@ -18,6 +17,7 @@ import { JsonPatchOperationModel } from './json-patch.model'; import { getFirstCompletedRemoteData } from '../shared/operators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteData } from '../data/remote-data'; +import { CoreState } from '../core-state.model'; /** * An abstract class that provides methods to make JSON Patch requests. diff --git a/src/app/core/json-patch/selectors.ts b/src/app/core/json-patch/selectors.ts index 1ccde294de..20eb853891 100644 --- a/src/app/core/json-patch/selectors.ts +++ b/src/app/core/json-patch/selectors.ts @@ -1,8 +1,8 @@ import { MemoizedSelector } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; import { JsonPatchOperationsEntry, JsonPatchOperationsResourceEntry } from './json-patch-operations.reducer'; import { keySelector, subStateSelector } from '../../submission/selectors'; +import { CoreState } from '../core-state.model'; /** * Return MemoizedSelector to select all jsonPatchOperations for a specified resource type, stored in the state diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 1c6946b0d3..469a908ab5 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -31,7 +31,7 @@ import { MetaTagState } from './meta-tag.reducer'; import { createSelector, select, Store } from '@ngrx/store'; import { AddMetaTagAction, ClearMetaTagAction } from './meta-tag.actions'; import { coreSelector } from '../core.selectors'; -import { CoreState } from '../core.reducers'; +import { CoreState } from '../core-state.model'; /** * The base selector function to select the metaTag section in the store diff --git a/src/app/core/pagination/pagination.service.spec.ts b/src/app/core/pagination/pagination.service.spec.ts index 18f94cc84c..94b6b48d59 100644 --- a/src/app/core/pagination/pagination.service.spec.ts +++ b/src/app/core/pagination/pagination.service.spec.ts @@ -3,7 +3,7 @@ import { RouterStub } from '../../shared/testing/router.stub'; import { of as observableOf } from 'rxjs'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../cache/models/sort-options.model'; -import { FindListOptions } from '../data/request.models'; +import { FindListOptions } from '../data/find-list-options.model'; describe('PaginationService', () => { diff --git a/src/app/core/pagination/pagination.service.ts b/src/app/core/pagination/pagination.service.ts index dae6991834..bddb565c89 100644 --- a/src/app/core/pagination/pagination.service.ts +++ b/src/app/core/pagination/pagination.service.ts @@ -5,10 +5,10 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { filter, map, take } from 'rxjs/operators'; import { SortDirection, SortOptions } from '../cache/models/sort-options.model'; -import { FindListOptions } from '../data/request.models'; import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { difference } from '../../shared/object.util'; import { isNumeric } from 'rxjs/internal-compatibility'; +import { FindListOptions } from '../data/find-list-options.model'; @Injectable({ diff --git a/src/app/core/registry/registry.service.spec.ts b/src/app/core/registry/registry.service.spec.ts index 199f43e98e..db52a0547e 100644 --- a/src/app/core/registry/registry.service.spec.ts +++ b/src/app/core/registry/registry.service.spec.ts @@ -23,13 +23,13 @@ import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { RegistryService } from './registry.service'; import { storeModuleConfig } from '../../app.reducer'; -import { FindListOptions } from '../data/request.models'; import { MetadataSchemaDataService } from '../data/metadata-schema-data.service'; import { MetadataFieldDataService } from '../data/metadata-field-data.service'; import { createNoContentRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { RemoteData } from '../data/remote-data'; import { NoContent } from '../shared/NoContent.model'; +import { FindListOptions } from '../data/find-list-options.model'; @Component({ template: '' }) class DummyComponent { diff --git a/src/app/core/registry/registry.service.ts b/src/app/core/registry/registry.service.ts index 0046dbdb19..7a377405bd 100644 --- a/src/app/core/registry/registry.service.ts +++ b/src/app/core/registry/registry.service.ts @@ -2,7 +2,6 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { Injectable } from '@angular/core'; import { RemoteData } from '../data/remote-data'; import { PaginatedList } from '../data/paginated-list.model'; -import { FindListOptions } from '../data/request.models'; import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util'; import { getFirstSucceededRemoteDataPayload } from '../shared/operators'; import { createSelector, select, Store } from '@ngrx/store'; @@ -30,6 +29,7 @@ import { MetadataFieldDataService } from '../data/metadata-field-data.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { RequestParam } from '../cache/models/request-param.model'; import { NoContent } from '../shared/NoContent.model'; +import { FindListOptions } from '../data/find-list-options.model'; const metadataRegistryStateSelector = (state: AppState) => state.metadataRegistry; const editMetadataSchemaSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.editSchema); diff --git a/src/app/core/resource-policy/resource-policy.service.spec.ts b/src/app/core/resource-policy/resource-policy.service.spec.ts index 9330def8e8..59316c0098 100644 --- a/src/app/core/resource-policy/resource-policy.service.spec.ts +++ b/src/app/core/resource-policy/resource-policy.service.spec.ts @@ -12,13 +12,13 @@ import { RequestService } from '../data/request.service'; import { ResourcePolicyService } from './resource-policy.service'; import { PolicyType } from './models/policy-type.model'; import { ActionType } from './models/action-type.model'; -import { FindListOptions } from '../data/request.models'; import { RequestParam } from '../cache/models/request-param.model'; import { PageInfo } from '../shared/page-info.model'; import { buildPaginatedList } from '../data/paginated-list.model'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; -import { RequestEntry } from '../data/request.reducer'; import { RestResponse } from '../cache/response.models'; +import { RequestEntry } from '../data/request-entry.model'; +import { FindListOptions } from '../data/find-list-options.model'; describe('ResourcePolicyService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/resource-policy/resource-policy.service.ts b/src/app/core/resource-policy/resource-policy.service.ts index 0f4c6a78f8..eeba5a1011 100644 --- a/src/app/core/resource-policy/resource-policy.service.ts +++ b/src/app/core/resource-policy/resource-policy.service.ts @@ -8,13 +8,11 @@ import { dataService } from '../cache/builders/build-decorators'; import { DataService } from '../data/data.service'; import { RequestService } from '../data/request.service'; -import { FindListOptions } from '../data/request.models'; import { Collection } from '../shared/collection.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ResourcePolicy } from './models/resource-policy.model'; import { RemoteData } from '../data/remote-data'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { RESOURCE_POLICY } from './models/resource-policy.resource-type'; @@ -27,6 +25,8 @@ import { isNotEmpty } from '../../shared/empty.util'; import { map } from 'rxjs/operators'; import { NoContent } from '../shared/NoContent.model'; import { getFirstCompletedRemoteData } from '../shared/operators'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/services/route.service.ts b/src/app/core/services/route.service.ts index 23b7ccec85..b84bb40373 100644 --- a/src/app/core/services/route.service.ts +++ b/src/app/core/services/route.service.ts @@ -7,11 +7,11 @@ import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { isEqual } from 'lodash'; import { AddParameterAction, SetParameterAction, SetParametersAction, SetQueryParameterAction, SetQueryParametersAction } from './route.actions'; -import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; import { hasValue } from '../../shared/empty.util'; import { historySelector } from '../history/selectors'; import { AddUrlToHistoryAction } from '../history/history.actions'; +import { CoreState } from '../core-state.model'; /** * Selector to select all route parameters from the store diff --git a/src/app/core/shared/operators.spec.ts b/src/app/core/shared/operators.spec.ts index eb102065cd..872c087325 100644 --- a/src/app/core/shared/operators.spec.ts +++ b/src/app/core/shared/operators.spec.ts @@ -2,7 +2,6 @@ import { cold, getTestScheduler, hot } from 'jasmine-marbles'; import { TestScheduler } from 'rxjs/testing'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { GetRequest } from '../data/request.models'; -import { RequestEntry } from '../data/request.reducer'; import { RequestService } from '../data/request.service'; import { getAllSucceededRemoteData, @@ -16,6 +15,7 @@ import { } from '../../shared/remote-data.utils'; import { getRequestFromRequestHref, getRequestFromRequestUUID, getResponseFromEntry, sendRequest } from './request.operators'; import { redirectOn4xx } from './authorized.operators'; +import { RequestEntry } from '../data/request-entry.model'; // tslint:disable:no-shadowed-variable diff --git a/src/app/core/shared/request.operators.ts b/src/app/core/shared/request.operators.ts index 3caba824b9..c529259f03 100644 --- a/src/app/core/shared/request.operators.ts +++ b/src/app/core/shared/request.operators.ts @@ -1,9 +1,10 @@ import { RequestService } from '../data/request.service'; import { Observable } from 'rxjs'; -import { RestRequest } from '../data/request.models'; import { filter, map, mergeMap, tap } from 'rxjs/operators'; -import { RequestEntry, ResponseState } from '../data/request.reducer'; import { hasValue, hasValueOperator } from '../../shared/empty.util'; +import { RestRequest } from '../data/rest-request.model'; +import { RequestEntry } from '../data/request-entry.model'; +import { ResponseState } from '../data/response-state.model'; /** * This file contains custom RxJS operators that can be used in multiple places diff --git a/src/app/core/shared/search/search-configuration.service.spec.ts b/src/app/core/shared/search/search-configuration.service.spec.ts index 30be28f65f..0cb9763072 100644 --- a/src/app/core/shared/search/search-configuration.service.spec.ts +++ b/src/app/core/shared/search/search-configuration.service.spec.ts @@ -8,12 +8,12 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; import { HALEndpointServiceStub } from '../../../shared/testing/hal-endpoint-service.stub'; import { getMockRemoteDataBuildService } from '../../../shared/mocks/remote-data-build.service.mock'; -import { RequestEntry } from '../../data/request.reducer'; import { map } from 'rxjs/operators'; import { RemoteData } from '../../data/remote-data'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { SearchObjects } from '../../../shared/search/search-objects.model'; import { getMockRequestService } from '../../../shared/mocks/request.service.mock'; +import { RequestEntry } from '../../data/request-entry.model'; describe('SearchConfigurationService', () => { let service: SearchConfigurationService; diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index d62e5ea01b..0a2afe6183 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -491,7 +491,7 @@ export class SearchConfigurationService implements OnDestroy { let filters: SearchFilterConfig[]; if (isNotEmpty(rd.payload.filters)) { filters = rd.payload.filters - .map((filter: any) => Object.assign(new SearchFilterConfig(), filter)); + .map((f: any) => Object.assign(new SearchFilterConfig(), f)); } else { filters = []; } diff --git a/src/app/core/shared/search/search.service.spec.ts b/src/app/core/shared/search/search.service.spec.ts index d41f6d8d72..c9b6152efd 100644 --- a/src/app/core/shared/search/search.service.spec.ts +++ b/src/app/core/shared/search/search.service.spec.ts @@ -11,7 +11,6 @@ import { HALEndpointService } from '../hal-endpoint.service'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; import { RemoteData } from '../../data/remote-data'; -import { RequestEntry } from '../../data/request.reducer'; import { getMockRequestService } from '../../../shared/mocks/request.service.mock'; import { CommunityDataService } from '../../data/community-data.service'; import { ViewMode } from '../view-mode.model'; @@ -25,9 +24,10 @@ import { SearchObjects } from '../../../shared/search/search-objects.model'; import { PaginationService } from '../../pagination/pagination.service'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; -import { FindListOptions } from '../../data/request.models'; import { SearchConfigurationService } from './search-configuration.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { RequestEntry } from '../../data/request-entry.model'; +import { FindListOptions } from '../../data/find-list-options.model'; @Component({ template: '' }) class DummyComponent { diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 13340874e7..8219cb803c 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -7,7 +7,7 @@ import { LinkService } from '../../cache/builders/link.service'; import { PaginatedList } from '../../data/paginated-list.model'; import { ResponseParsingService } from '../../data/parsing.service'; import { RemoteData } from '../../data/remote-data'; -import { GetRequest, RestRequest } from '../../data/request.models'; +import { GetRequest} from '../../data/request.models'; import { RequestService } from '../../data/request.service'; import { DSpaceObject } from '../dspace-object.model'; import { GenericConstructor } from '../generic-constructor'; @@ -43,11 +43,12 @@ import { SearchConfigurationService } from './search-configuration.service'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { DataService } from '../../data/data.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DSOChangeAnalyzer } from '../../data/dso-change-analyzer.service'; +import { RestRequest } from '../../data/rest-request.model'; +import { CoreState } from '../../core-state.model'; /* tslint:disable:max-classes-per-file */ /** diff --git a/src/app/core/statistics/usage-report-data.service.ts b/src/app/core/statistics/usage-report-data.service.ts index 74385777b1..be23f71c48 100644 --- a/src/app/core/statistics/usage-report-data.service.ts +++ b/src/app/core/statistics/usage-report-data.service.ts @@ -5,7 +5,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DataService } from '../data/data.service'; import { RequestService } from '../data/request.service'; @@ -15,6 +14,7 @@ import { UsageReport } from './models/usage-report.model'; import { Observable } from 'rxjs'; import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../shared/operators'; import { map } from 'rxjs/operators'; +import { CoreState } from '../core-state.model'; /** * A service to retrieve {@link UsageReport}s from the REST API diff --git a/src/app/core/submission/submission-cc-license-data.service.ts b/src/app/core/submission/submission-cc-license-data.service.ts index 5a3fa1ec2b..4c92ad708a 100644 --- a/src/app/core/submission/submission-cc-license-data.service.ts +++ b/src/app/core/submission/submission-cc-license-data.service.ts @@ -5,13 +5,13 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DataService } from '../data/data.service'; import { RequestService } from '../data/request.service'; import { SUBMISSION_CC_LICENSE } from './models/submission-cc-licence.resource-type'; import { SubmissionCcLicence } from './models/submission-cc-license.model'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; +import { CoreState } from '../core-state.model'; @Injectable() @dataService(SUBMISSION_CC_LICENSE) diff --git a/src/app/core/submission/submission-cc-license-url-data.service.ts b/src/app/core/submission/submission-cc-license-url-data.service.ts index 0ca3853d0e..1e72f79193 100644 --- a/src/app/core/submission/submission-cc-license-url-data.service.ts +++ b/src/app/core/submission/submission-cc-license-url-data.service.ts @@ -5,7 +5,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DataService } from '../data/data.service'; import { RequestService } from '../data/request.service'; @@ -17,6 +16,7 @@ import { Observable } from 'rxjs'; import { filter, map, switchMap } from 'rxjs/operators'; import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../shared/operators'; import { isNotEmpty } from '../../shared/empty.util'; +import { CoreState } from '../core-state.model'; @Injectable() @dataService(SUBMISSION_CC_LICENSE_URL) diff --git a/src/app/core/submission/submission-json-patch-operations.service.spec.ts b/src/app/core/submission/submission-json-patch-operations.service.spec.ts index 72c807ffa1..5472e86856 100644 --- a/src/app/core/submission/submission-json-patch-operations.service.spec.ts +++ b/src/app/core/submission/submission-json-patch-operations.service.spec.ts @@ -3,12 +3,12 @@ import { Store } from '@ngrx/store'; import { getTestScheduler } from 'jasmine-marbles'; import { TestScheduler } from 'rxjs/testing'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { SubmissionJsonPatchOperationsService } from './submission-json-patch-operations.service'; import { RequestService } from '../data/request.service'; import { SubmissionPatchRequest } from '../data/request.models'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { CoreState } from '../core-state.model'; describe('SubmissionJsonPatchOperationsService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/submission/submission-json-patch-operations.service.ts b/src/app/core/submission/submission-json-patch-operations.service.ts index cd6308f7f3..5771c85b57 100644 --- a/src/app/core/submission/submission-json-patch-operations.service.ts +++ b/src/app/core/submission/submission-json-patch-operations.service.ts @@ -7,8 +7,8 @@ import { HALEndpointService } from '../shared/hal-endpoint.service'; import { JsonPatchOperationsService } from '../json-patch/json-patch-operations.service'; import { SubmitDataResponseDefinitionObject } from '../shared/submit-data-response-definition.model'; import { SubmissionPatchRequest } from '../data/request.models'; -import { CoreState } from '../core.reducers'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { CoreState } from '../core-state.model'; /** * A service that provides methods to make JSON Patch requests. diff --git a/src/app/core/submission/submission-response-parsing.service.ts b/src/app/core/submission/submission-response-parsing.service.ts index 3b028c67cc..ff84776235 100644 --- a/src/app/core/submission/submission-response-parsing.service.ts +++ b/src/app/core/submission/submission-response-parsing.service.ts @@ -3,7 +3,6 @@ import { deepClone } from 'fast-json-patch'; import { DSOResponseParsingService } from '../data/dso-response-parsing.service'; import { ResponseParsingService } from '../data/parsing.service'; -import { RestRequest } from '../data/request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ParsedResponse } from '../cache/response.models'; import { isEmpty, isNotEmpty, isNotNull } from '../../shared/empty.util'; @@ -14,6 +13,7 @@ import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/f import { SubmissionObject } from './models/submission-object.model'; import { WorkflowItem } from './models/workflowitem.model'; import { WorkspaceItem } from './models/workspaceitem.model'; +import { RestRequest } from '../data/rest-request.model'; /** * Export a function to check if object has same properties of FormFieldMetadataValueObject diff --git a/src/app/core/submission/submission-rest.service.ts b/src/app/core/submission/submission-rest.service.ts index 000f8396f8..cf4b741af2 100644 --- a/src/app/core/submission/submission-rest.service.ts +++ b/src/app/core/submission/submission-rest.service.ts @@ -8,7 +8,6 @@ import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { DeleteRequest, PostRequest, - RestRequest, SubmissionDeleteRequest, SubmissionPatchRequest, SubmissionPostRequest, @@ -22,6 +21,7 @@ import { getFirstCompletedRemoteData } from '../shared/operators'; import { URLCombiner } from '../url-combiner/url-combiner'; import { RemoteData } from '../data/remote-data'; import { SubmissionResponse } from './submission-response.model'; +import { RestRequest } from '../data/rest-request.model'; /** * The service handling all submission REST requests diff --git a/src/app/core/submission/vocabularies/models/vocabulary-find-options.model.ts b/src/app/core/submission/vocabularies/models/vocabulary-find-options.model.ts index 977228ea9d..7feacd591b 100644 --- a/src/app/core/submission/vocabularies/models/vocabulary-find-options.model.ts +++ b/src/app/core/submission/vocabularies/models/vocabulary-find-options.model.ts @@ -1,7 +1,7 @@ import { SortOptions } from '../../../cache/models/sort-options.model'; -import { FindListOptions } from '../../../data/request.models'; import { RequestParam } from '../../../cache/models/request-param.model'; import { isNotEmpty } from '../../../../shared/empty.util'; +import { FindListOptions } from '../../../data/find-list-options.model'; /** * Representing properties used to build a vocabulary find request diff --git a/src/app/core/submission/vocabularies/vocabulary.service.spec.ts b/src/app/core/submission/vocabularies/vocabulary.service.spec.ts index 9f8bec307f..8f733e0149 100644 --- a/src/app/core/submission/vocabularies/vocabulary.service.spec.ts +++ b/src/app/core/submission/vocabularies/vocabulary.service.spec.ts @@ -16,7 +16,6 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; -import { RequestEntry } from '../../data/request.reducer'; import { RestResponse } from '../../cache/response.models'; import { VocabularyService } from './vocabulary.service'; import { getMockRequestService } from '../../../shared/mocks/request.service.mock'; @@ -26,6 +25,7 @@ import { VocabularyFindOptions } from './models/vocabulary-find-options.model'; import { HrefOnlyDataService } from '../../data/href-only-data.service'; import { getMockHrefOnlyDataService } from '../../../shared/mocks/href-only-data.service.mock'; import { createPaginatedList } from '../../../shared/testing/utils.test'; +import { RequestEntry } from '../../data/request-entry.model'; describe('VocabularyService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/submission/vocabularies/vocabulary.service.ts b/src/app/core/submission/vocabularies/vocabulary.service.ts index da58512441..2e7f1911ca 100644 --- a/src/app/core/submission/vocabularies/vocabulary.service.ts +++ b/src/app/core/submission/vocabularies/vocabulary.service.ts @@ -9,11 +9,9 @@ import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link- import { dataService } from '../../cache/builders/build-decorators'; import { DataService } from '../../data/data.service'; import { RequestService } from '../../data/request.service'; -import { FindListOptions } from '../../data/request.models'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { RemoteData } from '../../data/remote-data'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { ChangeAnalyzer } from '../../data/change-analyzer'; @@ -33,6 +31,8 @@ import { RequestParam } from '../../cache/models/request-param.model'; import { VocabularyOptions } from './models/vocabulary-options.model'; import { PageInfo } from '../../shared/page-info.model'; import { HrefOnlyDataService } from '../../data/href-only-data.service'; +import { CoreState } from '../../core-state.model'; +import { FindListOptions } from '../../data/find-list-options.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/submission/workflowitem-data.service.ts b/src/app/core/submission/workflowitem-data.service.ts index 099cfa8627..c97426e681 100644 --- a/src/app/core/submission/workflowitem-data.service.ts +++ b/src/app/core/submission/workflowitem-data.service.ts @@ -4,7 +4,6 @@ import { HttpClient } from '@angular/common/http'; import { Store } from '@ngrx/store'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { CoreState } from '../core.reducers'; import { DataService } from '../data/data.service'; import { RequestService } from '../data/request.service'; import { WorkflowItem } from './models/workflowitem.model'; @@ -19,6 +18,7 @@ import { hasValue } from '../../shared/empty.util'; import { RemoteData } from '../data/remote-data'; import { NoContent } from '../shared/NoContent.model'; import { getFirstCompletedRemoteData } from '../shared/operators'; +import { CoreState } from '../core-state.model'; /** * A service that provides methods to make REST requests with workflow items endpoint. diff --git a/src/app/core/submission/workspaceitem-data.service.ts b/src/app/core/submission/workspaceitem-data.service.ts index 2fc95bdd00..f4a4321b01 100644 --- a/src/app/core/submission/workspaceitem-data.service.ts +++ b/src/app/core/submission/workspaceitem-data.service.ts @@ -4,7 +4,6 @@ import { HttpClient } from '@angular/common/http'; import { Store } from '@ngrx/store'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { CoreState } from '../core.reducers'; import { DataService } from '../data/data.service'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -12,6 +11,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { ObjectCacheService } from '../cache/object-cache.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { WorkspaceItem } from './models/workspaceitem.model'; +import { CoreState } from '../core-state.model'; /** * A service that provides methods to make REST requests with workspaceitems endpoint. diff --git a/src/app/core/tasks/claimed-task-data.service.spec.ts b/src/app/core/tasks/claimed-task-data.service.spec.ts index ab9727592e..722324d94c 100644 --- a/src/app/core/tasks/claimed-task-data.service.spec.ts +++ b/src/app/core/tasks/claimed-task-data.service.spec.ts @@ -6,15 +6,15 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { CoreState } from '../core.reducers'; import { ClaimedTaskDataService } from './claimed-task-data.service'; import { of as observableOf } from 'rxjs/internal/observable/of'; -import { FindListOptions } from '../data/request.models'; import { RequestParam } from '../cache/models/request-param.model'; import { getTestScheduler } from 'jasmine-marbles'; import { TestScheduler } from 'rxjs/testing'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; describe('ClaimedTaskDataService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/tasks/claimed-task-data.service.ts b/src/app/core/tasks/claimed-task-data.service.ts index 9cfd5a44d6..3860f84870 100644 --- a/src/app/core/tasks/claimed-task-data.service.ts +++ b/src/app/core/tasks/claimed-task-data.service.ts @@ -7,7 +7,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -16,10 +15,11 @@ import { CLAIMED_TASK } from './models/claimed-task-object.resource-type'; import { ProcessTaskResponse } from './models/process-task-response'; import { TasksService } from './tasks.service'; import { RemoteData } from '../data/remote-data'; -import { FindListOptions } from '../data/request.models'; import { RequestParam } from '../cache/models/request-param.model'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { getFirstSucceededRemoteData } from '../shared/operators'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; /** * The service handling all REST requests for ClaimedTask diff --git a/src/app/core/tasks/pool-task-data.service.spec.ts b/src/app/core/tasks/pool-task-data.service.spec.ts index 7279c96e5c..d06076009e 100644 --- a/src/app/core/tasks/pool-task-data.service.spec.ts +++ b/src/app/core/tasks/pool-task-data.service.spec.ts @@ -6,15 +6,15 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { CoreState } from '../core.reducers'; import { PoolTaskDataService } from './pool-task-data.service'; import { getTestScheduler } from 'jasmine-marbles'; import { TestScheduler } from 'rxjs/testing'; import { of as observableOf } from 'rxjs/internal/observable/of'; -import { FindListOptions } from '../data/request.models'; import { RequestParam } from '../cache/models/request-param.model'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; describe('PoolTaskDataService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/tasks/pool-task-data.service.ts b/src/app/core/tasks/pool-task-data.service.ts index d44e402e7f..042c9a3455 100644 --- a/src/app/core/tasks/pool-task-data.service.ts +++ b/src/app/core/tasks/pool-task-data.service.ts @@ -7,7 +7,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -15,9 +14,10 @@ import { PoolTask } from './models/pool-task-object.model'; import { POOL_TASK } from './models/pool-task-object.resource-type'; import { TasksService } from './tasks.service'; import { RemoteData } from '../data/remote-data'; -import { FindListOptions } from '../data/request.models'; import { RequestParam } from '../cache/models/request-param.model'; import { getFirstCompletedRemoteData } from '../shared/operators'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; /** * The service handling all REST requests for PoolTask diff --git a/src/app/core/tasks/task-response-parsing.service.ts b/src/app/core/tasks/task-response-parsing.service.ts index 75c4056718..e2e009386f 100644 --- a/src/app/core/tasks/task-response-parsing.service.ts +++ b/src/app/core/tasks/task-response-parsing.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; import { ResponseParsingService } from '../data/parsing.service'; -import { RestRequest } from '../data/request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { BaseResponseParsingService } from '../data/base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { ParsedResponse } from '../cache/response.models'; +import { RestRequest } from '../data/rest-request.model'; /** * Provides methods to parse response for a task request. diff --git a/src/app/core/tasks/tasks.service.spec.ts b/src/app/core/tasks/tasks.service.spec.ts index f0c86d2abf..aa267b9076 100644 --- a/src/app/core/tasks/tasks.service.spec.ts +++ b/src/app/core/tasks/tasks.service.spec.ts @@ -4,13 +4,12 @@ import { TestScheduler } from 'rxjs/testing'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { TasksService } from './tasks.service'; import { RequestService } from '../data/request.service'; -import { FindListOptions, TaskDeleteRequest, TaskPostRequest } from '../data/request.models'; +import { TaskDeleteRequest, TaskPostRequest } from '../data/request.models'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { TaskObject } from './models/task-object.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient, HttpHeaders } from '@angular/common/http'; @@ -22,6 +21,8 @@ import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { of } from 'rxjs'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; const LINK_NAME = 'test'; diff --git a/src/app/core/tasks/tasks.service.ts b/src/app/core/tasks/tasks.service.ts index d96e3320f2..2b76412a9c 100644 --- a/src/app/core/tasks/tasks.service.ts +++ b/src/app/core/tasks/tasks.service.ts @@ -6,7 +6,6 @@ import { distinctUntilChanged, filter, find, map, mergeMap, tap } from 'rxjs/ope import { DataService } from '../data/data.service'; import { DeleteRequest, - FindListOptions, PostRequest, TaskDeleteRequest, TaskPostRequest @@ -18,6 +17,7 @@ import { getAllCompletedRemoteData, getFirstCompletedRemoteData } from '../share import { RemoteData } from '../data/remote-data'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { FindListOptions } from '../data/find-list-options.model'; /** * An abstract class that provides methods to handle task requests. diff --git a/src/app/forgot-password/forgot-password-form/forgot-password-form.component.spec.ts b/src/app/forgot-password/forgot-password-form/forgot-password-form.component.spec.ts index 30048acc6d..55abd5edd8 100644 --- a/src/app/forgot-password/forgot-password-form/forgot-password-form.component.spec.ts +++ b/src/app/forgot-password/forgot-password-form/forgot-password-form.component.spec.ts @@ -11,12 +11,12 @@ import { Store } from '@ngrx/store'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { CoreState } from '../../core/core.reducers'; import { Registration } from '../../core/shared/registration.model'; import { ForgotPasswordFormComponent } from './forgot-password-form.component'; import { By } from '@angular/platform-browser'; import { AuthenticateAction } from '../../core/auth/auth.actions'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { CoreState } from '../../core/core-state.model'; describe('ForgotPasswordFormComponent', () => { let comp: ForgotPasswordFormComponent; diff --git a/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts b/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts index 707c70f19c..e6e5e5cb06 100644 --- a/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts +++ b/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts @@ -8,10 +8,10 @@ import { map } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; import { AuthenticateAction } from '../../core/auth/auth.actions'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core/core.reducers'; import { RemoteData } from '../../core/data/remote-data'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { CoreState } from '../../core/core-state.model'; @Component({ selector: 'ds-forgot-password-form', 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 00408e4696..eb52ca9243 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 @@ -13,7 +13,6 @@ import { buildPaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { SharedModule } from '../../shared/shared.module'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { FindListOptions } from '../../core/data/request.models'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { CommunityDataService } from '../../core/data/community-data.service'; @@ -25,6 +24,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('TopLevelCommunityList Component', () => { let comp: TopLevelCommunityListComponent; 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 807d9f8357..133b13cb27 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 @@ -19,8 +19,8 @@ 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 { FindListOptions } from '../../../../../core/data/request.models'; 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 9b225632df..396e6c3216 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 @@ -18,9 +18,9 @@ import { NotificationsService } from '../../../../shared/notifications/notificat 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 { FindListOptions } from '../../../../core/data/request.models'; 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/related-items/related-items-component.ts b/src/app/item-page/simple/related-items/related-items-component.ts index f752d3589e..e0d65b9a6d 100644 --- a/src/app/item-page/simple/related-items/related-items-component.ts +++ b/src/app/item-page/simple/related-items/related-items-component.ts @@ -3,10 +3,10 @@ import { Item } from '../../../core/shared/item.model'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list.model'; -import { FindListOptions } from '../../../core/data/request.models'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { RelationshipService } from '../../../core/data/relationship.service'; import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; @Component({ selector: 'ds-related-items', 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 53646a27d8..c33d27ec77 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 @@ -15,13 +15,13 @@ import { CollectionDropdownComponent } from '../../shared/collection-dropdown/co import { Collection } from '../../core/shared/collection.model'; import { RemoteData } from '../../core/data/remote-data'; import { Community } from '../../core/shared/community.model'; -import { FindListOptions } from '../../core/data/request.models'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { CollectionDataService } from '../../core/data/collection-data.service'; import { MockElementRef } from '../../shared/testing/element-ref.mock'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('CollectionSelectorComponent', () => { diff --git a/src/app/my-dspace-page/my-dspace-page.component.spec.ts b/src/app/my-dspace-page/my-dspace-page.component.spec.ts index a7f16d4184..d2d742f940 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.spec.ts +++ b/src/app/my-dspace-page/my-dspace-page.component.spec.ts @@ -139,7 +139,7 @@ describe('MyDSpacePageComponent', () => { fixture.detectChanges(); searchServiceObject = (comp as any).service; searchConfigurationServiceObject = (comp as any).searchConfigService; - console.log(searchConfigurationServiceObject) + console.log(searchConfigurationServiceObject); }); afterEach(() => { 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 98e78f6b36..4cbd7e3453 100644 --- a/src/app/process-page/overview/process-overview.component.spec.ts +++ b/src/app/process-page/overview/process-overview.component.spec.ts @@ -16,8 +16,8 @@ import { of as observableOf } from 'rxjs'; 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 { FindListOptions } from '../../core/data/request.models'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('ProcessOverviewComponent', () => { let component: ProcessOverviewComponent; diff --git a/src/app/process-page/overview/process-overview.component.ts b/src/app/process-page/overview/process-overview.component.ts index 03fcf27222..7afcd9cb76 100644 --- a/src/app/process-page/overview/process-overview.component.ts +++ b/src/app/process-page/overview/process-overview.component.ts @@ -4,13 +4,13 @@ import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { Process } from '../processes/process.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { FindListOptions } from '../../core/data/request.models'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { map, switchMap } from 'rxjs/operators'; import { ProcessDataService } from '../../core/data/processes/process-data.service'; import { PaginationService } from '../../core/pagination/pagination.service'; +import { FindListOptions } from '../../core/data/find-list-options.model'; @Component({ selector: 'ds-process-overview', diff --git a/src/app/register-page/create-profile/create-profile.component.spec.ts b/src/app/register-page/create-profile/create-profile.component.spec.ts index 6371044229..06b0499542 100644 --- a/src/app/register-page/create-profile/create-profile.component.spec.ts +++ b/src/app/register-page/create-profile/create-profile.component.spec.ts @@ -12,7 +12,6 @@ import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { of as observableOf } from 'rxjs'; import { By } from '@angular/platform-browser'; -import { CoreState } from '../../core/core.reducers'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { AuthenticateAction } from '../../core/auth/auth.actions'; import { RouterStub } from '../../shared/testing/router.stub'; @@ -22,6 +21,7 @@ import { EndUserAgreementService } from '../../core/end-user-agreement/end-user-agreement.service'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { CoreState } from '../../core/core-state.model'; describe('CreateProfileComponent', () => { let comp: CreateProfileComponent; diff --git a/src/app/register-page/create-profile/create-profile.component.ts b/src/app/register-page/create-profile/create-profile.component.ts index 790e1d6fc5..8c38e3a546 100644 --- a/src/app/register-page/create-profile/create-profile.component.ts +++ b/src/app/register-page/create-profile/create-profile.component.ts @@ -9,7 +9,6 @@ import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { LangConfig } from '../../../config/lang-config.interface'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core/core.reducers'; import { AuthenticateAction } from '../../core/auth/auth.actions'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { environment } from '../../../environments/environment'; @@ -20,6 +19,7 @@ import { EndUserAgreementService } from '../../core/end-user-agreement/end-user-agreement.service'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { CoreState } from '../../core/core-state.model'; /** * Component that renders the create profile page to be used by a user registering through a token diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index 806f4bdb6f..e266c61378 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -18,9 +18,9 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { storeModuleConfig } from '../../app.reducer'; -import { FindListOptions } from '../../core/data/request.models'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('BrowseByComponent', () => { let comp: BrowseByComponent; diff --git a/src/app/shared/collection-dropdown/collection-dropdown.component.spec.ts b/src/app/shared/collection-dropdown/collection-dropdown.component.spec.ts index f08df65ca4..ce90d5d6a7 100644 --- a/src/app/shared/collection-dropdown/collection-dropdown.component.spec.ts +++ b/src/app/shared/collection-dropdown/collection-dropdown.component.spec.ts @@ -18,8 +18,8 @@ import { TranslateLoaderMock } from '../mocks/translate-loader.mock'; import { Community } from '../../core/shared/community.model'; import { MockElementRef } from '../testing/element-ref.mock'; import { FollowLinkConfig } from '../utils/follow-link-config.model'; -import { FindListOptions } from '../../core/data/request.models'; import { Observable } from 'rxjs/internal/Observable'; +import { FindListOptions } from '../../core/data/find-list-options.model'; const community: Community = Object.assign(new Community(), { id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88', diff --git a/src/app/shared/collection-dropdown/collection-dropdown.component.ts b/src/app/shared/collection-dropdown/collection-dropdown.component.ts index c91ddbdb0a..4a5fc705a8 100644 --- a/src/app/shared/collection-dropdown/collection-dropdown.component.ts +++ b/src/app/shared/collection-dropdown/collection-dropdown.component.ts @@ -15,7 +15,6 @@ import { debounceTime, distinctUntilChanged, map, mergeMap, reduce, startWith, s import { hasValue } from '../empty.util'; import { RemoteData } from '../../core/data/remote-data'; -import { FindListOptions } from '../../core/data/request.models'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { Community } from '../../core/shared/community.model'; import { CollectionDataService } from '../../core/data/collection-data.service'; @@ -25,6 +24,7 @@ import { getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteWithNotEmptyData } from '../../core/shared/operators'; +import { FindListOptions } from '../../core/data/find-list-options.model'; /** * An interface to represent a collection entry diff --git a/src/app/shared/log-in/log-in.component.ts b/src/app/shared/log-in/log-in.component.ts index c0df6adec8..120f3ac4fa 100644 --- a/src/app/shared/log-in/log-in.component.ts +++ b/src/app/shared/log-in/log-in.component.ts @@ -8,12 +8,12 @@ import { isAuthenticated, isAuthenticationLoading } from '../../core/auth/selectors'; -import { CoreState } from '../../core/core.reducers'; import { getForgotPasswordRoute, getRegisterRoute } from '../../app-routing-paths'; import { hasValue } from '../empty.util'; import { AuthService } from '../../core/auth/auth.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { CoreState } from '../../core/core-state.model'; /** * /users/sign-in diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.ts b/src/app/shared/log-in/methods/password/log-in-password.component.ts index c72881d3bf..e4a92ded29 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.ts +++ b/src/app/shared/log-in/methods/password/log-in-password.component.ts @@ -7,7 +7,6 @@ import { Observable } from 'rxjs'; import { AuthenticateAction, ResetAuthenticationMessagesAction } from '../../../../core/auth/auth.actions'; import { getAuthenticationError, getAuthenticationInfo, } from '../../../../core/auth/selectors'; -import { CoreState } from '../../../../core/core.reducers'; import { isNotEmpty } from '../../../empty.util'; import { fadeOut } from '../../../animations/fade'; import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; @@ -15,6 +14,7 @@ import { renderAuthMethodFor } from '../log-in.methods-decorator'; import { AuthMethod } from '../../../../core/auth/models/auth.method'; import { AuthService } from '../../../../core/auth/auth.service'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; +import { CoreState } from '../../../../core/core-state.model'; /** * /users/sign-in diff --git a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.ts b/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.ts index b0704d5cb1..d218a7ca4e 100644 --- a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.ts +++ b/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.ts @@ -7,7 +7,6 @@ import { renderAuthMethodFor } from '../log-in.methods-decorator'; import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; import { AuthMethod } from '../../../../core/auth/models/auth.method'; -import { CoreState } from '../../../../core/core.reducers'; import { isAuthenticated, isAuthenticationLoading } from '../../../../core/auth/selectors'; import { RouteService } from '../../../../core/services/route.service'; import { NativeWindowRef, NativeWindowService } from '../../../../core/services/window.service'; @@ -16,6 +15,7 @@ import { AuthService } from '../../../../core/auth/auth.service'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; import { take } from 'rxjs/operators'; import { URLCombiner } from '../../../../core/url-combiner/url-combiner'; +import { CoreState } from '../../../../core/core-state.model'; @Component({ selector: 'ds-log-in-shibboleth', diff --git a/src/app/shared/mocks/remote-data-build.service.mock.ts b/src/app/shared/mocks/remote-data-build.service.mock.ts index 90071f8dc6..7b96c5a67a 100644 --- a/src/app/shared/mocks/remote-data-build.service.mock.ts +++ b/src/app/shared/mocks/remote-data-build.service.mock.ts @@ -3,10 +3,10 @@ import { map, switchMap } from 'rxjs/operators'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; import { PaginatedList, buildPaginatedList } from '../../core/data/paginated-list.model'; import { RemoteData } from '../../core/data/remote-data'; -import { RequestEntry } from '../../core/data/request.reducer'; import { PageInfo } from '../../core/shared/page-info.model'; import { hasValue } from '../empty.util'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { RequestEntry } from '../../core/data/request-entry.model'; export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observable>, buildList$?: Observable>>): RemoteDataBuildService { return { diff --git a/src/app/shared/mocks/request.service.mock.ts b/src/app/shared/mocks/request.service.mock.ts index 106fa8b6c7..f07aec587e 100644 --- a/src/app/shared/mocks/request.service.mock.ts +++ b/src/app/shared/mocks/request.service.mock.ts @@ -1,7 +1,7 @@ import {of as observableOf, Observable } from 'rxjs'; import { RequestService } from '../../core/data/request.service'; -import { RequestEntry } from '../../core/data/request.reducer'; import SpyObj = jasmine.SpyObj; +import { RequestEntry } from '../../core/data/request-entry.model'; export function getMockRequestService(requestEntry$: Observable = observableOf(new RequestEntry())): SpyObj { return jasmine.createSpyObj('requestService', { diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts index 07acf3ea75..0b5acd343b 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts @@ -15,7 +15,6 @@ import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-anal import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service'; import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { RemoteData } from '../../../../core/data/remote-data'; -import { FindListOptions } from '../../../../core/data/request.models'; import { Bitstream } from '../../../../core/shared/bitstream.model'; import { FileService } from '../../../../core/shared/file.service'; import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; @@ -32,6 +31,7 @@ import { VarDirective } from '../../../utils/var.directive'; import { ItemDetailPreviewFieldComponent } from './item-detail-preview-field/item-detail-preview-field.component'; import { ItemDetailPreviewComponent } from './item-detail-preview.component'; import { createPaginatedList } from '../../../testing/utils.test'; +import { FindListOptions } from '../../../../core/data/find-list-options.model'; function getMockFileService(): FileService { return jasmine.createSpyObj('FileService', { diff --git a/src/app/shared/pagination/pagination.component.spec.ts b/src/app/shared/pagination/pagination.component.spec.ts index cf2d1c13fd..0edab7c662 100644 --- a/src/app/shared/pagination/pagination.component.spec.ts +++ b/src/app/shared/pagination/pagination.component.spec.ts @@ -32,9 +32,9 @@ 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 { FindListOptions } from '../../core/data/request.models'; import { BehaviorSubject, of as observableOf } from 'rxjs'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; function expectPages(fixture: ComponentFixture, pagesDef: string[]): void { const de = fixture.debugElement.query(By.css('.pagination')); diff --git a/src/app/shared/pagination/pagination.utils.ts b/src/app/shared/pagination/pagination.utils.ts index 5701c96b54..f70a3d1030 100644 --- a/src/app/shared/pagination/pagination.utils.ts +++ b/src/app/shared/pagination/pagination.utils.ts @@ -1,5 +1,5 @@ import { PaginationComponentOptions } from './pagination-component-options.model'; -import { FindListOptions } from '../../core/data/request.models'; +import { FindListOptions } from '../../core/data/find-list-options.model'; /** * Transform a PaginationComponentOptions object into a FindListOptions object diff --git a/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.spec.ts b/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.spec.ts index 73aaab5170..91d9200c2d 100644 --- a/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.spec.ts +++ b/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.spec.ts @@ -20,9 +20,9 @@ import { buildPaginatedList } from '../../../../core/data/paginated-list.model'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../../core/data/request.models'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../testing/pagination-service.stub'; +import { FindListOptions } from '../../../../core/data/find-list-options.model'; describe('EpersonGroupListComponent test suite', () => { let comp: EpersonGroupListComponent; diff --git a/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.ts b/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.ts index 11b5b5e7b3..3277f724a2 100644 --- a/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.ts +++ b/src/app/shared/resource-policies/form/eperson-group-list/eperson-group-list.component.ts @@ -10,7 +10,6 @@ import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; import { DataService } from '../../../../core/data/data.service'; import { hasValue, isNotEmpty } from '../../../empty.util'; -import { FindListOptions } from '../../../../core/data/request.models'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { getDataServiceFor } from '../../../../core/cache/builders/build-decorators'; import { EPERSON } from '../../../../core/eperson/models/eperson.resource-type'; @@ -21,6 +20,7 @@ import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { fadeInOut } from '../../../animations/fade'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { FindListOptions } from '../../../../core/data/find-list-options.model'; export interface SearchEvent { scope: string; diff --git a/src/app/shared/search-form/search-form.component.spec.ts b/src/app/shared/search-form/search-form.component.spec.ts index 1469eac566..77b51d813d 100644 --- a/src/app/shared/search-form/search-form.component.spec.ts +++ b/src/app/shared/search-form/search-form.component.spec.ts @@ -10,11 +10,11 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; 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 { FindListOptions } from '../../core/data/request.models'; import { of as observableOf } from 'rxjs'; 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('SearchFormComponent', () => { let comp: SearchFormComponent; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts index 7299a39c32..59b14776a2 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts @@ -17,9 +17,9 @@ import { SearchFilterConfig } from '../../../../search-filter-config.model'; import { SearchFacetOptionComponent } from './search-facet-option.component'; import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../../../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../../../../core/data/request.models'; import { PaginationService } from '../../../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub'; +import { FindListOptions } from '../../../../../../core/data/find-list-options.model'; describe('SearchFacetOptionComponent', () => { let comp: SearchFacetOptionComponent; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts index 9ed8dee0ea..324773192f 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts @@ -21,9 +21,9 @@ import { } from '../../search-range-filter/search-range-filter.component'; import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../../../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../../../../core/data/request.models'; import { PaginationService } from '../../../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub'; +import { FindListOptions } from '../../../../../../core/data/find-list-options.model'; describe('SearchFacetRangeOptionComponent', () => { let comp: SearchFacetRangeOptionComponent; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts index 8f422b41bf..2fb9b67fca 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts @@ -16,9 +16,9 @@ import { SearchFilterConfig } from '../../../../search-filter-config.model'; import { SearchFacetSelectedOptionComponent } from './search-facet-selected-option.component'; import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../../../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../../../../core/data/request.models'; import { PaginationService } from '../../../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub'; +import { FindListOptions } from '../../../../../../core/data/find-list-options.model'; describe('SearchFacetSelectedOptionComponent', () => { let comp: SearchFacetSelectedOptionComponent; 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 b3f9471cbd..50bcbc6938 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 @@ -13,10 +13,10 @@ import { SearchConfigurationServiceStub } from '../../../testing/search-configur 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 { FindListOptions } from '../../../../core/data/request.models'; 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 dfee88c955..138f8d43ac 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 @@ -13,9 +13,9 @@ 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 { FindListOptions } from '../../../core/data/request.models'; 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 9f9d9d6d42..f8715e6ebf 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 @@ -10,10 +10,10 @@ 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 { FindListOptions } from '../../../core/data/request.models'; 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/pagination-service.stub.ts b/src/app/shared/testing/pagination-service.stub.ts index 985a5bfc4a..3ea8e732c8 100644 --- a/src/app/shared/testing/pagination-service.stub.ts +++ b/src/app/shared/testing/pagination-service.stub.ts @@ -1,7 +1,7 @@ import { of as observableOf } from 'rxjs'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../core/data/request.models'; +import { FindListOptions } from '../../core/data/find-list-options.model'; export class PaginationServiceStub { diff --git a/src/app/shared/testing/submission-rest-service.stub.ts b/src/app/shared/testing/submission-rest-service.stub.ts index 53b2341b50..b6ec9a86a2 100644 --- a/src/app/shared/testing/submission-rest-service.stub.ts +++ b/src/app/shared/testing/submission-rest-service.stub.ts @@ -2,8 +2,8 @@ import { of as observableOf } from 'rxjs'; import { Store } from '@ngrx/store'; import { RequestService } from '../../core/data/request.service'; -import { CoreState } from '../../core/core.reducers'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +import { CoreState } from '../../core/core-state.model'; export class SubmissionRestServiceStub { protected linkPath = 'workspaceitems'; diff --git a/src/app/shared/testing/utils.test.ts b/src/app/shared/testing/utils.test.ts index 1e62ee8f00..2f3b85d791 100644 --- a/src/app/shared/testing/utils.test.ts +++ b/src/app/shared/testing/utils.test.ts @@ -2,10 +2,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { Observable } from 'rxjs/internal/Observable'; -import { RequestEntry} from '../../core/data/request.reducer'; import { of as observableOf } from 'rxjs/internal/observable/of'; import { UnCacheableObject } from '../../core/shared/uncacheable-object.model'; import { RequestEntryState } from '../../core/data/request-entry-state.model'; +import { RequestEntry } from '../../core/data/request-entry.model'; /** * Returns true if a Native Element has a specified css class. diff --git a/src/app/shared/utils/follow-link-config.model.ts b/src/app/shared/utils/follow-link-config.model.ts index 08153e1217..8d785dbaaf 100644 --- a/src/app/shared/utils/follow-link-config.model.ts +++ b/src/app/shared/utils/follow-link-config.model.ts @@ -1,6 +1,6 @@ -import { FindListOptions } from '../../core/data/request.models'; import { HALResource } from '../../core/shared/hal-resource.model'; import { hasValue } from '../empty.util'; +import { FindListOptions } from '../../core/data/find-list-options.model'; /** * A class to send the retrieval of a {@link HALLink} diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts index 41ba775171..48c60e005c 100644 --- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts +++ b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts @@ -10,7 +10,6 @@ import { TranslateService } from '@ngx-translate/core'; import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { hasValue, isEmpty, isNotEmpty } from '../empty.util'; import { isAuthenticated } from '../../core/auth/selectors'; -import { CoreState } from '../../core/core.reducers'; import { VocabularyTreeviewService } from './vocabulary-treeview.service'; import { LOAD_MORE, LOAD_MORE_ROOT, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model'; import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model'; @@ -18,6 +17,7 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model'; import { VocabularyTreeFlattener } from './vocabulary-tree-flattener'; import { VocabularyTreeFlatDataSource } from './vocabulary-tree-flat-data-source'; +import { CoreState } from '../../core/core-state.model'; /** * Component that show a hierarchical vocabulary in a tree view diff --git a/src/app/statistics/statistics.service.spec.ts b/src/app/statistics/statistics.service.spec.ts index cdf81afdb6..347d3afb5e 100644 --- a/src/app/statistics/statistics.service.spec.ts +++ b/src/app/statistics/statistics.service.spec.ts @@ -2,10 +2,10 @@ import { StatisticsService } from './statistics.service'; import { RequestService } from '../core/data/request.service'; import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service.stub'; import { getMockRequestService } from '../shared/mocks/request.service.mock'; -import { TrackRequest } from './track-request.model'; import { isEqual } from 'lodash'; import { DSpaceObjectType } from '../core/shared/dspace-object-type.model'; import { SearchOptions } from '../shared/search/search-options.model'; +import { RestRequest } from '../core/data/rest-request.model'; describe('StatisticsService', () => { let service: StatisticsService; @@ -27,7 +27,7 @@ describe('StatisticsService', () => { it('should send a request to track an item view ', () => { const mockItem: any = {uuid: 'mock-item-uuid', type: 'item'}; service.trackViewEvent(mockItem); - const request: TrackRequest = requestService.send.calls.mostRecent().args[0]; + const request: RestRequest = requestService.send.calls.mostRecent().args[0]; expect(request.body).toBeDefined('request.body'); const body = JSON.parse(request.body); expect(body.targetId).toBe('mock-item-uuid'); @@ -51,7 +51,7 @@ describe('StatisticsService', () => { }; const sort = {by: 'search-field', order: 'ASC'}; service.trackSearchEvent(mockSearch, page, sort); - const request: TrackRequest = requestService.send.calls.mostRecent().args[0]; + const request: RestRequest = requestService.send.calls.mostRecent().args[0]; const body = JSON.parse(request.body); it('should specify the right query', () => { @@ -108,7 +108,7 @@ describe('StatisticsService', () => { } ]; service.trackSearchEvent(mockSearch, page, sort, filters); - const request: TrackRequest = requestService.send.calls.mostRecent().args[0]; + const request: RestRequest = requestService.send.calls.mostRecent().args[0]; const body = JSON.parse(request.body); it('should specify the dsoType', () => { diff --git a/src/app/statistics/statistics.service.ts b/src/app/statistics/statistics.service.ts index 9e12e627b5..0031773f76 100644 --- a/src/app/statistics/statistics.service.ts +++ b/src/app/statistics/statistics.service.ts @@ -5,8 +5,8 @@ import { map, take } from 'rxjs/operators'; import { TrackRequest } from './track-request.model'; import { hasValue, isNotEmpty } from '../shared/empty.util'; import { HALEndpointService } from '../core/shared/hal-endpoint.service'; -import { RestRequest } from '../core/data/request.models'; import { SearchOptions } from '../shared/search/search-options.model'; +import { RestRequest } from '../core/data/rest-request.model'; /** * The statistics service diff --git a/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.spec.ts b/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.spec.ts index 983e0355af..08b746ebdc 100644 --- a/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.spec.ts +++ b/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.spec.ts @@ -18,11 +18,11 @@ import { PageInfo } from '../../../core/shared/page-info.model'; import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated-list.model'; import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; import { ExternalSource } from '../../../core/shared/external-source.model'; -import { FindListOptions } from '../../../core/data/request.models'; import { HostWindowService } from '../../../shared/host-window.service'; import { HostWindowServiceStub } from '../../../shared/testing/host-window-service.stub'; import { getTestScheduler } from 'jasmine-marbles'; import { TestScheduler } from 'rxjs/testing'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('SubmissionImportExternalSearchbarComponent test suite', () => { let comp: SubmissionImportExternalSearchbarComponent; diff --git a/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.ts b/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.ts index 30f5184d57..0f1128243a 100644 --- a/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.ts +++ b/src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.ts @@ -17,10 +17,10 @@ import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated- import { RemoteData } from '../../../core/data/remote-data'; import { PageInfo } from '../../../core/shared/page-info.model'; import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; -import { FindListOptions } from '../../../core/data/request.models'; import { getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; import { HostWindowService } from '../../../shared/host-window.service'; import { hasValue } from '../../../shared/empty.util'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; /** * Interface for the selected external source element. From 41ad1727963b6e4ef70d36e7dfc939fc9a79d2a8 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 22 Mar 2022 14:00:55 +0100 Subject: [PATCH 094/570] add github actions check for circular dependencies --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6f4a11acc8..c08299c584 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,6 +78,9 @@ jobs: - name: Run lint run: yarn run lint + - name: Check for circular dependencies + run: npx madge --exclude '(bitstream|bundle|collection|config-submission-form|eperson|item|version)\.model\.ts$' --circular --extensions ts ./ + - name: Run build run: yarn run build:prod From c5110f89bc17290b6e224f9366cfa14dfa431d58 Mon Sep 17 00:00:00 2001 From: reetagithub <51482276+reetagithub@users.noreply.github.com> Date: Thu, 24 Mar 2022 11:18:18 +0200 Subject: [PATCH 095/570] Update fi.json5 Modified some keys to harmonize the translations of term 'administrative'. Also, translated "Administer Workflow" to a verb instead of a noun. --- src/assets/i18n/fi.json5 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/assets/i18n/fi.json5 b/src/assets/i18n/fi.json5 index 02f020a45d..860062fa67 100644 --- a/src/assets/i18n/fi.json5 +++ b/src/assets/i18n/fi.json5 @@ -107,7 +107,7 @@ "admin.registries.bitstream-formats.edit.head": "Tiedostoformaatti: {{ 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": "Sisäisiksi merkittyjä formaatteja käytetään hallinnollisiin tarkoituksiin, ja ne on piilotettu käyttäjiltä.", + "admin.registries.bitstream-formats.edit.internal.hint": "Sisäisiksi merkittyjä formaatteja käytetään ylläpitotarkoituksiin, ja ne on piilotettu käyttäjiltä.", // "admin.registries.bitstream-formats.edit.internal.label": "Internal", "admin.registries.bitstream-formats.edit.internal.label": "Sisäinen", @@ -662,7 +662,7 @@ // "admin.search.breadcrumbs": "Administrative Search", - "admin.search.breadcrumbs": "Hallinnollinen haku", + "admin.search.breadcrumbs": "Ylläpitäjän haku", // "admin.search.collection.edit": "Edit", "admin.search.collection.edit": "Muokkaa", @@ -692,19 +692,19 @@ "admin.search.item.withdraw": "Poista käytöstä", // "admin.search.title": "Administrative Search", - "admin.search.title": "Hallinnollinen haku", + "admin.search.title": "Ylläpitäjän haku", // "administrativeView.search.results.head": "Administrative Search", - "administrativeView.search.results.head": "Hallinnollinen haku", + "administrativeView.search.results.head": "Ylläpitäjän haku", // "admin.workflow.breadcrumbs": "Administer Workflow", - "admin.workflow.breadcrumbs": "Hallinnointityönkulku", + "admin.workflow.breadcrumbs": "Hallinnoi työnkulkua", // "admin.workflow.title": "Administer Workflow", - "admin.workflow.title": "Hallinnointityönkulku", + "admin.workflow.title": "Hallinnoi työnkulkua", // "admin.workflow.item.workflow": "Workflow", "admin.workflow.item.workflow": "Työnkulku", @@ -2954,7 +2954,7 @@ // "menu.section.admin_search": "Admin Search", - "menu.section.admin_search": "Admin-haku", + "menu.section.admin_search": "Ylläpitäjän haku", @@ -3033,7 +3033,7 @@ "menu.section.icon.access_control": "Pääsyoikeudet", // "menu.section.icon.admin_search": "Admin search menu section", - "menu.section.icon.admin_search": "Admin-haku", + "menu.section.icon.admin_search": "Ylläpitäjän haku", // "menu.section.icon.control_panel": "Control Panel menu section", "menu.section.icon.control_panel": "Hallintapaneeli", @@ -3168,7 +3168,7 @@ // "menu.section.workflow": "Administer Workflow", - "menu.section.workflow": "Hallinnointityönkulku", + "menu.section.workflow": "Hallinnoi työnkulkua", // "mydspace.description": "", @@ -5079,7 +5079,7 @@ // "workflowAdmin.search.results.head": "Administer Workflow", - "workflowAdmin.search.results.head": "Hallinnointityönkulku", + "workflowAdmin.search.results.head": "Hallinnoi työnkulkua", From 521e4ddb8de450841698102f17131f6364bd9c45 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 24 Mar 2022 11:42:04 +0100 Subject: [PATCH 096/570] 88248: Theming alert --- src/app/shared/alert/alert.component.html | 2 +- src/app/shared/item/item-alerts/item-alerts.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/alert/alert.component.html b/src/app/shared/alert/alert.component.html index 0b30edb5cc..3bc4782a77 100644 --- a/src/app/shared/alert/alert.component.html +++ b/src/app/shared/alert/alert.component.html @@ -1,4 +1,4 @@ - diff --git a/src/app/submission/sections/sherpa-policies/publication-information/publication-information.component.html b/src/app/submission/sections/sherpa-policies/publication-information/publication-information.component.html index 2e6da459ae..079535b2fb 100644 --- a/src/app/submission/sections/sherpa-policies/publication-information/publication-information.component.html +++ b/src/app/submission/sections/sherpa-policies/publication-information/publication-information.component.html @@ -1,8 +1,7 @@
-

{{'submission.sections.sherpa.publication.information.title' - | translate}}

+

{{'submission.sections.sherpa.publication.information.title' | translate}}

{{title}} @@ -11,8 +10,7 @@

-

{{'submission.sections.sherpa.publication.information.issns' - | translate}}

+

{{'submission.sections.sherpa.publication.information.issns' | translate}}

{{issn}} @@ -21,12 +19,11 @@

-

{{'submission.sections.sherpa.publication.information.url' - | translate}}

+

{{'submission.sections.sherpa.publication.information.url' | translate}}

- + {{journal.url}}

@@ -34,12 +31,11 @@
-

{{'submission.sections.sherpa.publication.information.publishers' - | translate}}

+

{{'submission.sections.sherpa.publication.information.publishers' | translate}}

- + {{publisher.name}}

@@ -47,8 +43,7 @@
-

{{'submission.sections.sherpa.publication.information.romeoPub' - | translate}}

+

{{'submission.sections.sherpa.publication.information.romeoPub' | translate}}

@@ -58,8 +53,7 @@

-

{{'submission.sections.sherpa.publication.information.zetoPub' - | translate}}

+

{{'submission.sections.sherpa.publication.information.zetoPub' | translate}}

@@ -67,4 +61,4 @@

-
\ No newline at end of file +
diff --git a/src/app/submission/sections/sherpa-policies/publisher-policy/publisher-policy.component.html b/src/app/submission/sections/sherpa-policies/publisher-policy/publisher-policy.component.html index 4a03bae735..ada6613aa2 100644 --- a/src/app/submission/sections/sherpa-policies/publisher-policy/publisher-policy.component.html +++ b/src/app/submission/sections/sherpa-policies/publisher-policy/publisher-policy.component.html @@ -15,9 +15,9 @@

-
\ No newline at end of file +
From aa7ceec15aaa331335a67e6d595c075d14918c25 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 29 Apr 2022 19:15:40 +0200 Subject: [PATCH 292/570] [CST-5738] Fix retrieving of embed from auth status model --- src/app/core/auth/auth-request.service.ts | 19 +++++++++++-------- src/app/core/auth/auth.service.ts | 3 ++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/app/core/auth/auth-request.service.ts b/src/app/core/auth/auth-request.service.ts index 516502ca6e..d0fec5fa71 100644 --- a/src/app/core/auth/auth-request.service.ts +++ b/src/app/core/auth/auth-request.service.ts @@ -12,6 +12,7 @@ import { AuthStatus } from './models/auth-status.model'; import { ShortLivedToken } from './models/short-lived-token.model'; import { URLCombiner } from '../url-combiner/url-combiner'; import { RestRequest } from '../data/rest-request.model'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; /** * Abstract service to send authentication requests @@ -26,16 +27,18 @@ export abstract class AuthRequestService { ) { } - protected fetchRequest(request: RestRequest): Observable> { - return this.rdbService.buildFromRequestUUID(request.uuid).pipe( + protected fetchRequest(request: RestRequest, ...linksToFollow: FollowLinkConfig[]): Observable> { + return this.rdbService.buildFromRequestUUID(request.uuid, ...linksToFollow).pipe( getFirstCompletedRemoteData(), ); } - protected getEndpointByMethod(endpoint: string, method: string, embed = false): string { + protected getEndpointByMethod(endpoint: string, method: string, ...linksToFollow: FollowLinkConfig[]): string { let url = isNotEmpty(method) ? `${endpoint}/${method}` : `${endpoint}`; - if (embed) { - url += '?embed=specialGroups'; + if (linksToFollow && linksToFollow.length > 0) { + linksToFollow.forEach((link: FollowLinkConfig, index: number) => { + url += ((index === 0) ? '?' : '&') + `embed=${link.name}`; + }); } return url; @@ -52,14 +55,14 @@ export abstract class AuthRequestService { distinctUntilChanged()); } - public getRequest(method: string, options?: HttpOptions, embed = false): Observable> { + public getRequest(method: string, options?: HttpOptions, ...linksToFollow: FollowLinkConfig[]): Observable> { return this.halService.getEndpoint(this.linkName).pipe( filter((href: string) => isNotEmpty(href)), - map((endpointURL) => this.getEndpointByMethod(endpointURL, method)), + map((endpointURL) => this.getEndpointByMethod(endpointURL, method, ...linksToFollow)), distinctUntilChanged(), map((endpointURL: string) => new GetRequest(this.requestService.generateRequestId(), endpointURL, undefined, options)), tap((request: GetRequest) => this.requestService.send(request)), - mergeMap((request: GetRequest) => this.fetchRequest(request)), + mergeMap((request: GetRequest) => this.fetchRequest(request, ...linksToFollow)), distinctUntilChanged()); } diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index 2d2c61490c..7796094e39 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -55,6 +55,7 @@ import { buildPaginatedList, PaginatedList } from '../data/paginated-list.model' import { Group } from '../eperson/models/group.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { PageInfo } from '../shared/page-info.model'; +import { followLink } from '../../shared/utils/follow-link-config.model'; export const LOGIN_ROUTE = '/login'; export const LOGOUT_ROUTE = '/logout'; @@ -219,7 +220,7 @@ export class AuthService { * Return the special groups list embedded in the AuthStatus model */ public getSpecialGroupsFromAuthStatus(): Observable>> { - return this.authRequestService.getRequest('status', null, true).pipe( + return this.authRequestService.getRequest('status', null, followLink('specialGroups')).pipe( getFirstCompletedRemoteData(), switchMap((status: RemoteData) => { if (status.hasSucceeded) { From 917e5fe5bc0fdee2f022892c6bfab795384578ba Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Mon, 2 May 2022 14:55:23 +0200 Subject: [PATCH 293/570] Issue 1635: Fix for @input not working on search bar ds-search-form --- src/app/shared/search-form/search-form.component.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 2791aee378..cabcab4859 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -77,6 +77,9 @@ export class SearchFormComponent { * @param data Values submitted using the form */ onSubmit(data: any) { + if (isNotEmpty(this.scope)) { + data = Object.assign(data, { scope: this.scope }); + } this.updateSearch(data); this.submitSearch.emit(data); } From f3a40a5ef900b180d7e6c27e320df4c20753e7a6 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 2 May 2022 16:08:58 +0200 Subject: [PATCH 294/570] [CST-5239] Fix button visualization --- .../metadata-import-page.component.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html index fb96c4becd..2c710935d5 100644 --- a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html +++ b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html @@ -12,8 +12,10 @@ [dropMessageLabelReplacement]="'admin.metadata-import.page.dropMsgReplace'"> - - +
+ + +
From bb3cc1c619dfe0e2f9a8bfb55380f8f5eb0671e9 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 2 May 2022 17:02:04 +0200 Subject: [PATCH 295/570] [CST-5674] Edit policy target; modal content; test --- .../resource-policy.service.spec.ts | 8 ++- .../resource-policy.service.ts | 55 +++++++++++++++++-- .../edit/resource-policy-edit.component.ts | 33 +++++++---- .../form/resource-policy-form.component.html | 15 ++++- .../resource-policy-form.component.spec.ts | 13 +++-- .../form/resource-policy-form.component.ts | 5 ++ src/assets/i18n/en.json5 | 10 ++++ 7 files changed, 117 insertions(+), 22 deletions(-) diff --git a/src/app/core/resource-policy/resource-policy.service.spec.ts b/src/app/core/resource-policy/resource-policy.service.spec.ts index 59316c0098..ca62159f59 100644 --- a/src/app/core/resource-policy/resource-policy.service.spec.ts +++ b/src/app/core/resource-policy/resource-policy.service.spec.ts @@ -19,6 +19,8 @@ import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils import { RestResponse } from '../cache/response.models'; import { RequestEntry } from '../data/request-entry.model'; import { FindListOptions } from '../data/find-list-options.model'; +import { EPersonDataService } from '../eperson/eperson-data.service'; +import { GroupDataService } from '../eperson/group-data.service'; describe('ResourcePolicyService', () => { let scheduler: TestScheduler; @@ -28,6 +30,8 @@ describe('ResourcePolicyService', () => { let objectCache: ObjectCacheService; let halService: HALEndpointService; let responseCacheEntry: RequestEntry; + let ePersonService: EPersonDataService; + let groupService: GroupDataService; const resourcePolicy: any = { id: '1', @@ -129,7 +133,9 @@ describe('ResourcePolicyService', () => { halService, notificationsService, http, - comparator + comparator, + ePersonService, + groupService ); spyOn((service as any).dataService, 'create').and.callThrough(); diff --git a/src/app/core/resource-policy/resource-policy.service.ts b/src/app/core/resource-policy/resource-policy.service.ts index 065e58c53d..f4f70a73e2 100644 --- a/src/app/core/resource-policy/resource-policy.service.ts +++ b/src/app/core/resource-policy/resource-policy.service.ts @@ -1,6 +1,6 @@ /* eslint-disable max-classes-per-file */ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; @@ -23,11 +23,19 @@ import { PaginatedList } from '../data/paginated-list.model'; import { ActionType } from './models/action-type.model'; import { RequestParam } from '../cache/models/request-param.model'; import { isNotEmpty } from '../../shared/empty.util'; -import { map } from 'rxjs/operators'; +import { map, switchMap, take } from 'rxjs/operators'; import { NoContent } from '../shared/NoContent.model'; import { getFirstCompletedRemoteData } from '../shared/operators'; import { CoreState } from '../core-state.model'; import { FindListOptions } from '../data/find-list-options.model'; +import { HttpOptions } from '../dspace-rest/dspace-rest.service'; +import { PostRequest } from '../data/request.models'; +import { GenericConstructor } from '../shared/generic-constructor'; +import { ResponseParsingService } from '../data/parsing.service'; +import { StatusCodeOnlyResponseParsingService } from '../data/status-code-only-response-parsing.service'; +import { HALLink } from '../shared/hal-link.model'; +import { EPersonDataService } from '../eperson/eperson-data.service'; +import { GroupDataService } from '../eperson/group-data.service'; /** @@ -44,7 +52,8 @@ class DataServiceImpl extends DataService { protected halService: HALEndpointService, protected notificationsService: NotificationsService, protected http: HttpClient, - protected comparator: ChangeAnalyzer) { + protected comparator: ChangeAnalyzer, + ) { super(); } @@ -68,7 +77,10 @@ export class ResourcePolicyService { protected halService: HALEndpointService, protected notificationsService: NotificationsService, protected http: HttpClient, - protected comparator: DefaultChangeAnalyzer) { + protected comparator: DefaultChangeAnalyzer, + protected ePersonService: EPersonDataService, + protected groupService: GroupDataService, + ) { this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator); } @@ -221,4 +233,39 @@ export class ResourcePolicyService { return this.dataService.searchBy(this.searchByResourceMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } + /** + * Update the target of the resource policy + * @param resourcePolicyHref the link to the resource policy + * @param uuid the UUID of the target to which the permission is being granted + * @param type the type of the target (eperson or group) to which the permission is being granted + */ + updateTarget(resourcePolicyHref: string, uuid: string, type: string): Observable> { + + const targetService = type === 'eperson' ? this.ePersonService : this.groupService; + + const ep$ = targetService.getBrowseEndpoint().pipe( + take(1), + map((endpoint: string) =>`${endpoint}/${uuid}`), + ); + + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'text/uri-list'); + options.headers = headers; + + const requestId = this.requestService.generateRequestId(); + + return ep$.pipe(switchMap((ep) => { + const request = new PostRequest(requestId, resourcePolicyHref, ep, options); + Object.assign(request, { + getResponseParser(): GenericConstructor { + return StatusCodeOnlyResponseParsingService; + } + }); + this.requestService.send(request); + return this.rdbService.buildFromRequestUUID(requestId); + })); + + } + } diff --git a/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts b/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts index a515eef675..d2c1b6dd0d 100644 --- a/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts +++ b/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, Observable, of, combineLatest as observableCombineLatest, } from 'rxjs'; import { map, take } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; @@ -88,16 +88,29 @@ export class ResourcePolicyEditComponent implements OnInit { type: RESOURCE_POLICY.value, _links: this.resourcePolicy._links }); - this.resourcePolicyService.update(updatedObject).pipe( + + const updateTargetSucceeded$ = event.updateTarget ? this.resourcePolicyService.updateTarget( + this.resourcePolicy._links.self.href, event.target.uuid, event.target.type + ).pipe( getFirstCompletedRemoteData(), - ).subscribe((responseRD: RemoteData) => { - this.processing$.next(false); - if (responseRD && responseRD.hasSucceeded) { - this.notificationsService.success(null, this.translate.get('resource-policies.edit.page.success.content')); - this.redirectToAuthorizationsPage(); - } else { - this.notificationsService.error(null, this.translate.get('resource-policies.edit.page.failure.content')); + map((responseRD) => responseRD && responseRD.hasSucceeded) + ) : of(true); + + const updateResourcePolicySucceeded$ = this.resourcePolicyService.update(updatedObject).pipe( + getFirstCompletedRemoteData(), + map((responseRD) => responseRD && responseRD.hasSucceeded) + ); + + observableCombineLatest([updateTargetSucceeded$, updateResourcePolicySucceeded$]).subscribe( + ([updateTargetSucceeded, updateResourcePolicySucceeded]) => { + this.processing$.next(false); + if (updateTargetSucceeded && updateResourcePolicySucceeded) { + this.notificationsService.success(null, this.translate.get('resource-policies.edit.page.success.content')); + this.redirectToAuthorizationsPage(); + } else { + this.notificationsService.error(null, this.translate.get('resource-policies.edit.page.failure.content')); + } } - }); + ); } } diff --git a/src/app/shared/resource-policies/form/resource-policy-form.component.html b/src/app/shared/resource-policies/form/resource-policy-form.component.html index 821d15f414..f7aad55ce8 100644 --- a/src/app/shared/resource-policies/form/resource-policy-form.component.html +++ b/src/app/shared/resource-policies/form/resource-policy-form.component.html @@ -55,15 +55,24 @@ diff --git a/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts b/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts index c31a65c1f6..456eb6db5e 100644 --- a/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts +++ b/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts @@ -222,6 +222,8 @@ describe('ResourcePolicyFormComponent test suite', () => { testFixture = createTestComponent(html, TestComponent) as ComponentFixture; testComp = testFixture.componentInstance; + testComp.resourcePolicy = resourcePolicy; + fixture.detectChanges(); }); afterEach(() => { @@ -242,6 +244,7 @@ describe('ResourcePolicyFormComponent test suite', () => { fixture = TestBed.createComponent(ResourcePolicyFormComponent); comp = fixture.componentInstance; compAsAny = fixture.componentInstance; + compAsAny.resourcePolicy = resourcePolicy; comp.isProcessing = observableOf(false); }); @@ -261,7 +264,7 @@ describe('ResourcePolicyFormComponent test suite', () => { expect(compAsAny.buildResourcePolicyForm).toHaveBeenCalled(); expect(compAsAny.initModelsValue).toHaveBeenCalled(); expect(compAsAny.formModel.length).toBe(5); - expect(compAsAny.subs.length).toBe(0); + expect(compAsAny.subs.length).toBe(1); }); @@ -279,7 +282,7 @@ describe('ResourcePolicyFormComponent test suite', () => { expect(compAsAny.reset.emit).toHaveBeenCalled(); }); - it('should update resource policy grant object properly', () => { + it('should update resource policy grant object properly', () => { comp.updateObjectSelected(EPersonMock, true); expect(comp.resourcePolicyGrant).toEqual(EPersonMock); @@ -301,6 +304,7 @@ describe('ResourcePolicyFormComponent test suite', () => { comp = fixture.componentInstance; compAsAny = fixture.componentInstance; comp.resourcePolicy = resourcePolicy; + compAsAny.resourcePolicy = resourcePolicy; comp.isProcessing = observableOf(false); compAsAny.ePersonService.findByHref.and.returnValue( observableOf(createSuccessfulRemoteDataObject({})).pipe(delay(100)) @@ -343,8 +347,8 @@ describe('ResourcePolicyFormComponent test suite', () => { }); }); - it('should not can set grant', () => { - expect(comp.isBeingEdited()).toBeFalsy(); + it('should be being edited', () => { + expect(comp.isBeingEdited()).toBeTrue(); }); it('should have a target name', () => { @@ -398,6 +402,7 @@ describe('ResourcePolicyFormComponent test suite', () => { type: 'group', uuid: GroupMock.id }; + eventPayload.updateTarget = false; scheduler = getTestScheduler(); scheduler.schedule(() => comp.onSubmit()); diff --git a/src/app/shared/resource-policies/form/resource-policy-form.component.ts b/src/app/shared/resource-policies/form/resource-policy-form.component.ts index ea49433db0..2783200d8f 100644 --- a/src/app/shared/resource-policies/form/resource-policy-form.component.ts +++ b/src/app/shared/resource-policies/form/resource-policy-form.component.ts @@ -49,6 +49,7 @@ export interface ResourcePolicyEvent { type: string, uuid: string }; + updateTarget: boolean; } @Component({ @@ -130,6 +131,8 @@ export class ResourcePolicyFormComponent implements OnInit, OnDestroy { navActiveId: string; + resourcePolicyTargetUpdated = false; + /** * Initialize instance variables * @@ -278,6 +281,7 @@ export class ResourcePolicyFormComponent implements OnInit, OnDestroy { * Update reference to the eperson or group that will be granted the permission */ updateObjectSelected(object: DSpaceObject, isEPerson: boolean): void { + this.resourcePolicyTargetUpdated = true; this.resourcePolicyGrant = object; this.resourcePolicyGrantType = isEPerson ? 'eperson' : 'group'; this.resourcePolicyTargetName$.next(this.getResourcePolicyTargetName()); @@ -304,6 +308,7 @@ export class ResourcePolicyFormComponent implements OnInit, OnDestroy { type: this.resourcePolicyGrantType, uuid: this.resourcePolicyGrant.id }; + eventPayload.updateTarget = this.resourcePolicyTargetUpdated; this.submit.emit(eventPayload); }); } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index a7ce942e7d..dfc715b72b 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3136,6 +3136,16 @@ "resource-policies.form.eperson-group-list.table.headers.name": "Name", + "resource-policies.form.eperson-group-list.modal.header": "Cannot change type", + + "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.toEPerson": "It is not possible to replace a group with an 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.close": "Ok", + "resource-policies.form.date.end.label": "End Date", "resource-policies.form.date.start.label": "Start Date", From 833637c215afa64389a6ee47738ee27d6907f4b6 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 3 May 2022 14:10:33 +1200 Subject: [PATCH 296/570] [TLC-254] Get Type Bind config from backend --- config/config.example.yml | 5 --- .../ds-dynamic-type-bind-relation.service.ts | 8 ++-- .../form/builder/form-builder.service.spec.ts | 26 ++++++++++- .../form/builder/form-builder.service.ts | 43 ++++++++++++++++--- .../builder/parsers/concat-field-parser.ts | 2 +- .../builder/parsers/date-field-parser.spec.ts | 3 +- .../parsers/disabled-field-parser.spec.ts | 3 +- .../parsers/dropdown-field-parser.spec.ts | 3 +- .../builder/parsers/dropdown-field-parser.ts | 4 +- .../form/builder/parsers/field-parser.ts | 18 ++++---- .../builder/parsers/list-field-parser.spec.ts | 3 +- .../parsers/lookup-field-parser.spec.ts | 3 +- .../parsers/lookup-name-field-parser.spec.ts | 3 +- .../builder/parsers/name-field-parser.spec.ts | 3 +- .../parsers/onebox-field-parser.spec.ts | 3 +- .../form/builder/parsers/parser-options.ts | 1 + .../relation-group-field-parser.spec.ts | 3 +- .../form/builder/parsers/row-parser.spec.ts | 23 +++++----- .../shared/form/builder/parsers/row-parser.ts | 6 ++- .../parsers/series-field-parser.spec.ts | 3 +- .../builder/parsers/tag-field-parser.spec.ts | 3 +- .../parsers/textarea-field-parser.spec.ts | 3 +- src/app/submission/submission.module.ts | 3 +- 23 files changed, 123 insertions(+), 52 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index fb0b4fd589..771c7b1653 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -77,11 +77,6 @@ submission: # NOTE: after how many time (milliseconds) submission is saved automatically # eg. timer: 5 * (1000 * 60); // 5 minutes timer: 0 - typeBind: - # NOTE: which field to use when matching to type-bind configuration, - # eg. dc.type, local.publicationType - # default: dc.type - field: dc.type icons: metadata: # NOTE: example of configuration diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts index c9473bd9d2..39f754af24 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts @@ -2,7 +2,7 @@ import { Inject, Injectable, Injector, Optional } from '@angular/core'; import { FormControl } from '@angular/forms'; import { Subscription } from 'rxjs'; -import { startWith } from 'rxjs/operators'; +import {map, startWith} from 'rxjs/operators'; import { AND_OPERATOR, @@ -15,10 +15,12 @@ import { OR_OPERATOR } from '@ng-dynamic-forms/core'; -import { hasNoValue, hasValue } from '../../../empty.util'; +import {hasNoValue, hasValue, isEmpty} from '../../../empty.util'; import { FormBuilderService } from '../form-builder.service'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-constants'; +import {getFirstCompletedRemoteData} from "../../../../core/shared/operators"; +import {ConfigurationDataService} from "../../../../core/data/configuration-data.service"; /** * Service to manage type binding for submission input fields @@ -31,7 +33,6 @@ export class DsDynamicTypeBindRelationService { protected dynamicFormRelationService: DynamicFormRelationService, protected formBuilderService: FormBuilderService, protected injector: Injector) { - } /** @@ -53,6 +54,7 @@ export class DsDynamicTypeBindRelationService { return value; } + /** * Get models for this bind type * @param model diff --git a/src/app/shared/form/builder/form-builder.service.spec.ts b/src/app/shared/form/builder/form-builder.service.spec.ts index 14c4e336cd..d2cd887e82 100644 --- a/src/app/shared/form/builder/form-builder.service.spec.ts +++ b/src/app/shared/form/builder/form-builder.service.spec.ts @@ -48,12 +48,18 @@ import { DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-conca import { DynamicLookupNameModel } from './ds-dynamic-form-ui/models/lookup/dynamic-lookup-name.model'; import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { FormRowModel } from '../../../core/config/models/config-submission-form.model'; +import {ConfigurationDataService} from "../../../core/data/configuration-data.service"; +import {createSuccessfulRemoteDataObject$} from "../../remote-data.utils"; +import {ConfigurationProperty} from "../../../core/shared/configuration-property.model"; describe('FormBuilderService test suite', () => { let testModel: DynamicFormControlModel[]; let testFormConfiguration: SubmissionFormsModel; let service: FormBuilderService; + let configSpy: ConfigurationDataService; + const typeFieldProp = 'submit.type-bind.field'; + const typeFieldTestValue = 'dc.type'; const submissionId = '1234'; @@ -65,15 +71,24 @@ describe('FormBuilderService test suite', () => { return new Promise((resolve) => setTimeout(() => resolve(true), 0)); } - beforeEach(() => { + const createConfigSuccessSpy = (...values: string[]) => jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$({ + ... new ConfigurationProperty(), + name: typeFieldProp, + values: values, + }), + }); + beforeEach(() => { + configSpy = createConfigSuccessSpy(typeFieldTestValue); TestBed.configureTestingModule({ imports: [ReactiveFormsModule], providers: [ { provide: FormBuilderService, useClass: FormBuilderService }, { provide: DynamicFormValidationService, useValue: {} }, { provide: NG_VALIDATORS, useValue: testValidator, multi: true }, - { provide: NG_ASYNC_VALIDATORS, useValue: testAsyncValidator, multi: true } + { provide: NG_ASYNC_VALIDATORS, useValue: testAsyncValidator, multi: true }, + { provide: ConfigurationDataService, useValue: configSpy } ] }); @@ -881,4 +896,11 @@ describe('FormBuilderService test suite', () => { expect(formArray.length === 0).toBe(true); }); + it(`should request the ${typeFieldProp} property and set value "dc_type"`, () => { + service.setTypeBindFieldFromConfig(); + expect(configSpy.findByPropertyName).toHaveBeenCalledTimes(1); + expect(configSpy.findByPropertyName).toHaveBeenCalledWith(typeFieldProp); + expect(service.getTypeField()).toEqual('dc_type'); + }); + }); diff --git a/src/app/shared/form/builder/form-builder.service.ts b/src/app/shared/form/builder/form-builder.service.ts index 532a4fe085..9cca31e755 100644 --- a/src/app/shared/form/builder/form-builder.service.ts +++ b/src/app/shared/form/builder/form-builder.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import {Injectable, Optional} from '@angular/core'; import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; import { @@ -42,6 +42,8 @@ import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-ui/d import { CONCAT_GROUP_SUFFIX, DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model'; import { VIRTUAL_METADATA_PREFIX } from '../../../core/shared/metadata.models'; import { environment } from '../../../../environments/environment'; +import {ConfigurationDataService} from "../../../core/data/configuration-data.service"; +import {getFirstCompletedRemoteData} from "../../../core/shared/operators"; @Injectable() export class FormBuilderService extends DynamicFormService { @@ -66,13 +68,17 @@ export class FormBuilderService extends DynamicFormService { constructor( componentService: DynamicFormComponentService, validationService: DynamicFormValidationService, - protected rowParser: RowParser + protected rowParser: RowParser, + @Optional() protected configService: ConfigurationDataService, ) { super(componentService, validationService); this.formModels = new Map(); this.formGroups = new Map(); - // Replace . with _ in configured type field here, to make configuration more simple and user-friendly - this.typeField = environment.submission.typeBind.field.replace(/\./g, '_'); + if (hasValue(configService)) { + this.setTypeBindFieldFromConfig(); + } else { + this.typeField = 'dc_type'; + } } createDynamicFormControlEvent(control: FormControl, group: FormGroup, model: DynamicFormControlModel, type: string): DynamicFormControlEvent { @@ -278,7 +284,8 @@ export class FormBuilderService extends DynamicFormService { const rawData = typeof json === 'string' ? JSON.parse(json, parseReviver) : json; if (rawData.rows && !isEmpty(rawData.rows)) { rawData.rows.forEach((currentRow) => { - const rowParsed = this.rowParser.parse(submissionId, currentRow, scopeUUID, sectionData, submissionScope, readOnly); + const rowParsed = this.rowParser.parse(submissionId, currentRow, scopeUUID, sectionData, submissionScope, + readOnly, this.typeField); if (isNotNull(rowParsed)) { if (Array.isArray(rowParsed)) { rows = rows.concat(rowParsed); @@ -489,4 +496,30 @@ export class FormBuilderService extends DynamicFormService { return Object.keys(result); } + /** + * Get the type bind field from config + */ + setTypeBindFieldFromConfig(): void { + this.configService.findByPropertyName('submit.type-bind.field').pipe( + getFirstCompletedRemoteData(), + ).subscribe((remoteData: any) => { + // make sure we got a success response from the backend + if (!remoteData.hasSucceeded) { + this.typeField = 'dc_type'; + return; + } + // Read type bind value from response and set if non-empty + const typeFieldConfig = remoteData.payload.values[0]; + if (isEmpty(typeFieldConfig)) { + this.typeField = 'dc_type'; + } else { + this.typeField = typeFieldConfig.replace(/\./g, '_'); + } + }); + } + + getTypeField(): string { + return this.typeField; + } + } diff --git a/src/app/shared/form/builder/parsers/concat-field-parser.ts b/src/app/shared/form/builder/parsers/concat-field-parser.ts index 8085924422..2de5f256dc 100644 --- a/src/app/shared/form/builder/parsers/concat-field-parser.ts +++ b/src/app/shared/form/builder/parsers/concat-field-parser.ts @@ -1,4 +1,4 @@ -import { Inject } from '@angular/core'; +import {Inject} from '@angular/core'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { diff --git a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts index b9adf3ed65..9ab43709ad 100644 --- a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts @@ -12,7 +12,8 @@ describe('DateFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts b/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts index e3e86d7051..d69f0e48e9 100644 --- a/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts @@ -11,7 +11,8 @@ describe('DisabledFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts index 82d2aeac63..3dca7558b3 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts @@ -11,7 +11,8 @@ describe('DropdownFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts index 760fc63482..3e5ec0b9da 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts @@ -1,4 +1,4 @@ -import { Inject } from '@angular/core'; +import {Inject} from '@angular/core'; import { FormFieldModel } from '../models/form-field.model'; import { CONFIG_DATA, @@ -22,7 +22,7 @@ export class DropdownFieldParser extends FieldParser { @Inject(SUBMISSION_ID) submissionId: string, @Inject(CONFIG_DATA) configData: FormFieldModel, @Inject(INIT_FORM_VALUES) initFormValues, - @Inject(PARSER_OPTIONS) parserOptions: ParserOptions + @Inject(PARSER_OPTIONS) parserOptions: ParserOptions, ) { super(submissionId, configData, initFormValues, parserOptions); } diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index c8b8e07bad..35743ec9b7 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -1,9 +1,9 @@ -import { Inject, InjectionToken } from '@angular/core'; +import {Inject, InjectionToken} from '@angular/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 {hasValue, isEmpty, isNotEmpty, isNotNull, isNotUndefined} from '../../../empty.util'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { @@ -18,6 +18,8 @@ import { VocabularyOptions } from '../../../../core/submission/vocabularies/mode import { ParserType } from './parser-type'; import { isNgbDateStruct } from '../../../date.util'; import { environment } from '../../../../../environments/environment'; +import {getFirstCompletedRemoteData} from "../../../../core/shared/operators"; +import {map} from "rxjs/operators"; export const SUBMISSION_ID: InjectionToken = new InjectionToken('submissionId'); export const CONFIG_DATA: InjectionToken = new InjectionToken('configData'); @@ -39,8 +41,6 @@ export abstract class FieldParser { @Inject(INIT_FORM_VALUES) protected initFormValues: any, @Inject(PARSER_OPTIONS) protected parserOptions: ParserOptions ) { - // Replace . with _ in configured type field here, to make configuration more simple and user-friendly - this.typeField = environment.submission.typeBind.field.replace(/\./g, '_'); } public abstract modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any; @@ -75,7 +75,8 @@ export abstract class FieldParser { metadataFields: this.getAllFieldIds(), hasSelectableMetadata: isNotEmpty(this.configData.selectableMetadata), isDraggable, - typeBindRelations: isNotEmpty(this.configData.typeBind) ? this.getTypeBindRelations(this.configData.typeBind) : null, + typeBindRelations: isNotEmpty(this.configData.typeBind) ? this.getTypeBindRelations(this.configData.typeBind, + this.parserOptions.typeField) : null, groupFactory: () => { let model; if ((arrayCounter === 0)) { @@ -303,7 +304,8 @@ export abstract class FieldParser { // If typeBind is configured if (isNotEmpty(this.configData.typeBind)) { - (controlModel as DsDynamicInputModel).typeBindRelations = this.getTypeBindRelations(this.configData.typeBind); + (controlModel as DsDynamicInputModel).typeBindRelations = this.getTypeBindRelations(this.configData.typeBind, + this.parserOptions.typeField); } return controlModel; @@ -319,11 +321,11 @@ export abstract class FieldParser { * @private * @return DynamicFormControlRelation[] array with one relation in it, for type bind matching to show a field */ - private getTypeBindRelations(configuredTypeBindValues: string[]): DynamicFormControlRelation[] { + private getTypeBindRelations(configuredTypeBindValues: string[], typeField: string): DynamicFormControlRelation[] { const bindValues = []; configuredTypeBindValues.forEach((value) => { bindValues.push({ - id: this.typeField, + id: typeField, value: value }); }); diff --git a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts index 8a05b169fd..30d1913a51 100644 --- a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts @@ -13,7 +13,8 @@ describe('ListFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts index 87cee9d950..24efcf3462 100644 --- a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts @@ -12,7 +12,8 @@ describe('LookupFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts index 3d02b6952e..d0281681ef 100644 --- a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts @@ -12,7 +12,8 @@ describe('LookupNameFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts index 514585f03f..6b520142cc 100644 --- a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts @@ -14,7 +14,8 @@ describe('NameFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts index 8ecce24194..e7e68a6461 100644 --- a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts @@ -15,7 +15,8 @@ describe('OneboxFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/parser-options.ts b/src/app/shared/form/builder/parsers/parser-options.ts index 8b0b42008e..f7aac3449d 100644 --- a/src/app/shared/form/builder/parsers/parser-options.ts +++ b/src/app/shared/form/builder/parsers/parser-options.ts @@ -2,4 +2,5 @@ export interface ParserOptions { readOnly: boolean; submissionScope: string; collectionUUID: string; + typeField: string; } diff --git a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts index 111193a637..7d48ad2d00 100644 --- a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts @@ -12,7 +12,8 @@ describe('RelationGroupFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: 'WORKSPACE' + collectionUUID: 'WORKSPACE', + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/row-parser.spec.ts b/src/app/shared/form/builder/parsers/row-parser.spec.ts index e612534d55..1f9bde8a7f 100644 --- a/src/app/shared/form/builder/parsers/row-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/row-parser.spec.ts @@ -22,6 +22,7 @@ describe('RowParser test suite', () => { const initFormValues = {}; const submissionScope = 'WORKSPACE'; const readOnly = false; + const typeField = 'dc_type'; beforeEach(() => { row1 = { @@ -338,7 +339,7 @@ describe('RowParser test suite', () => { it('should return a DynamicRowGroupModel object', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel instanceof DynamicRowGroupModel).toBe(true); }); @@ -346,7 +347,7 @@ describe('RowParser test suite', () => { it('should return a row with three fields', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect((rowModel as DynamicRowGroupModel).group.length).toBe(3); }); @@ -354,7 +355,7 @@ describe('RowParser test suite', () => { it('should return a DynamicRowArrayModel object', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row2, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row2, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel instanceof DynamicRowArrayModel).toBe(true); }); @@ -362,7 +363,7 @@ describe('RowParser test suite', () => { it('should return a row that contains only scoped fields', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row3, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row3, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect((rowModel as DynamicRowGroupModel).group.length).toBe(1); }); @@ -370,7 +371,7 @@ describe('RowParser test suite', () => { it('should be able to parse a dropdown combo field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row4, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row4, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel).toBeDefined(); }); @@ -378,7 +379,7 @@ describe('RowParser test suite', () => { it('should be able to parse a lookup-name field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row5, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row5, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel).toBeDefined(); }); @@ -386,7 +387,7 @@ describe('RowParser test suite', () => { it('should be able to parse a list field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row6, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row6, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel).toBeDefined(); }); @@ -394,7 +395,7 @@ describe('RowParser test suite', () => { it('should be able to parse a date field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row7, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row7, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel).toBeDefined(); }); @@ -402,7 +403,7 @@ describe('RowParser test suite', () => { it('should be able to parse a tag field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row8, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row8, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel).toBeDefined(); }); @@ -410,7 +411,7 @@ describe('RowParser test suite', () => { it('should be able to parse a textarea field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row9, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row9, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel).toBeDefined(); }); @@ -418,7 +419,7 @@ describe('RowParser test suite', () => { it('should be able to parse a group field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row10, scopeUUID, initFormValues, submissionScope, readOnly); + const rowModel = parser.parse(submissionId, row10, scopeUUID, initFormValues, submissionScope, readOnly, typeField); expect(rowModel).toBeDefined(); }); diff --git a/src/app/shared/form/builder/parsers/row-parser.ts b/src/app/shared/form/builder/parsers/row-parser.ts index fe664305b0..764f52ffdf 100644 --- a/src/app/shared/form/builder/parsers/row-parser.ts +++ b/src/app/shared/form/builder/parsers/row-parser.ts @@ -31,7 +31,8 @@ export class RowParser { scopeUUID, initFormValues: any, submissionScope, - readOnly: boolean): DynamicRowGroupModel { + readOnly: boolean, + typeField: string): DynamicRowGroupModel { let fieldModel: any = null; let parsedResult = null; const config: DynamicFormGroupModelConfig = { @@ -47,7 +48,8 @@ export class RowParser { const parserOptions: ParserOptions = { readOnly: readOnly, submissionScope: submissionScope, - collectionUUID: scopeUUID + collectionUUID: scopeUUID, + typeField: typeField }; // Iterate over row's fields diff --git a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts index b044f43833..0761cfe60e 100644 --- a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts @@ -12,7 +12,8 @@ describe('SeriesFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts index 7c63235f67..115829f8d3 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts @@ -12,7 +12,8 @@ describe('TagFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts index a81907aa13..855e464f21 100644 --- a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts @@ -12,7 +12,8 @@ describe('TextareaFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, - collectionUUID: null + collectionUUID: null, + typeField: 'dc_type' }; beforeEach(() => { diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 939d1bff29..d9b83d9864 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -42,6 +42,7 @@ import { NgbAccordionModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component'; import { SubmissionAccessesConfigService } from '../core/config/submission-accesses-config.service'; import { SectionAccessesService } from './sections/accesses/section-accesses.service'; +import {ConfigurationDataService} from "../core/data/configuration-data.service"; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -97,7 +98,7 @@ const DECLARATIONS = [ SectionsService, SubmissionUploadsConfigService, SubmissionAccessesConfigService, - SectionAccessesService + SectionAccessesService, ] }) From fd0c8f409e061487c6b57a1a50c18c0c9f3166f5 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 3 May 2022 14:11:02 +1200 Subject: [PATCH 297/570] [TLC-254] Tests for isObjectEmpty() --- src/app/shared/empty.util.spec.ts | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/app/shared/empty.util.spec.ts b/src/app/shared/empty.util.spec.ts index 1112883c2a..7516e08ca7 100644 --- a/src/app/shared/empty.util.spec.ts +++ b/src/app/shared/empty.util.spec.ts @@ -9,7 +9,7 @@ import { isNotEmptyOperator, isNotNull, isNotUndefined, - isNull, + isNull, isObjectEmpty, isUndefined } from './empty.util'; @@ -444,6 +444,43 @@ describe('Empty Utils', () => { }); }); + describe('isObjectEmpty', () => { + /* + isObjectEmpty(); // true + isObjectEmpty(null); // true + isObjectEmpty(undefined); // true + isObjectEmpty(''); // true + isObjectEmpty([]); // true + isObjectEmpty({}); // true + isObjectEmpty({name: null}); // true + isObjectEmpty({ name: 'Adam Hawkins', surname : null}); // false + */ + it('should be empty if no parameter passed', () => { + expect(isObjectEmpty()).toBeTrue(); + }) + it('should be empty if null parameter passed', () => { + expect(isObjectEmpty(null)).toBeTrue(); + }) + it('should be empty if undefined parameter passed', () => { + expect(isObjectEmpty(undefined)).toBeTrue(); + }) + it('should be empty if empty string passed', () => { + expect(isObjectEmpty('')).toBeTrue(); + }) + it('should be empty if empty array passed', () => { + expect(isObjectEmpty([])).toBeTrue(); + }) + it('should be empty if empty object passed', () => { + expect(isObjectEmpty({})).toBeTrue(); + }) + it('should be empty if single key with null value passed', () => { + expect(isObjectEmpty({ name: null })).toBeTrue(); + }) + it('should NOT be empty if object with at least one non-null value passed', () => { + expect(isObjectEmpty({ name: 'Adam Hawkins', surname : null })).toBeFalse(); + }) + }); + describe('ensureArrayHasValue', () => { it('should let all arrays pass unchanged, and turn everything else in to empty arrays', () => { const sourceData = { From b9b5b50999a3e3173a97709e403c4773839116a5 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 3 May 2022 08:56:37 +0200 Subject: [PATCH 298/570] 89741: Added ThemedExpandableNavbarSectionComponent --- ...med-expandable-navbar-section.component.ts | 29 +++++++++++++++++++ src/app/navbar/navbar.module.ts | 11 +++---- 2 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts diff --git a/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts b/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts new file mode 100644 index 0000000000..744eeb5a02 --- /dev/null +++ b/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts @@ -0,0 +1,29 @@ +import { Component } from '@angular/core'; +import { ThemedComponent } from '../../shared/theme-support/themed.component'; +import { ExpandableNavbarSectionComponent } from './expandable-navbar-section.component'; +import { rendersSectionForMenu } from '../../shared/menu/menu-section.decorator'; +import { MenuID } from '../../shared/menu/initial-menus-state'; + +/** + * Themed wrapper for ExpandableNavbarSectionComponent + */ +@Component({ + /* tslint:disable:component-selector */ + selector: 'li[ds-themed-expandable-navbar-section]', + styleUrls: [], + templateUrl: '../../shared/theme-support/themed.component.html', +}) +@rendersSectionForMenu(MenuID.PUBLIC, true) +export class ThemedExpandableNavbarSectionComponent extends ThemedComponent { + protected getComponentName(): string { + return 'ExpandableNavbarSectionComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/navbar/expandable-navbar-section/expandable-navbar-section.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./expandable-navbar-section.component`); + } +} diff --git a/src/app/navbar/navbar.module.ts b/src/app/navbar/navbar.module.ts index c84e732fd5..95641de41b 100644 --- a/src/app/navbar/navbar.module.ts +++ b/src/app/navbar/navbar.module.ts @@ -7,6 +7,7 @@ import { CoreModule } from '../core/core.module'; import { NavbarEffects } from './navbar.effects'; import { NavbarSectionComponent } from './navbar-section/navbar-section.component'; import { ExpandableNavbarSectionComponent } from './expandable-navbar-section/expandable-navbar-section.component'; +import { ThemedExpandableNavbarSectionComponent } from './expandable-navbar-section/themed-expandable-navbar-section.component'; import { NavbarComponent } from './navbar.component'; import { MenuModule } from '../shared/menu/menu.module'; import { SharedModule } from '../shared/shared.module'; @@ -20,7 +21,7 @@ const effects = [ const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator NavbarSectionComponent, - ExpandableNavbarSectionComponent, + ThemedExpandableNavbarSectionComponent, ]; @NgModule({ @@ -36,19 +37,19 @@ const ENTRY_COMPONENTS = [ NavbarComponent, ThemedNavbarComponent, NavbarSectionComponent, - ExpandableNavbarSectionComponent + ExpandableNavbarSectionComponent, + ThemedExpandableNavbarSectionComponent, ], providers: [ ], entryComponents: [ - NavbarSectionComponent, - ExpandableNavbarSectionComponent + ...ENTRY_COMPONENTS, ], exports: [ ThemedNavbarComponent, NavbarSectionComponent, - ExpandableNavbarSectionComponent + ThemedExpandableNavbarSectionComponent, ] }) From 22d5643d8b0276922bc6477db29283a6b5360993 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Tue, 3 May 2022 09:32:31 +0200 Subject: [PATCH 299/570] 90978: Set klaro cookie before each test --- cypress/support/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/support/index.ts b/cypress/support/index.ts index d9b6409a0d..024b46cdde 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -21,7 +21,7 @@ import './commands'; import 'cypress-axe'; // Runs once before the first test in each "block" -before(() => { +beforeEach(() => { // Pre-agree to all Klaro cookies by setting the klaro-anonymous cookie // This just ensures it doesn't get in the way of matching other objects in the page. cy.setCookie('klaro-anonymous', '{%22authentication%22:true%2C%22preferences%22:true%2C%22acknowledgement%22:true%2C%22google-analytics%22:true}'); From 32a91f64d9d8db83390589628c76882b212c968b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 3 May 2022 12:06:48 +0200 Subject: [PATCH 300/570] [CST-5535] Refactoring health page --- package.json | 1 - src/app/app-routing-paths.ts | 2 + src/app/app-routing.module.ts | 19 ++- .../health-info-component.component.html | 28 +++ .../health-info-component.component.scss | 0 .../health-info-component.component.spec.ts | 72 ++++++++ .../health-info-component.component.ts | 35 ++++ .../health-info/health-info.component.html | 7 + .../health-info/health-info.component.scss | 0 .../health-info/health-info.component.spec.ts | 37 ++++ .../health-info/health-info.component.ts | 14 ++ .../health-page/health-page.component.html | 21 +++ .../health-page/health-page.component.scss | 0 .../health-page/health-page.component.spec.ts | 72 ++++++++ src/app/health-page/health-page.component.ts | 41 +++++ src/app/health-page/health-page.module.ts | 35 ++++ ...odule.ts => health-page.routing.module.ts} | 19 +-- .../health-component.component.html | 27 +++ .../health-component.component.scss | 0 .../health-component.component.spec.ts | 77 +++++++++ .../health-component.component.ts | 27 +++ .../health-panel/health-panel.component.html | 21 +++ .../health-panel/health-panel.component.scss | 0 .../health-panel.component.spec.ts | 58 +++++++ .../health-panel/health-panel.component.ts | 21 +++ .../health-status.component.html | 6 + .../health-status.component.scss | 0 .../health-status.component.spec.ts | 48 ++++++ .../health-status/health-status.component.ts | 20 +++ src/app/health-page/health.module.ts | 23 --- .../health-page/health/health.component.html | 39 ----- .../health-page/health/health.component.scss | 8 - .../health/health.component.spec.ts | 160 ------------------ .../health-page/health/health.component.ts | 100 ----------- .../models/health-component.model.ts | 48 ++++++ src/app/shared/mocks/health-endpoint.mocks.ts | 140 +++++++++++++++ 36 files changed, 878 insertions(+), 348 deletions(-) create mode 100644 src/app/health-page/health-info/health-info-component/health-info-component.component.html create mode 100644 src/app/health-page/health-info/health-info-component/health-info-component.component.scss create mode 100644 src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts create mode 100644 src/app/health-page/health-info/health-info-component/health-info-component.component.ts create mode 100644 src/app/health-page/health-info/health-info.component.html create mode 100644 src/app/health-page/health-info/health-info.component.scss create mode 100644 src/app/health-page/health-info/health-info.component.spec.ts create mode 100644 src/app/health-page/health-info/health-info.component.ts create mode 100644 src/app/health-page/health-page.component.html create mode 100644 src/app/health-page/health-page.component.scss create mode 100644 src/app/health-page/health-page.component.spec.ts create mode 100644 src/app/health-page/health-page.component.ts create mode 100644 src/app/health-page/health-page.module.ts rename src/app/health-page/{health.routing.module.ts => health-page.routing.module.ts} (57%) create mode 100644 src/app/health-page/health-panel/health-component/health-component.component.html create mode 100644 src/app/health-page/health-panel/health-component/health-component.component.scss create mode 100644 src/app/health-page/health-panel/health-component/health-component.component.spec.ts create mode 100644 src/app/health-page/health-panel/health-component/health-component.component.ts create mode 100644 src/app/health-page/health-panel/health-panel.component.html create mode 100644 src/app/health-page/health-panel/health-panel.component.scss create mode 100644 src/app/health-page/health-panel/health-panel.component.spec.ts create mode 100644 src/app/health-page/health-panel/health-panel.component.ts create mode 100644 src/app/health-page/health-panel/health-status/health-status.component.html create mode 100644 src/app/health-page/health-panel/health-status/health-status.component.scss create mode 100644 src/app/health-page/health-panel/health-status/health-status.component.spec.ts create mode 100644 src/app/health-page/health-panel/health-status/health-status.component.ts delete mode 100644 src/app/health-page/health.module.ts delete mode 100644 src/app/health-page/health/health.component.html delete mode 100644 src/app/health-page/health/health.component.scss delete mode 100644 src/app/health-page/health/health.component.spec.ts delete mode 100644 src/app/health-page/health/health.component.ts create mode 100644 src/app/health-page/models/health-component.model.ts create mode 100644 src/app/shared/mocks/health-endpoint.mocks.ts diff --git a/package.json b/package.json index d6c234ef9e..75e22b40f3 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "@angular/core": "~13.2.6", "@angular/forms": "~13.2.6", "@angular/localize": "13.2.6", - "@angular/material": "13.3.5", "@angular/platform-browser": "~13.2.6", "@angular/platform-browser-dynamic": "~13.2.6", "@angular/platform-server": "~13.2.6", diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index 57767b6f3e..929cdbeaa2 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -116,3 +116,5 @@ export const REQUEST_COPY_MODULE_PATH = 'request-a-copy'; export function getRequestCopyModulePath() { return `/${REQUEST_COPY_MODULE_PATH}`; } + +export const HEALTH_PAGE_PATH = 'health'; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 80310774c8..243fe05f59 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -3,13 +3,16 @@ import { RouterModule } from '@angular/router'; import { AuthBlockingGuard } from './core/auth/auth-blocking.guard'; import { AuthenticatedGuard } from './core/auth/authenticated.guard'; -import { SiteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; +import { + SiteAdministratorGuard +} from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { ACCESS_CONTROL_MODULE_PATH, ADMIN_MODULE_PATH, BITSTREAM_MODULE_PATH, FORBIDDEN_PATH, FORGOT_PASSWORD_PATH, + HEALTH_PAGE_PATH, INFO_MODULE_PATH, INTERNAL_SERVER_ERROR, LEGACY_BITSTREAM_MODULE_PATH, @@ -27,8 +30,12 @@ import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end- import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; import { ThemedPageNotFoundComponent } from './pagenotfound/themed-pagenotfound.component'; import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component'; -import { GroupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; -import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component'; +import { + GroupAdministratorGuard +} from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; +import { + ThemedPageInternalServerErrorComponent +} from './page-internal-server-error/themed-page-internal-server-error.component'; import { ServerCheckGuard } from './core/server-check/server-check.guard'; @NgModule({ @@ -209,9 +216,9 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; .then((m) => m.StatisticsPageRoutingModule) }, { - path: 'health', - loadChildren: () => import('./health-page/health.module') - .then((m) => m.HealthModule) + path: HEALTH_PAGE_PATH, + loadChildren: () => import('./health-page/health-page.module') + .then((m) => m.HealthPageModule) }, { path: ACCESS_CONTROL_MODULE_PATH, diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.html b/src/app/health-page/health-info/health-info-component/health-info-component.component.html new file mode 100644 index 0000000000..55c1b3372f --- /dev/null +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.html @@ -0,0 +1,28 @@ + +
+
+ +
+ + +
+
+
+
+
+ +
+
+
+
+
+ +

{{ healthInfoComponentName | titlecase }} : {{healthInfoComponent}}

+
diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.scss b/src/app/health-page/health-info/health-info-component/health-info-component.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts b/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts new file mode 100644 index 0000000000..2297007cd5 --- /dev/null +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts @@ -0,0 +1,72 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { CommonModule } from '@angular/common'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; + +import { HealthInfoComponentComponent } from './health-info-component.component'; +import { HealthInfoComponentOne, HealthInfoComponentTwo } from '../../../shared/mocks/health-endpoint.mocks'; +import { ObjNgFor } from '../../../shared/utils/object-ngfor.pipe'; + +describe('HealthInfoComponentComponent', () => { + let component: HealthInfoComponentComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbCollapseModule, + NoopAnimationsModule + ], + declarations: [ + HealthInfoComponentComponent, + ObjNgFor + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthInfoComponentComponent); + component = fixture.componentInstance; + }); + + describe('when has nested components', () => { + beforeEach(() => { + component.healthInfoComponentName = 'App'; + component.healthInfoComponent = HealthInfoComponentOne; + component.isCollapsed = false; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display property', () => { + const components = fixture.debugElement.queryAll(By.css('[data-test="component"]')); + expect(components.length).toBe(4); + }); + + }); + + describe('when has plain properties', () => { + beforeEach(() => { + component.healthInfoComponentName = 'Java'; + component.healthInfoComponent = HealthInfoComponentTwo; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display property', () => { + const property = fixture.debugElement.queryAll(By.css('[data-test="property"]')); + expect(property.length).toBe(1); + }); + + }); +}); diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.ts b/src/app/health-page/health-info/health-info-component/health-info-component.component.ts new file mode 100644 index 0000000000..b6c31214c8 --- /dev/null +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.ts @@ -0,0 +1,35 @@ +import { Component, Input } from '@angular/core'; + +import { HealthInfoComponent } from '../../models/health-component.model'; + +@Component({ + selector: 'ds-health-info-component', + templateUrl: './health-info-component.component.html', + styleUrls: ['./health-info-component.component.scss'] +}) +export class HealthInfoComponentComponent { + + /** + * The HealthInfoComponent object to display + */ + @Input() healthInfoComponent: HealthInfoComponent|string; + + /** + * The HealthInfoComponent object name + */ + @Input() healthInfoComponentName: string; + + /** + * A boolean representing if div should start collapsed + */ + @Input() isNested = false; + + /** + * A boolean representing if div should start collapsed + */ + public isCollapsed = true; + + isPlainProperty(entry: HealthInfoComponent | string): boolean { + return typeof entry === 'string'; + } +} diff --git a/src/app/health-page/health-info/health-info.component.html b/src/app/health-page/health-info/health-info.component.html new file mode 100644 index 0000000000..e4d29adf54 --- /dev/null +++ b/src/app/health-page/health-info/health-info.component.html @@ -0,0 +1,7 @@ + +
+ +
+
diff --git a/src/app/health-page/health-info/health-info.component.scss b/src/app/health-page/health-info/health-info.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/health-page/health-info/health-info.component.spec.ts b/src/app/health-page/health-info/health-info.component.spec.ts new file mode 100644 index 0000000000..3af1d71db3 --- /dev/null +++ b/src/app/health-page/health-info/health-info.component.spec.ts @@ -0,0 +1,37 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HealthInfoComponent } from './health-info.component'; +import { HealthInfoResponseObj } from '../../shared/mocks/health-endpoint.mocks'; +import { ObjNgFor } from '../../shared/utils/object-ngfor.pipe'; +import { By } from '@angular/platform-browser'; + +describe('HealthInfoComponent', () => { + let component: HealthInfoComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + HealthInfoComponent, + ObjNgFor + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthInfoComponent); + component = fixture.componentInstance; + component.healthInfoResponse = HealthInfoResponseObj; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should create info component properly', () => { + const components = fixture.debugElement.queryAll(By.css('[data-test="info-component"]')); + expect(components.length).toBe(3); + }); +}); diff --git a/src/app/health-page/health-info/health-info.component.ts b/src/app/health-page/health-info/health-info.component.ts new file mode 100644 index 0000000000..a5fb0b282b --- /dev/null +++ b/src/app/health-page/health-info/health-info.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core'; + +import { HealthInfoResponse } from '../models/health-component.model'; + +@Component({ + selector: 'ds-health-info', + templateUrl: './health-info.component.html', + styleUrls: ['./health-info.component.scss'] +}) +export class HealthInfoComponent { + + @Input() healthInfoResponse: HealthInfoResponse; + +} diff --git a/src/app/health-page/health-page.component.html b/src/app/health-page/health-page.component.html new file mode 100644 index 0000000000..6ec9abddcb --- /dev/null +++ b/src/app/health-page/health-page.component.html @@ -0,0 +1,21 @@ + diff --git a/src/app/health-page/health-page.component.scss b/src/app/health-page/health-page.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/health-page/health-page.component.spec.ts b/src/app/health-page/health-page.component.spec.ts new file mode 100644 index 0000000000..205af8036a --- /dev/null +++ b/src/app/health-page/health-page.component.spec.ts @@ -0,0 +1,72 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { CommonModule } from '@angular/common'; +import { By } from '@angular/platform-browser'; + +import { of } from 'rxjs'; +import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { HealthPageComponent } from './health-page.component'; +import { HealthDataService } from './health-data.service'; +import { HealthInfoResponseObj, HealthResponseObj } from '../shared/mocks/health-endpoint.mocks'; +import { RawRestResponse } from '../core/dspace-rest/raw-rest-response.model'; +import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; + +describe('HealthPageComponent', () => { + let component: HealthPageComponent; + let fixture: ComponentFixture; + + const healthService = jasmine.createSpyObj('healthDataService', { + getHealth: jasmine.createSpy('getHealth'), + getInfo: jasmine.createSpy('getInfo'), + }); + + const healthRestResponse$ = of({ + payload: HealthResponseObj, + statusCode: 200, + statusText: 'OK' + } as RawRestResponse); + + const healthInfoRestResponse$ = of({ + payload: HealthInfoResponseObj, + statusCode: 200, + statusText: 'OK' + } as RawRestResponse); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbNavModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }) + ], + declarations: [ HealthPageComponent ], + providers: [ + { provide: HealthDataService, useValue: healthService } + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthPageComponent); + component = fixture.componentInstance; + healthService.getHealth.and.returnValue(healthRestResponse$); + healthService.getInfo.and.returnValue(healthInfoRestResponse$); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should create nav items properly', () => { + const navItems = fixture.debugElement.queryAll(By.css('li.nav-item')); + expect(navItems.length).toBe(2); + }); +}); diff --git a/src/app/health-page/health-page.component.ts b/src/app/health-page/health-page.component.ts new file mode 100644 index 0000000000..e4f4be7a03 --- /dev/null +++ b/src/app/health-page/health-page.component.ts @@ -0,0 +1,41 @@ +import { Component, OnInit } from '@angular/core'; + +import { BehaviorSubject } from 'rxjs'; +import { take } from 'rxjs/operators'; + +import { HealthDataService } from './health-data.service'; +import { HealthInfoResponse, HealthResponse } from './models/health-component.model'; + +@Component({ + selector: 'ds-health-page', + templateUrl: './health-page.component.html', + styleUrls: ['./health-page.component.scss'] +}) +export class HealthPageComponent implements OnInit { + + /** + * Health info endpoint response + */ + healthInfoResponse: BehaviorSubject = new BehaviorSubject(null); + + /** + * Health endpoint response + */ + healthResponse: BehaviorSubject = new BehaviorSubject(null); + + constructor(private healthDataService: HealthDataService) { + } + + /** + * Retrieve responses from rest + */ + ngOnInit(): void { + this.healthDataService.getHealth().pipe(take(1)).subscribe((data: any) => { + this.healthResponse.next(data.payload); + }); + + this.healthDataService.getInfo().pipe(take(1)).subscribe((data) => { + this.healthInfoResponse.next(data.payload); + }); + } +} diff --git a/src/app/health-page/health-page.module.ts b/src/app/health-page/health-page.module.ts new file mode 100644 index 0000000000..02a6a91a5f --- /dev/null +++ b/src/app/health-page/health-page.module.ts @@ -0,0 +1,35 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateModule } from '@ngx-translate/core'; + +import { HealthPageRoutingModule } from './health-page.routing.module'; +import { HealthPanelComponent } from './health-panel/health-panel.component'; +import { HealthStatusComponent } from './health-panel/health-status/health-status.component'; +import { SharedModule } from '../shared/shared.module'; +import { HealthPageComponent } from './health-page.component'; +import { HealthComponentComponent } from './health-panel/health-component/health-component.component'; +import { HealthInfoComponent } from './health-info/health-info.component'; +import { HealthInfoComponentComponent } from './health-info/health-info-component/health-info-component.component'; + + +@NgModule({ + imports: [ + CommonModule, + HealthPageRoutingModule, + NgbModule, + SharedModule, + TranslateModule + ], + declarations: [ + HealthPageComponent, + HealthPanelComponent, + HealthStatusComponent, + HealthComponentComponent, + HealthInfoComponent, + HealthInfoComponentComponent, + ] +}) +export class HealthPageModule { +} diff --git a/src/app/health-page/health.routing.module.ts b/src/app/health-page/health-page.routing.module.ts similarity index 57% rename from src/app/health-page/health.routing.module.ts rename to src/app/health-page/health-page.routing.module.ts index a8d94d9d1f..37c8b626eb 100644 --- a/src/app/health-page/health.routing.module.ts +++ b/src/app/health-page/health-page.routing.module.ts @@ -1,8 +1,11 @@ import { RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; -import { HealthComponent } from './health/health.component'; + import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { HealthPageComponent } from './health-page.component'; +import { + SiteAdministratorGuard +} from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; @NgModule({ imports: [ @@ -11,15 +14,9 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso path: '', resolve: { breadcrumb: I18nBreadcrumbResolver }, data: { breadcrumbKey: 'health' }, - canActivate: [AuthenticatedGuard], - children: [ - { - path: '', - component: HealthComponent, - }, - ] - }, - + canActivate: [SiteAdministratorGuard], + component: HealthPageComponent + } ]) ] }) diff --git a/src/app/health-page/health-panel/health-component/health-component.component.html b/src/app/health-page/health-panel/health-component/health-component.component.html new file mode 100644 index 0000000000..8171917767 --- /dev/null +++ b/src/app/health-page/health-panel/health-component/health-component.component.html @@ -0,0 +1,27 @@ + +
+
+ +
+ + +
+
+
+
+
+ +
+
+
+
+
+ +
+ {{ item.key | titlecase }} : {{item.value}} +
+
diff --git a/src/app/health-page/health-panel/health-component/health-component.component.scss b/src/app/health-page/health-panel/health-component/health-component.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/health-page/health-panel/health-component/health-component.component.spec.ts b/src/app/health-page/health-panel/health-component/health-component.component.spec.ts new file mode 100644 index 0000000000..149d504c23 --- /dev/null +++ b/src/app/health-page/health-panel/health-component/health-component.component.spec.ts @@ -0,0 +1,77 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { CommonModule } from '@angular/common'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; + +import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; + +import { HealthComponentComponent } from './health-component.component'; +import { HealthComponentOne, HealthComponentTwo } from '../../../shared/mocks/health-endpoint.mocks'; +import { ObjNgFor } from '../../../shared/utils/object-ngfor.pipe'; + +describe('HealthComponentComponent', () => { + let component: HealthComponentComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbCollapseModule, + NoopAnimationsModule + ], + declarations: [ + HealthComponentComponent, + ObjNgFor + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthComponentComponent); + component = fixture.componentInstance; + }); + + describe('when has nested components', () => { + beforeEach(() => { + component.healthComponentName = 'db'; + component.healthComponent = HealthComponentOne; + component.isCollapsed = false; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should create collapsible divs properly', () => { + const collapseDivs = fixture.debugElement.queryAll(By.css('[data-test="collapse"]')); + expect(collapseDivs.length).toBe(2); + const detailsDivs = fixture.debugElement.queryAll(By.css('[data-test="details"]')); + expect(detailsDivs.length).toBe(6); + }); + }); + + describe('when has details', () => { + beforeEach(() => { + component.healthComponentName = 'geoIp'; + component.healthComponent = HealthComponentTwo; + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should create detail divs properly', () => { + const detailsDivs = fixture.debugElement.queryAll(By.css('[data-test="details"]')); + expect(detailsDivs.length).toBe(1); + const collapseDivs = fixture.debugElement.queryAll(By.css('[data-test="collapse"]')); + expect(collapseDivs.length).toBe(0); + }); + }); +}); diff --git a/src/app/health-page/health-panel/health-component/health-component.component.ts b/src/app/health-page/health-panel/health-component/health-component.component.ts new file mode 100644 index 0000000000..5ad40c9469 --- /dev/null +++ b/src/app/health-page/health-panel/health-component/health-component.component.ts @@ -0,0 +1,27 @@ +import { Component, Input } from '@angular/core'; + +import { HealthComponent } from '../../models/health-component.model'; + +@Component({ + selector: 'ds-health-component', + templateUrl: './health-component.component.html', + styleUrls: ['./health-component.component.scss'] +}) +export class HealthComponentComponent { + + /** + * The HealthComponent object to display + */ + @Input() healthComponent: HealthComponent; + + /** + * The HealthComponent object name + */ + @Input() healthComponentName: string; + + /** + * A boolean representing if div should start collapsed + */ + public isCollapsed = true; + +} diff --git a/src/app/health-page/health-panel/health-panel.component.html b/src/app/health-page/health-panel/health-panel.component.html new file mode 100644 index 0000000000..d582fb77f3 --- /dev/null +++ b/src/app/health-page/health-panel/health-panel.component.html @@ -0,0 +1,21 @@ +

{{'health-page.status' | translate}} :

+
+
+ +
+ + + +
+
+
+
+
+ +
+
+
+
diff --git a/src/app/health-page/health-panel/health-panel.component.scss b/src/app/health-page/health-panel/health-panel.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/health-page/health-panel/health-panel.component.spec.ts b/src/app/health-page/health-panel/health-panel.component.spec.ts new file mode 100644 index 0000000000..da392f7ba8 --- /dev/null +++ b/src/app/health-page/health-panel/health-panel.component.spec.ts @@ -0,0 +1,58 @@ +import { CommonModule } from '@angular/common'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; + +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { NgbCollapseModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; + +import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; +import { HealthPanelComponent } from './health-panel.component'; +import { HealthResponseObj } from '../../shared/mocks/health-endpoint.mocks'; +import { ObjNgFor } from '../../shared/utils/object-ngfor.pipe'; + +describe('HealthComponent', () => { + let component: HealthPanelComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + NgbNavModule, + NgbCollapseModule, + CommonModule, + BrowserAnimationsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + declarations: [ + HealthPanelComponent, + ObjNgFor + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthPanelComponent); + component = fixture.componentInstance; + component.healthResponse = HealthResponseObj; + component.isCollapsed = false; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should render a card for each component', () => { + const components = fixture.debugElement.queryAll(By.css('[data-test="component"]')); + expect(components.length).toBe(5); + }); + +}); diff --git a/src/app/health-page/health-panel/health-panel.component.ts b/src/app/health-page/health-panel/health-panel.component.ts new file mode 100644 index 0000000000..549544c370 --- /dev/null +++ b/src/app/health-page/health-panel/health-panel.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from '@angular/core'; +import { HealthResponse } from '../models/health-component.model'; + +@Component({ + selector: 'ds-health-panel', + templateUrl: './health-panel.component.html', + styleUrls: ['./health-panel.component.scss'] +}) +export class HealthPanelComponent { + + /** + * Health endpoint response + */ + @Input() healthResponse: HealthResponse; + + /** + * A boolean representing if div should start collapsed + */ + public isCollapsed = true; + +} diff --git a/src/app/health-page/health-panel/health-status/health-status.component.html b/src/app/health-page/health-panel/health-status/health-status.component.html new file mode 100644 index 0000000000..fdd726cddf --- /dev/null +++ b/src/app/health-page/health-panel/health-status/health-status.component.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/app/health-page/health-panel/health-status/health-status.component.scss b/src/app/health-page/health-panel/health-status/health-status.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/health-page/health-panel/health-status/health-status.component.spec.ts b/src/app/health-page/health-panel/health-status/health-status.component.spec.ts new file mode 100644 index 0000000000..13df9c23e3 --- /dev/null +++ b/src/app/health-page/health-panel/health-status/health-status.component.spec.ts @@ -0,0 +1,48 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; + +import { HealthStatusComponent } from './health-status.component'; +import { HealthStatus } from '../../models/health-component.model'; + +describe('HealthStatusComponent', () => { + let component: HealthStatusComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HealthStatusComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should create success icon', () => { + component.status = HealthStatus.UP; + fixture.detectChanges(); + const icon = fixture.debugElement.query(By.css('i.text-success')); + expect(icon).toBeTruthy(); + }); + + it('should create warning icon', () => { + component.status = HealthStatus.UP_WITH_ISSUES; + fixture.detectChanges(); + const icon = fixture.debugElement.query(By.css('i.text-warning')); + expect(icon).toBeTruthy(); + }); + + it('should create success icon', () => { + component.status = HealthStatus.DOWN; + fixture.detectChanges(); + const icon = fixture.debugElement.query(By.css('i.text-danger')); + expect(icon).toBeTruthy(); + }); +}); diff --git a/src/app/health-page/health-panel/health-status/health-status.component.ts b/src/app/health-page/health-panel/health-status/health-status.component.ts new file mode 100644 index 0000000000..9285483a97 --- /dev/null +++ b/src/app/health-page/health-panel/health-status/health-status.component.ts @@ -0,0 +1,20 @@ +import { Component, Input } from '@angular/core'; +import { HealthStatus } from '../../models/health-component.model'; + +@Component({ + selector: 'ds-health-status', + templateUrl: './health-status.component.html', + styleUrls: ['./health-status.component.scss'] +}) +export class HealthStatusComponent { + /** + * The current status to show + */ + @Input() status: HealthStatus; + + /** + * He + */ + HealthStatus = HealthStatus; + +} diff --git a/src/app/health-page/health.module.ts b/src/app/health-page/health.module.ts deleted file mode 100644 index 8731b77f77..0000000000 --- a/src/app/health-page/health.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { HealthPageRoutingModule } from './health.routing.module'; -import { HealthComponent } from './health/health.component'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { TranslateModule } from '@ngx-translate/core'; - - -@NgModule({ - imports: [ - CommonModule, - HealthPageRoutingModule, - MatExpansionModule, - NgbModule, - TranslateModule - ], - declarations: [ - HealthComponent - ] - }) - export class HealthModule { - } diff --git a/src/app/health-page/health/health.component.html b/src/app/health-page/health/health.component.html deleted file mode 100644 index 05f77225fb..0000000000 --- a/src/app/health-page/health/health.component.html +++ /dev/null @@ -1,39 +0,0 @@ -
- -
-
- - - - diff --git a/src/app/health-page/health/health.component.scss b/src/app/health-page/health/health.component.scss deleted file mode 100644 index c085111079..0000000000 --- a/src/app/health-page/health/health.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -.mat-expansion-panel-header { - padding-left: 0px; -} - -.circle-red { - color:red; - align-items: center; -} \ No newline at end of file diff --git a/src/app/health-page/health/health.component.spec.ts b/src/app/health-page/health/health.component.spec.ts deleted file mode 100644 index 4515aef2cb..0000000000 --- a/src/app/health-page/health/health.component.spec.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { By } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { of } from 'rxjs'; -import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; -import { HealthDataService } from '../health-data.service'; -import { HealthPageRoutingModule } from '../health.routing.module'; -import { HealthComponent } from './health.component'; - - function getHealth() { - return of({ - 'payload':{ - 'status':'UP_WITH_ISSUES', - 'components':{ - 'db':{ - 'status':'UP', - 'components':{ - 'dataSource':{ - 'status':'UP', - 'details':{ - 'database':'PostgreSQL', - 'result':1, - 'validationQuery':'SELECT 1' - } - }, - 'dspaceDataSource':{ - 'status':'UP', - 'details':{ - 'database':'PostgreSQL', - 'result':1, - 'validationQuery':'SELECT 1' - } - } - } - }, - 'geoIp':{ - 'status':'UP_WITH_ISSUES', - 'details':{ - 'reason':'The GeoLite Database file is missing (/var/lib/GeoIP/GeoLite2-City.mmdb)! Solr Statistics cannot generate location based reports! Please see the DSpace installation instructions for instructions to install this file.' - } - }, - 'solrOaiCore':{ - 'status':'UP', - 'details':{ - 'status':0, - 'detectedPathType':'particular core' - } - }, - 'solrSearchCore':{ - 'status':'UP', - 'details':{ - 'status':0, - 'detectedPathType':'particular core' - } - }, - 'solrStatisticsCore':{ - 'status':'UP', - 'details':{ - 'status':0, - 'detectedPathType':'particular core' - } - } - } - }, - 'statusCode':200, - 'statusText':'OK' - }); - } - - function getInfo() { - return of({ - 'payload':{ - 'app':{ - 'name':'DSpace at My University', - 'version':'7.3', - 'dir':'/Users/pratikrajkotiya/Documents/Project/FrontEnd/dspace-cris-install', - 'url':'http://localhost:8080/server', - 'db':'jdbc:postgresql://localhost:5432/4science', - 'solr':{ - 'server':'http://localhost:8983/solr', - 'prefix':'' - }, - 'mail':{ - 'server':'smtp.example.com', - 'from-address':'dspace-noreply@myu.edu', - 'feedback-recipient':'dspace-help@myu.edu', - 'mail-admin':'dspace-help@myu.edu', - 'mail-helpdesk':'dspace-help@myu.edu', - 'alert-recipient':'dspace-help@myu.edu' - }, - 'cors':{ - 'allowed-origins':'http://localhost:4000' - }, - 'ui':{ - 'url':'http://localhost:4000' - } - } - }, - 'statusCode':200, - 'statusText':'OK' - }); - } - -function getMockHealthDataService() { - return jasmine.createSpyObj('healthDataService', { - getHealth: getHealth(), - getInfo: getInfo() - }); -} - -describe('HealthComponent', () => { - let component: HealthComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - NgbNavModule, - CommonModule, - HealthPageRoutingModule, - MatExpansionModule, - BrowserAnimationsModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - ], - declarations: [ HealthComponent ], - providers:[{ provide: HealthDataService, useValue: getMockHealthDataService() }] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(HealthComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should render health tab.', () => { - const healthTab = fixture.debugElement.query(By.css('#health')); - expect(healthTab).toBeTruthy(); - }); - - it('should render info tab.', () => { - const infoTab = fixture.debugElement.query(By.css('#info')); - expect(infoTab).toBeFalsy(); - }); - -}); diff --git a/src/app/health-page/health/health.component.ts b/src/app/health-page/health/health.component.ts deleted file mode 100644 index 89aa6611f6..0000000000 --- a/src/app/health-page/health/health.component.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { HealthDataService } from '../health-data.service'; - - -enum HealthStatus { - UP = 'UP', - UP_WITH_ISSUES = 'UP_WITH_ISSUES', - DOWN = 'DOWN' -} -@Component({ - selector: 'ds-health', - templateUrl: './health.component.html', - styleUrls: ['./health.component.scss'] -}) -export class HealthComponent implements OnInit { - healthArr: string[]; - serverInfoArr: string[]; - healthGlobalStatus: string; - activeId ='Health'; - constructor(private healthDataService: HealthDataService) { } - - ngOnInit(): void { - this.healthDataService.getHealth().subscribe((data) => { - this.healthArr = this.getHealth(data.payload.components); - this.healthGlobalStatus = data.payload.status; - }); - - this.healthDataService.getInfo().subscribe((data) => { - this.serverInfoArr = this.getInfo(data.payload, null, []); - }); - } - - /** - * @param obj represents a info - * @param key represents a nested key of info - * @param arr represents a key value pair or only key - * @returns {{arr}} of key value pair or only key - */ - getInfo(obj, key, arr) { - if (typeof obj === 'object' && key !== null) { - arr.push({style: {'font-weight': 'bold' ,'font-size.px': key === 'app' ? '30' : '20' }, value: key}); - } - if (typeof obj !== 'object') { - arr.push({style: {'font-size.px': '15'}, value: `${key} = ${obj}`}); - return obj; - } - // tslint:disable-next-line: forin - for (const objKey in obj) { - this.getInfo(obj[objKey], objKey, arr); - } - return arr; - } - - /** - * @param subCompObj represent nested sub component - * @param superCompkey represents a key of super component - * @returns linear components array - */ - getHealthSubComponents(subCompObj, superCompkey) { - const subCompArr = []; - // tslint:disable-next-line: forin - for (const key in subCompObj) { - subCompArr.push({ ...subCompObj[key], components: superCompkey + '.' + key }); - } - return subCompArr; - } - - /** - * @param componentsObj represent health data - * @returns linear components array - */ - getHealth(componentsObj) { - let componentsArr = []; - for (const key in componentsObj) { - if (componentsObj[key].hasOwnProperty('components')) { - componentsArr.push({ ...componentsObj[key], components: key }); - // tslint:disable-next-line: no-string-literal - componentsArr = [...componentsArr, ...this.getHealthSubComponents(componentsObj[key]['components'], key)]; - } else { - componentsArr.push({ ...componentsObj[key], components: key }); - } - } - return componentsArr; - } - - /** - * @param status of perticular block - * @returns {{ string }} class respective status - */ - getHealthIconClass(status: string): string { - if (status === HealthStatus.UP) { - return 'fa fa-check-circle text-success ml-2 mt-1'; - } else if (status === HealthStatus.UP_WITH_ISSUES) { - return 'fa fa-exclamation-triangle text-warning ml-2 mt-1'; - } else { - return 'fa fa-times-circle circle-red ml-2 mt-1'; - } - } - -} diff --git a/src/app/health-page/models/health-component.model.ts b/src/app/health-page/models/health-component.model.ts new file mode 100644 index 0000000000..8461d4d967 --- /dev/null +++ b/src/app/health-page/models/health-component.model.ts @@ -0,0 +1,48 @@ +/** + * Interface for Health Status + */ +export enum HealthStatus { + UP = 'UP', + UP_WITH_ISSUES = 'UP_WITH_ISSUES', + DOWN = 'DOWN' +} + +/** + * Interface describing the Health endpoint response + */ +export interface HealthResponse { + status: HealthStatus; + components: { + [name: string]: HealthComponent; + }; +} + +/** + * Interface describing a single component retrieved from the Health endpoint response + */ +export interface HealthComponent { + status: HealthStatus; + details?: { + [name: string]: number|string; + }; + components?: { + [name: string]: HealthComponent; + }; +} + +/** + * Interface describing the Health info endpoint response + */ +export interface HealthInfoResponse { + [name: string]: HealthInfoComponent|string; +} + +/** + * Interface describing a single component retrieved from the Health info endpoint response + */ +export interface HealthInfoComponent { + [property: string]: HealthInfoComponent|string; +} + + + diff --git a/src/app/shared/mocks/health-endpoint.mocks.ts b/src/app/shared/mocks/health-endpoint.mocks.ts new file mode 100644 index 0000000000..9bd3956139 --- /dev/null +++ b/src/app/shared/mocks/health-endpoint.mocks.ts @@ -0,0 +1,140 @@ +import { + HealthComponent, + HealthInfoComponent, + HealthInfoResponse, + HealthResponse, + HealthStatus +} from '../../health-page/models/health-component.model'; + +export const HealthResponseObj: HealthResponse = { + 'status': HealthStatus.UP_WITH_ISSUES, + 'components': { + 'db': { + 'status': HealthStatus.UP, + 'components': { + 'dataSource': { + 'status': HealthStatus.UP, + 'details': { + 'database': 'PostgreSQL', + 'result': 1, + 'validationQuery': 'SELECT 1' + } + }, + 'dspaceDataSource': { + 'status': HealthStatus.UP, + 'details': { + 'database': 'PostgreSQL', + 'result': 1, + 'validationQuery': 'SELECT 1' + } + } + } + }, + 'geoIp': { + 'status': HealthStatus.UP_WITH_ISSUES, + 'details': { + 'reason': 'The GeoLite Database file is missing (/var/lib/GeoIP/GeoLite2-City.mmdb)! Solr Statistics cannot generate location based reports! Please see the DSpace installation instructions for instructions to install this file.' + } + }, + 'solrOaiCore': { + 'status': HealthStatus.UP, + 'details': { + 'status': 0, + 'detectedPathType': 'particular core' + } + }, + 'solrSearchCore': { + 'status': HealthStatus.UP, + 'details': { + 'status': 0, + 'detectedPathType': 'particular core' + } + }, + 'solrStatisticsCore': { + 'status': HealthStatus.UP, + 'details': { + 'status': 0, + 'detectedPathType': 'particular core' + } + } + } +}; + +export const HealthComponentOne: HealthComponent = { + 'status': HealthStatus.UP, + 'components': { + 'dataSource': { + 'status': HealthStatus.UP, + 'details': { + 'database': 'PostgreSQL', + 'result': 1, + 'validationQuery': 'SELECT 1' + } + }, + 'dspaceDataSource': { + 'status': HealthStatus.UP, + 'details': { + 'database': 'PostgreSQL', + 'result': 1, + 'validationQuery': 'SELECT 1' + } + } + } +}; + +export const HealthComponentTwo: HealthComponent = { + 'status': HealthStatus.UP_WITH_ISSUES, + 'details': { + 'reason': 'The GeoLite Database file is missing (/var/lib/GeoIP/GeoLite2-City.mmdb)! Solr Statistics cannot generate location based reports! Please see the DSpace installation instructions for instructions to install this file.' + } +}; + +export const HealthInfoResponseObj: HealthInfoResponse = { + 'app': { + 'name': 'DSpace at My University', + 'dir': '/home/giuseppe/development/java/install/dspace7-review', + 'url': 'http://localhost:8080/server', + 'db': 'jdbc:postgresql://localhost:5432/dspace7', + 'solr': { + 'server': 'http://localhost:8983/solr', + 'prefix': '' + }, + 'mail': { + 'server': 'smtp.example.com', + 'from-address': 'dspace-noreply@myu.edu', + 'feedback-recipient': 'dspace-help@myu.edu', + 'mail-admin': 'dspace-help@myu.edu', + 'mail-helpdesk': 'dspace-help@myu.edu', + 'alert-recipient': 'dspace-help@myu.edu' + }, + 'cors': { + 'allowed-origins': 'http://localhost:4000' + }, + 'ui': { + 'url': 'http://localhost:4000' + } + }, + 'java': { + 'vendor': 'Private Build', + 'version': '11.0.15', + 'runtime': { + 'name': 'OpenJDK Runtime Environment', + 'version': '11.0.15+10-Ubuntu-0ubuntu0.20.04.1' + }, + 'jvm': { + 'name': 'OpenJDK 64-Bit Server VM', + 'vendor': 'Private Build', + 'version': '11.0.15+10-Ubuntu-0ubuntu0.20.04.1' + } + }, + 'version': '7.3-SNAPSHOT' +}; + +export const HealthInfoComponentOne: HealthInfoComponent = { + 'name': 'DSpace at My University', + 'dir': '/home/giuseppe/development/java/install/dspace7-review', + 'url': 'http://localhost:8080/server', + 'db': 'jdbc:postgresql://localhost:5432/dspace7' +}; + +export const HealthInfoComponentTwo = '7.3-SNAPSHOT'; From 3c2f26f6c18bbf379e9b0be64f713a91cada110f Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 3 May 2022 15:30:20 +0200 Subject: [PATCH 301/570] [CST-5535] Add environment configuration for Actuators --- src/config/actuators.config.ts | 11 +++++++++++ src/config/app-config.interface.ts | 2 ++ src/config/default-app-config.ts | 5 +++++ src/environments/environment.test.ts | 4 ++++ 4 files changed, 22 insertions(+) create mode 100644 src/config/actuators.config.ts diff --git a/src/config/actuators.config.ts b/src/config/actuators.config.ts new file mode 100644 index 0000000000..8f59a13c98 --- /dev/null +++ b/src/config/actuators.config.ts @@ -0,0 +1,11 @@ +import { Config } from './config.interface'; + +/** + * Config that determines the spring Actuators options + */ +export class ActuatorsConfig implements Config { + /** + * The endpoint path + */ + public endpointPath: string; +} diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index a41ec05b82..e8bda53373 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -15,6 +15,7 @@ import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; import { BundleConfig } from './bundle-config.interface'; +import { ActuatorsConfig } from './actuators.config'; interface AppConfig extends Config { ui: UIServerConfig; @@ -34,6 +35,7 @@ interface AppConfig extends Config { themes: ThemeConfig[]; mediaViewer: MediaViewerConfig; bundle: BundleConfig; + actuators: ActuatorsConfig } const APP_CONFIG = new InjectionToken('APP_CONFIG'); diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index a7360bd1d1..27950f5269 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -15,6 +15,7 @@ import { SubmissionConfig } from './submission-config.interface'; import { ThemeConfig } from './theme.model'; import { UIServerConfig } from './ui-server-config.interface'; import { BundleConfig } from './bundle-config.interface'; +import { ActuatorsConfig } from './actuators.config'; export class DefaultAppConfig implements AppConfig { production = false; @@ -48,6 +49,10 @@ export class DefaultAppConfig implements AppConfig { nameSpace: '/', }; + actuators: ActuatorsConfig = { + endpointPath: '/actuator/health' + }; + // Caching settings cache: CacheConfig = { // NOTE: how long should objects be cached for by default diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index bc1622f5f2..d2f2ad7426 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -38,6 +38,10 @@ export const environment: BuildConfig = { baseUrl: 'https://rest.com/api' }, + actuators: { + endpointPath: '/actuator/health' + }, + // Caching settings cache: { // NOTE: how long should objects be cached for by default From 675f3910ccb23a2eb2376d61af067a018db59e72 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 3 May 2022 15:32:02 +0200 Subject: [PATCH 302/570] [CST-5535] Refactoring health check server side request --- package.json | 1 + server.ts | 31 +++++++++++++++++++------------ yarn.lock | 10 +++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 75e22b40f3..14e2436ccd 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@nicky-lenaers/ngx-scroll-to": "^9.0.0", "angular-idle-preload": "3.0.0", "angulartics2": "^10.0.0", + "axios": "^0.27.2", "bootstrap": "4.3.1", "caniuse-lite": "^1.0.30001165", "cerialize": "0.1.18", diff --git a/server.ts b/server.ts index 7f8bb9bbdb..14ce33ed36 100644 --- a/server.ts +++ b/server.ts @@ -19,6 +19,7 @@ import 'zone.js/node'; import 'reflect-metadata'; import 'rxjs'; +import axios from 'axios'; import * as pem from 'pem'; import * as https from 'https'; import * as morgan from 'morgan'; @@ -37,14 +38,14 @@ import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'; import { environment } from './src/environments/environment'; import { createProxyMiddleware } from 'http-proxy-middleware'; -import { hasValue, hasNoValue } from './src/app/shared/empty.util'; +import { hasNoValue, hasValue } from './src/app/shared/empty.util'; import { UIServerConfig } from './src/config/ui-server-config.interface'; import { ServerAppModule } from './src/main.server'; import { buildAppConfig } from './src/config/config.server'; -import { AppConfig, APP_CONFIG } from './src/config/app-config.interface'; +import { APP_CONFIG, AppConfig } from './src/config/app-config.interface'; import { extendEnvironmentWithAppConfig } from './src/config/config.util'; /* @@ -160,16 +161,7 @@ export function app() { /** * Checking server status */ - server.get('/app/health', async (req,res) => { - try { - const serverStatus = await https.get(`${environment.rest.baseUrl}/actuator/health`); - res.send(serverStatus); - } catch (error) { - res.send({ - error: error.message - }); - } - }); + server.get('/app/health', healthCheck); // Register the ngApp callback function to handle incoming requests server.get('*', ngApp); @@ -301,6 +293,21 @@ function start() { } } +/* + * The callback function to serve health check requests + */ +function healthCheck(req, res) { + const baseUrl = `${environment.rest.baseUrl}${environment.actuators.endpointPath}`; + axios.get(baseUrl) + .then((response) => { + res.status(response.status).send(response.data); + }) + .catch((error) => { + res.status(error.response.status).send({ + error: error.message + }); + }); +} // Webpack will replace 'require' with '__webpack_require__' // '__non_webpack_require__' is a proxy to Node 'require' // The below code is to ensure that the server is run only when not requiring the bundle. diff --git a/yarn.lock b/yarn.lock index 00c93618d9..a612a985d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3226,6 +3226,14 @@ axios@0.21.4: dependencies: follow-redirects "^1.14.0" +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + axobject-query@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" @@ -6108,7 +6116,7 @@ flatted@^3.1.0, flatted@^3.2.5: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== -follow-redirects@^1.0.0, follow-redirects@^1.14.0: +follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.9: version "1.14.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== From 804930fbe2b5cca46343d24bf7486034cf5cc4f8 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Tue, 3 May 2022 15:35:51 +0200 Subject: [PATCH 303/570] [CST-5307] Improved researcher profile component --- ...on-search-result-list-element.component.ts | 15 ++- .../item-pages/person/person.component.html | 3 +- .../person/person.component.spec.ts | 7 +- .../item-pages/person/person.component.ts | 93 ++++++++++++++++++- .../full/full-item-page.component.html | 3 +- .../full/full-item-page.component.ts | 9 +- .../item-page/simple/item-page.component.ts | 63 +------------ .../item-types/shared/item.component.spec.ts | 13 ++- ...rofile-page-researcher-form.component.html | 4 +- .../profile-page/profile-page.component.html | 17 ++-- .../profile-page.component.spec.ts | 10 ++ .../profile-page/profile-page.component.ts | 19 +++- 12 files changed, 162 insertions(+), 94 deletions(-) diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts index a6f64bff1b..32785d7b8a 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts @@ -2,6 +2,9 @@ import { Component } from '@angular/core'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; +import {TruncatableService} from '../../../../../shared/truncatable/truncatable.service'; +import {DSONameService} from '../../../../../core/breadcrumbs/dso-name.service'; +import {isNotEmpty} from '../../../../../shared/empty.util'; @listableObjectComponent('PersonSearchResult', ViewMode.ListElement) @Component({ @@ -14,9 +17,15 @@ import { ItemSearchResultListElementComponent } from '../../../../../shared/obje */ export class PersonSearchResultListElementComponent extends ItemSearchResultListElementComponent { + public constructor(protected truncatableService: TruncatableService, protected dsoNameService: DSONameService) { + super(truncatableService, dsoNameService); + } + get name() { - return this.value ? - this.value : - this.firstMetadataValue('person.familyName') + ', ' + this.firstMetadataValue('person.givenName'); + let personName = this.dsoNameService.getName(this.dso); + if (isNotEmpty(this.firstMetadataValue('person.familyName')) && isNotEmpty(this.firstMetadataValue('person.givenName'))) { + personName = this.firstMetadataValue('person.familyName') + ', ' + this.firstMetadataValue('person.givenName'); + } + return personName; } } 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 8cf6117121..7505a31327 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 @@ -1,9 +1,10 @@

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

+
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts b/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts index 546621700a..93b3cf208d 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts @@ -54,7 +54,12 @@ const mockItem: Item = Object.assign(new Item(), { } ] }, - relationships: createRelationshipsObservable() + relationships: createRelationshipsObservable(), + _links: { + self : { + href: 'item-href' + } + } }); describe('PersonComponent', getItemPageFieldsTest(mockItem, PersonComponent)); 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 8b104cc9b1..42eb4ec7e6 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,20 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component'; 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 {FeatureID} from '../../../../core/data/feature-authorization/feature-id'; +import {mergeMap, take} from 'rxjs/operators'; +import {getFirstSucceededRemoteData} from '../../../../core/shared/operators'; +import {RemoteData} from '../../../../core/data/remote-data'; +import {ResearcherProfile} from '../../../../core/profile/model/researcher-profile.model'; +import {isNotUndefined} from '../../../../shared/empty.util'; +import {BehaviorSubject, Observable} from 'rxjs'; +import {RouteService} from '../../../../core/services/route.service'; +import {AuthorizationDataService} from '../../../../core/data/feature-authorization/authorization-data.service'; +import {ResearcherProfileService} from '../../../../core/profile/researcher-profile.service'; +import {NotificationsService} from '../../../../shared/notifications/notifications.service'; +import {TranslateService} from '@ngx-translate/core'; @listableObjectComponent('Person', ViewMode.StandalonePage) @Component({ @@ -12,5 +25,81 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh /** * The component for displaying metadata and relations of an item of the type Person */ -export class PersonComponent extends ItemComponent { +export class PersonComponent extends ItemComponent implements OnInit { + + claimable$: BehaviorSubject = new BehaviorSubject(false); + + constructor(protected routeService: RouteService, + protected authorizationService: AuthorizationDataService, + protected notificationsService: NotificationsService, + protected translate: TranslateService, + protected researcherProfileService: ResearcherProfileService) { + super(routeService); + } + + ngOnInit(): void { + super.ngOnInit(); + + this.authorizationService.isAuthorized(FeatureID.ShowClaimItem, this.object._links.self.href).pipe( + take(1) + ).subscribe((isAuthorized: boolean) => { + this.claimable$.next(isAuthorized); + }); + + } + + claim() { + + this.authorizationService.isAuthorized(FeatureID.CanClaimItem, this.object._links.self.href).pipe( + take(1) + ).subscribe((isAuthorized: boolean) => { + if (!isAuthorized) { + this.notificationsService.warning(this.translate.get('researcherprofile.claim.not-authorized')); + } else { + this.createFromExternalSource(); + } + }); + + } + + createFromExternalSource() { + this.researcherProfileService.createFromExternalSource(this.object._links.self.href).pipe( + getFirstSucceededRemoteData(), + mergeMap((rd: RemoteData) => { + return this.researcherProfileService.findRelatedItemId(rd.payload); + })) + .subscribe((id: string) => { + if (isNotUndefined(id)) { + this.notificationsService.success(this.translate.get('researcherprofile.success.claim.title'), + this.translate.get('researcherprofile.success.claim.body')); + this.claimable$.next(false); + } else { + this.notificationsService.error( + this.translate.get('researcherprofile.error.claim.title'), + this.translate.get('researcherprofile.error.claim.body')); + } + }); + } + + isClaimable(): Observable { + return this.claimable$; + } + + getTitleMetadataValues(): MetadataValue[]{ + const metadataValues = []; + const familyName = this.object?.firstMetadata('person.familyName'); + const givenName = this.object?.firstMetadata('person.givenName'); + const title = this.object?.firstMetadata('dc.title'); + if (familyName){ + metadataValues.push(familyName); + } + if (givenName){ + metadataValues.push(givenName); + } + if (metadataValues.length === 0 && title){ + metadataValues.push(title); + } + return metadataValues; + } + } 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 bfa25ec009..7cc8ff92c4 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -10,7 +10,6 @@
-
-
\ No newline at end of file +
diff --git a/src/app/item-page/full/full-item-page.component.ts b/src/app/item-page/full/full-item-page.component.ts index 99cf083bc5..369769c77d 100644 --- a/src/app/item-page/full/full-item-page.component.ts +++ b/src/app/item-page/full/full-item-page.component.ts @@ -16,10 +16,6 @@ import { hasValue } from '../../shared/empty.util'; import { AuthService } from '../../core/auth/auth.service'; import { Location } from '@angular/common'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { TranslateService } from '@ngx-translate/core'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { ResearcherProfileService } from '../../core/profile/researcher-profile.service'; -import { CollectionDataService } from '../../core/data/collection-data.service'; /** @@ -52,11 +48,8 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit, items: ItemDataService, authService: AuthService, authorizationService: AuthorizationDataService, - translate: TranslateService, - notificationsService: NotificationsService, - researcherProfileService: ResearcherProfileService, private _location: Location) { - super(route, router, items, authService, authorizationService, translate, notificationsService, researcherProfileService); + super(route, router, items, authService, authorizationService); } /*** AoT inheritance fix, will hopefully be resolved in the near future **/ diff --git a/src/app/item-page/simple/item-page.component.ts b/src/app/item-page/simple/item-page.component.ts index 34a059246d..b3660eb9d7 100644 --- a/src/app/item-page/simple/item-page.component.ts +++ b/src/app/item-page/simple/item-page.component.ts @@ -64,24 +64,13 @@ export class ItemPageComponent implements OnInit { itemUrl: string; - public claimable$: BehaviorSubject = new BehaviorSubject(false); - public isProcessing$: BehaviorSubject = new BehaviorSubject(false); - constructor( protected route: ActivatedRoute, private router: Router, private items: ItemDataService, private authService: AuthService, - private authorizationService: AuthorizationDataService, - private translate: TranslateService, - private notificationsService: NotificationsService, - private researcherProfileService: ResearcherProfileService + private authorizationService: AuthorizationDataService ) { - this.route.data.pipe( - map((data) => data.dso as RemoteData) - ).subscribe((data: RemoteData) => { - this.itemUrl = data?.payload?.self; - }); } /** @@ -99,55 +88,5 @@ export class ItemPageComponent implements OnInit { this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf); - this.authorizationService.isAuthorized(FeatureID.ShowClaimItem, this.itemUrl).pipe( - take(1) - ).subscribe((isAuthorized: boolean) => { - this.claimable$.next(isAuthorized); - }); - } - - claim() { - this.isProcessing$.next(true); - - this.authorizationService.isAuthorized(FeatureID.CanClaimItem, this.itemUrl).pipe( - take(1) - ).subscribe((isAuthorized: boolean) => { - if (!isAuthorized) { - this.notificationsService.warning(this.translate.get('researcherprofile.claim.not-authorized')); - this.isProcessing$.next(false); - } else { - this.createFromExternalSource(); - } - }); - - } - - createFromExternalSource() { - this.researcherProfileService.createFromExternalSource(this.itemUrl).pipe( - tap((rd: any) => { - if (!rd.hasSucceeded) { - this.isProcessing$.next(false); - } - }), - getFirstSucceededRemoteData(), - mergeMap((rd: RemoteData) => { - return this.researcherProfileService.findRelatedItemId(rd.payload); - })) - .subscribe((id: string) => { - if (isNotUndefined(id)) { - this.notificationsService.success(this.translate.get('researcherprofile.success.claim.title'), - this.translate.get('researcherprofile.success.claim.body')); - this.claimable$.next(false); - this.isProcessing$.next(false); - } else { - this.notificationsService.error( - this.translate.get('researcherprofile.error.claim.title'), - this.translate.get('researcherprofile.error.claim.body')); - } - }); - } - - isClaimable(): Observable { - return this.claimable$; } } 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 fc07f60b28..6f7684b896 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 @@ -4,7 +4,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Store } from '@ngrx/store'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; +import {Observable, of as observableOf} from 'rxjs'; import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; @@ -32,6 +32,8 @@ 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'; +import {AuthorizationDataService} from '../../../../core/data/feature-authorization/authorization-data.service'; +import {ResearcherProfileService} from '../../../../core/profile/researcher-profile.service'; export const iiifEnabled = Object.assign(new MetadataValue(),{ 'value': 'true', @@ -69,6 +71,11 @@ export function getItemPageFieldsTest(mockItem: Item, component) { return createSuccessfulRemoteDataObject$(new Bitstream()); } }; + + const authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ loader: { @@ -92,7 +99,9 @@ export function getItemPageFieldsTest(mockItem: Item, component) { { provide: NotificationsService, useValue: {} }, { provide: DefaultChangeAnalyzer, useValue: {} }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, - { provide: RouteService, useValue: {} } + { provide: RouteService, useValue: {} }, + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: ResearcherProfileService, useValue: {} } ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html index b2d53ea0e3..bb55418744 100644 --- a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html +++ b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html @@ -9,7 +9,7 @@

{{'researcher.profile.not.associated' | translate}}

- - diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts b/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts index 355d328f3f..5238482770 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts +++ b/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts @@ -17,6 +17,7 @@ import { storeModuleConfig } from '../../../../app.reducer'; import { AuthMethod } from '../../../../core/auth/models/auth.method'; import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; +import { BrowserOnlyMockPipe } from '../../../testing/browser-only-mock.pipe'; describe('LogInPasswordComponent', () => { @@ -57,7 +58,8 @@ describe('LogInPasswordComponent', () => { TranslateModule.forRoot() ], declarations: [ - LogInPasswordComponent + LogInPasswordComponent, + BrowserOnlyMockPipe, ], providers: [ { provide: AuthService, useClass: AuthServiceStub }, diff --git a/src/app/shared/log-out/log-out.component.html b/src/app/shared/log-out/log-out.component.html index 9151cb0c2d..83e58b3841 100644 --- a/src/app/shared/log-out/log-out.component.html +++ b/src/app/shared/log-out/log-out.component.html @@ -2,5 +2,5 @@ - + diff --git a/src/app/shared/log-out/log-out.component.spec.ts b/src/app/shared/log-out/log-out.component.spec.ts index f15203ed8b..028738a019 100644 --- a/src/app/shared/log-out/log-out.component.spec.ts +++ b/src/app/shared/log-out/log-out.component.spec.ts @@ -12,6 +12,7 @@ import { Router } from '@angular/router'; import { AppState } from '../../app.reducer'; import { LogOutComponent } from './log-out.component'; import { RouterStub } from '../testing/router.stub'; +import { BrowserOnlyMockPipe } from '../testing/browser-only-mock.pipe'; describe('LogOutComponent', () => { @@ -46,7 +47,8 @@ describe('LogOutComponent', () => { TranslateModule.forRoot() ], declarations: [ - LogOutComponent + LogOutComponent, + BrowserOnlyMockPipe, ], providers: [ { provide: Router, useValue: routerStub }, diff --git a/src/app/shared/object-grid/object-grid.component.html b/src/app/shared/object-grid/object-grid.component.html index 07dbcec805..c988e76803 100644 --- a/src/app/shared/object-grid/object-grid.component.html +++ b/src/app/shared/object-grid/object-grid.component.html @@ -18,7 +18,7 @@ >
-
+
diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html index 492cb4cf07..927f2b9d2a 100644 --- a/src/app/shared/object-list/object-list.component.html +++ b/src/app/shared/object-list/object-list.component.html @@ -17,7 +17,7 @@ (next)="goNext()" >
    -
  • +
- - +
diff --git a/src/app/shared/search-form/search-form.component.spec.ts b/src/app/shared/search-form/search-form.component.spec.ts index 934f00b10c..84c61f569b 100644 --- a/src/app/shared/search-form/search-form.component.spec.ts +++ b/src/app/shared/search-form/search-form.component.spec.ts @@ -14,6 +14,7 @@ import { PaginationServiceStub } from '../testing/pagination-service.stub'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { FindListOptions } from '../../core/data/find-list-options.model'; +import { BrowserOnlyMockPipe } from '../testing/browser-only-mock.pipe'; describe('SearchFormComponent', () => { let comp: SearchFormComponent; @@ -37,7 +38,10 @@ describe('SearchFormComponent', () => { { provide: SearchConfigurationService, useValue: searchConfigService }, { provide: DSpaceObjectDataService, useValue: { findById: () => createSuccessfulRemoteDataObject$(undefined)} } ], - declarations: [SearchFormComponent] + declarations: [ + SearchFormComponent, + BrowserOnlyMockPipe, + ] }).compileComponents(); })); diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-filter.component.html index 230f072772..452433e165 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.html @@ -4,6 +4,7 @@ class="filter-name d-flex" [attr.aria-controls]="regionId" [id]="toggleId" [attr.aria-expanded]="false" [attr.aria-label]="((collapsed$ | async) ? 'search.filters.filter.expand' : 'search.filters.filter.collapse') | translate" + [attr.data-test]="'filter-toggle' | dsBrowserOnly" >
{{'search.filters.filter.' + filter.name + '.head'| translate}} diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-filter.component.spec.ts index 662b380ce8..7abe45ca8c 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.spec.ts @@ -13,6 +13,7 @@ import { FilterType } from '../../models/filter-type.model'; import { SearchConfigurationServiceStub } from '../../../testing/search-configuration-service.stub'; import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component'; import { SequenceService } from '../../../../core/shared/sequence.service'; +import { BrowserOnlyMockPipe } from '../../../testing/browser-only-mock.pipe'; describe('SearchFilterComponent', () => { let comp: SearchFilterComponent; @@ -62,7 +63,10 @@ describe('SearchFilterComponent', () => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule], - declarations: [SearchFilterComponent], + declarations: [ + SearchFilterComponent, + BrowserOnlyMockPipe, + ], providers: [ { provide: SearchService, useValue: searchServiceStub }, { diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 37f4fafb1e..2388c2f13e 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -173,7 +173,7 @@ import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; import { ExternalLinkMenuItemComponent } from './menu/menu-item/external-link-menu-item.component'; - +import { BrowserOnlyPipe } from './utils/browser-only.pipe'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here CommonModule, @@ -215,7 +215,8 @@ const PIPES = [ ObjectKeysPipe, ObjectValuesPipe, ConsolePipe, - ObjNgFor + ObjNgFor, + BrowserOnlyPipe, ]; const COMPONENTS = [ diff --git a/src/app/shared/sidebar/filter/sidebar-filter.component.html b/src/app/shared/sidebar/filter/sidebar-filter.component.html index bd392aa715..79afaa7583 100644 --- a/src/app/shared/sidebar/filter/sidebar-filter.component.html +++ b/src/app/shared/sidebar/filter/sidebar-filter.component.html @@ -1,5 +1,5 @@
-
+
{{ label | translate }}
diff --git a/src/app/shared/testing/browser-only-mock.pipe.ts b/src/app/shared/testing/browser-only-mock.pipe.ts new file mode 100644 index 0000000000..12e5a7b2d7 --- /dev/null +++ b/src/app/shared/testing/browser-only-mock.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +/** + * Support dsBrowserOnly in unit tests. + */ +@Pipe({ + name: 'dsBrowserOnly' +}) +export class BrowserOnlyMockPipe implements PipeTransform { + transform(value: string): string | undefined { + return value; + } +} diff --git a/src/app/shared/testing/test-module.ts b/src/app/shared/testing/test-module.ts index b43adde8ae..7d45dd41f3 100644 --- a/src/app/shared/testing/test-module.ts +++ b/src/app/shared/testing/test-module.ts @@ -5,6 +5,7 @@ import { SharedModule } from '../shared.module'; import { NgComponentOutletDirectiveStub } from './ng-component-outlet-directive.stub'; import { QueryParamsDirectiveStub } from './query-params-directive.stub'; import { RouterLinkDirectiveStub } from './router-link-directive.stub'; +import { BrowserOnlyMockPipe } from './browser-only-mock.pipe'; /** * This module isn't used. It serves to prevent the AoT compiler @@ -21,7 +22,8 @@ import { RouterLinkDirectiveStub } from './router-link-directive.stub'; QueryParamsDirectiveStub, MySimpleItemActionComponent, RouterLinkDirectiveStub, - NgComponentOutletDirectiveStub + NgComponentOutletDirectiveStub, + BrowserOnlyMockPipe, ], exports: [ QueryParamsDirectiveStub diff --git a/src/app/shared/utils/browser-only.pipe.ts b/src/app/shared/utils/browser-only.pipe.ts new file mode 100644 index 0000000000..fc95bfda22 --- /dev/null +++ b/src/app/shared/utils/browser-only.pipe.ts @@ -0,0 +1,24 @@ +import { Inject, Pipe, PipeTransform, PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; + +/** + * A pipe that only returns its intput when run in the browser. + * Used to distinguish client-side rendered content from server-side rendered content. + */ +@Pipe({ + name: 'dsBrowserOnly' +}) +export class BrowserOnlyPipe implements PipeTransform { + constructor( + @Inject(PLATFORM_ID) private platformID: any, + ) { + } + + transform(value: string): string | undefined { + if (isPlatformBrowser((this.platformID))) { + return value; + } else { + return undefined; + } + } +} diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.html b/src/app/shared/view-mode-switch/view-mode-switch.component.html index 2863a4832b..5f70bc699c 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.html +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.html @@ -7,7 +7,7 @@ routerLinkActive="active" [class.active]="currentMode === viewModeEnum.ListElement" class="btn btn-secondary" - data-test="list-view"> + [attr.data-test]="'list-view' | dsBrowserOnly"> + [attr.data-test]="'grid-view' | dsBrowserOnly"> + [attr.data-test]="'detail-view' | dsBrowserOnly">
diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts index d361857413..5780ea5cf3 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts @@ -9,6 +9,7 @@ import { SearchService } from '../../core/shared/search/search.service'; import { ViewModeSwitchComponent } from './view-mode-switch.component'; import { SearchServiceStub } from '../testing/search-service.stub'; import { ViewMode } from '../../core/shared/view-mode.model'; +import { BrowserOnlyMockPipe } from '../testing/browser-only-mock.pipe'; @Component({ template: '' }) class DummyComponent { @@ -36,7 +37,8 @@ describe('ViewModeSwitchComponent', () => { ], declarations: [ ViewModeSwitchComponent, - DummyComponent + DummyComponent, + BrowserOnlyMockPipe, ], providers: [ { provide: SearchService, useValue: searchService }, diff --git a/src/app/submission/form/footer/submission-form-footer.component.html b/src/app/submission/form/footer/submission-form-footer.component.html index 7013c55667..e898e1c220 100644 --- a/src/app/submission/form/footer/submission-form-footer.component.html +++ b/src/app/submission/form/footer/submission-form-footer.component.html @@ -3,6 +3,7 @@ +
+
+ + + +
+ +
+
+
+
+ +
- diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.scss b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.scss new file mode 100644 index 0000000000..c3694e6784 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.scss @@ -0,0 +1,4 @@ +.auth-bitstream-container { + margin-top: -1em; + margin-bottom: 1.5em; +} diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts index 76597a135b..f3313d4cef 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts @@ -1,3 +1,5 @@ +import { isEqual } from 'lodash'; +import { DSONameService } from './../../../core/breadcrumbs/dso-name.service'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -25,7 +27,8 @@ interface BundleBitstreamsMapEntry { @Component({ selector: 'ds-item-authorizations', - templateUrl: './item-authorizations.component.html' + templateUrl: './item-authorizations.component.html', + styleUrls:['./item-authorizations.component.scss'] }) /** * Component that handles the item Authorizations @@ -36,14 +39,20 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * A map that contains all bitstream of the item's bundles * @type {Observable>>>} */ - public bundleBitstreamsMap: Map>> = new Map>>(); + public bundleBitstreamsMap: Map = new Map(); /** - * The list of bundle for the item + * The list of all bundles for the item * @type {Observable>} */ private bundles$: BehaviorSubject = new BehaviorSubject([]); + /** + * The list of bundles to be displayed in the template + * @type {BehaviorSubject} + */ + bundlesToShow$: BehaviorSubject = new BehaviorSubject([]); + /** * The target editing item * @type {Observable} @@ -56,6 +65,37 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { */ private subs: Subscription[] = []; + /** + * The size of the bundles to be loaded on demand + * @type {number} + */ + bunblesPerPage = 6; + + /** + * The number of current page + * @type {number} + */ + bunblesPageSize = 1; + + /** + * The flag to show or not the 'Load more' button + * based on the condition if all the bundles are loaded or not + * @type {boolean} + */ + allBundlesLoaded = false; + + /** + * Initial size of loaded bitstreams + * The size of incrementation used in bitstream pagination + */ + bitstreamSize = 4; + + /** + * The size of the loaded bitstremas at a certain moment + * @private + */ + private bitstreamPageSize = 4; + /** * Initialize instance variables * @@ -64,7 +104,8 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { */ constructor( private linkService: LinkService, - private route: ActivatedRoute + private route: ActivatedRoute, + private nameService: DSONameService ) { } @@ -72,16 +113,53 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * Initialize the component, setting up the bundle and bitstream within the item */ ngOnInit(): void { + this.getBundlesPerItem(); + } + + /** + * Return the item's UUID + */ + getItemUUID(): Observable { + return this.item$.pipe( + map((item: Item) => item.id), + first((UUID: string) => isNotEmpty(UUID)) + ); + } + + /** + * Return the item's name + */ + getItemName(): Observable { + return this.item$.pipe( + map((item: Item) => this.nameService.getName(item)) + ); + } + + /** + * Return all item's bundles + * + * @return an observable that emits all item's bundles + */ + getItemBundles(): Observable { + return this.bundles$.asObservable(); + } + + /** + * Get all bundles per item + * and all the bitstreams per bundle + * @param page number of current page + */ + getBundlesPerItem(page: number = 1) { this.item$ = this.route.data.pipe( map((data) => data.dso), getFirstSucceededRemoteDataWithNotEmptyPayload(), map((item: Item) => this.linkService.resolveLink( item, - followLink('bundles', {}, followLink('bitstreams')) + followLink('bundles', {findListOptions: {currentPage : page, elementsPerPage: this.bunblesPerPage}}, followLink('bitstreams')) )) ) as Observable; - const bundles$: Observable> = this.item$.pipe( + const bundles$: Observable> = this.item$.pipe( filter((item: Item) => isNotEmpty(item.bundles)), mergeMap((item: Item) => item.bundles), getFirstSucceededRemoteDataWithNotEmptyPayload(), @@ -96,37 +174,40 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { take(1), map((list: PaginatedList) => list.page) ).subscribe((bundles: Bundle[]) => { - this.bundles$.next(bundles); + if (isEqual(bundles.length,0) || bundles.length < this.bunblesPerPage) { + this.allBundlesLoaded = true; + } + if (isEqual(page, 1)) { + this.bundles$.next(bundles); + this.bundlesToShow$.next(bundles); + } else { + this.bundlesToShow$.next(this.bundles$.getValue().concat(bundles)); + this.bundles$.next(this.bundles$.getValue().concat(bundles)); + } }), bundles$.pipe( take(1), mergeMap((list: PaginatedList) => list.page), map((bundle: Bundle) => ({ id: bundle.id, bitstreams: this.getBundleBitstreams(bundle) })) ).subscribe((entry: BundleBitstreamsMapEntry) => { - this.bundleBitstreamsMap.set(entry.id, entry.bitstreams); + let bitstreamMapValues: BitstreamMapValue = { + isCollapsed: true, + allBitstreamsLoaded: false, + bitstreams: null + }; + let bits = entry.bitstreams.pipe( + map((b: PaginatedList) => { + bitstreamMapValues.allBitstreamsLoaded = b?.page.length < this.bitstreamSize ; + let firstLoaded = [...b.page.slice(0, this.bitstreamSize)]; + return firstLoaded; + }) + ); + bitstreamMapValues.bitstreams = bits; + this.bundleBitstreamsMap.set(entry.id, bitstreamMapValues); }) ); } - /** - * Return the item's UUID - */ - getItemUUID(): Observable { - return this.item$.pipe( - map((item: Item) => item.id), - first((UUID: string) => isNotEmpty(UUID)) - ); - } - - /** - * Return all item's bundles - * - * @return an observable that emits all item's bundles - */ - getItemBundles(): Observable { - return this.bundles$.asObservable(); - } - /** * Return all bundle's bitstreams * @@ -142,6 +223,48 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { ); } + /** + * Changes the collapsible state of the area that contains the bitstream list + * @param bundleId Id of bundle responsible for the requested bitstreams + */ + collapseArea(bundleId: string) { + this.bundleBitstreamsMap.get(bundleId).isCollapsed = !this.bundleBitstreamsMap.get(bundleId).isCollapsed; + } + + /** + * Loads as much bundles as initial value of bundleSize to be displayed + */ + onBunbleLoad(){ + this.bunblesPageSize ++; + this.getBundlesPerItem(this.bunblesPageSize); + } + + /** + * Calculates the bitstreams that are going to be loaded on demand, + * based on the number configured on this.bitstreamSize. + * @param bundle parent of bitstreams that are requested to be shown + * @returns Subscription + */ + onBitstreamsLoad(bundle: Bundle) { + return this.getBundleBitstreams(bundle).pipe( + map((res: PaginatedList) => { + let nextBitstreams = res?.page.slice(this.bitstreamPageSize, this.bitstreamPageSize + this.bitstreamSize); + let bitstreamsToShow = this.bundleBitstreamsMap.get(bundle.id).bitstreams.pipe( + map((existingBits: Bitstream[])=> { + return [... existingBits, ...nextBitstreams]; + }) + ); + this.bitstreamPageSize = this.bitstreamPageSize + this.bitstreamSize; + let bitstreamMapValues: BitstreamMapValue = { + bitstreams: bitstreamsToShow , + isCollapsed: this.bundleBitstreamsMap.get(bundle.id).isCollapsed, + allBitstreamsLoaded: res?.page.length <= this.bitstreamPageSize + }; + this.bundleBitstreamsMap.set(bundle.id, bitstreamMapValues); + }) + ).subscribe(); + } + /** * Unsubscribe from all subscriptions */ @@ -151,3 +274,9 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { .forEach((subscription) => subscription.unsubscribe()); } } + +export interface BitstreamMapValue { + bitstreams: Observable; + isCollapsed: boolean; + allBitstreamsLoaded: boolean; +} diff --git a/src/app/shared/resource-policies/resource-policies.component.html b/src/app/shared/resource-policies/resource-policies.component.html index 0a1ccf7952..5d11ac81f2 100644 --- a/src/app/shared/resource-policies/resource-policies.component.html +++ b/src/app/shared/resource-policies/resource-policies.component.html @@ -4,9 +4,16 @@
- {{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} {{resourceUUID}} + + {{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} + + {{resourceName}} + + ({{resourceUUID}}) + +
- -
@@ -21,13 +21,13 @@ [resourceName]="bitstream.name">
- +
- +
diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts index f3313d4cef..a833b90b20 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts @@ -45,13 +45,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * The list of all bundles for the item * @type {Observable>} */ - private bundles$: BehaviorSubject = new BehaviorSubject([]); - - /** - * The list of bundles to be displayed in the template - * @type {BehaviorSubject} - */ - bundlesToShow$: BehaviorSubject = new BehaviorSubject([]); + bundles$: BehaviorSubject = new BehaviorSubject([]); /** * The target editing item @@ -69,13 +63,13 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * The size of the bundles to be loaded on demand * @type {number} */ - bunblesPerPage = 6; + bunblesPerPage = 1; /** * The number of current page * @type {number} */ - bunblesPageSize = 1; + bunblesPageSize = 6; /** * The flag to show or not the 'Load more' button @@ -179,9 +173,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { } if (isEqual(page, 1)) { this.bundles$.next(bundles); - this.bundlesToShow$.next(bundles); } else { - this.bundlesToShow$.next(this.bundles$.getValue().concat(bundles)); this.bundles$.next(this.bundles$.getValue().concat(bundles)); } }), diff --git a/src/app/shared/resource-policies/resource-policies.component.html b/src/app/shared/resource-policies/resource-policies.component.html index 5d11ac81f2..d1fd9266b1 100644 --- a/src/app/shared/resource-policies/resource-policies.component.html +++ b/src/app/shared/resource-policies/resource-policies.component.html @@ -6,7 +6,6 @@
{{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} - {{resourceName}} ({{resourceUUID}}) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 76f4f538ea..cef52fde9c 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -848,6 +848,12 @@ "collection.edit.tabs.authorizations.title": "Collection Edit - Authorizations", + "collection.edit.item.authorizations.load-bundle-button": "Load more bundles", + + "collection.edit.item.authorizations.load-more-button": "Load more", + + "collection.edit.item.authorizations.show-bitstreams-button": "Show all Bitstreams' Policies for Bundle", + "collection.edit.tabs.metadata.head": "Edit Metadata", "collection.edit.tabs.metadata.title": "Collection Edit - Metadata", From 6cd41be3b80278fd086ffbf6ad7ad023a5aa3e67 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 9 May 2022 12:01:46 +0200 Subject: [PATCH 326/570] [CST-5677] Changed bunblesPerPage value --- .../item-authorizations/item-authorizations.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts index a833b90b20..f018d1c642 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts @@ -63,7 +63,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * The size of the bundles to be loaded on demand * @type {number} */ - bunblesPerPage = 1; + bunblesPerPage = 6; /** * The number of current page From 32d7fca27af3d92eda08c998414a2927fb4ad2c3 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 9 May 2022 12:02:50 +0200 Subject: [PATCH 327/570] [CST-5677] Changed bunblesPageSize value --- .../item-authorizations/item-authorizations.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts index f018d1c642..3a7a7d95f2 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts @@ -69,7 +69,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * The number of current page * @type {number} */ - bunblesPageSize = 6; + bunblesPageSize = 1; /** * The flag to show or not the 'Load more' button From 51058daf27f0d63aecdc3f28a981ddc23b108ef7 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 10 May 2022 09:25:59 +1200 Subject: [PATCH 328/570] [TLC-254] tidy imports in type bind service --- .../ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts index f8a7376c3e..5dd4a6627d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts @@ -15,7 +15,7 @@ import { OR_OPERATOR } from '@ng-dynamic-forms/core'; -import {hasNoValue, hasValue, isEmpty} from '../../../empty.util'; +import {hasNoValue, hasValue} from '../../../empty.util'; import { FormBuilderService } from '../form-builder.service'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-constants'; From 870a36180c4836108c23f790e5e49c04460a99b4 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Mon, 25 Apr 2022 17:26:12 +0200 Subject: [PATCH 329/570] 89984: Add ThemedLoadingComponent --- .../epeople-registry.component.html | 2 +- .../eperson-form/eperson-form.component.html | 4 +-- .../groups-registry.component.html | 2 +- .../edit-bitstream-page.component.html | 4 +-- .../browse-by-metadata-page.component.html | 2 +- .../collection-page.component.html | 8 ++--- .../collection-source.component.html | 2 +- .../edit-item-template-page.component.html | 2 +- .../community-list.component.html | 6 ++-- .../community-page.component.html | 2 +- ...ty-page-sub-collection-list.component.html | 2 +- ...ity-page-sub-community-list.component.html | 2 +- .../top-level-community-list.component.html | 2 +- .../item-bitstreams.component.html | 2 +- ...rag-and-drop-bitstream-list.component.html | 2 +- .../edit-relationship-list.component.html | 2 +- .../item-relationships.component.html | 2 +- .../full/full-item-page.component.html | 2 +- .../media-viewer/media-viewer.component.html | 4 +-- .../media-viewer.component.spec.ts | 2 +- .../file-section/file-section.component.html | 2 +- .../file-section.component.spec.ts | 2 +- .../item-page/simple/item-page.component.html | 2 +- .../simple/item-page.component.spec.ts | 2 +- ...etadata-representation-list.component.html | 2 +- .../related-items.component.html | 2 +- .../detail/process-detail.component.html | 2 +- .../deny-request-copy.component.html | 2 +- .../grant-deny-request-copy.component.html | 2 +- .../grant-request-copy.component.html | 2 +- src/app/root/root.component.html | 4 +-- .../user-menu/user-menu.component.html | 2 +- .../shared/browse-by/browse-by.component.html | 2 +- .../browse-by/browse-by.component.spec.ts | 2 +- .../collection-dropdown.component.html | 4 +-- .../comcol-role/comcol-role.component.html | 2 +- .../dso-selector/dso-selector.component.html | 2 +- .../entity-dropdown.component.html | 4 +-- ...sting-metadata-list-element.component.html | 2 +- ...sting-relation-list-element.component.html | 2 +- .../dynamic-relation-group.component.html | 2 +- ...namic-lookup-relation-modal.component.html | 2 +- ...elation-external-source-tab.component.html | 4 +-- ...tion-external-source-tab.component.spec.ts | 4 +-- .../loading/themed-loading.component.ts | 31 +++++++++++++++++++ src/app/shared/log-in/log-in.component.html | 2 +- .../object-detail.component.html | 2 +- .../object-grid/object-grid.component.html | 2 +- ...-search-result-list-element.component.html | 4 +-- ...-search-result-list-element.component.html | 4 +-- .../collection-select.component.html | 2 +- .../item-select/item-select.component.html | 2 +- .../search-results.component.html | 2 +- src/app/shared/shared.module.ts | 2 ++ .../vocabulary-treeview.component.html | 2 +- .../statistics-page.component.html | 2 +- .../form/submission-form.component.html | 2 +- ...-import-external-collection.component.html | 2 +- .../submission-import-external.component.html | 4 +-- ...mission-section-cc-licenses.component.html | 4 +-- .../sections/form/section-form.component.html | 2 +- src/app/thumbnail/thumbnail.component.html | 4 +-- src/app/thumbnail/thumbnail.component.spec.ts | 2 +- 63 files changed, 112 insertions(+), 79 deletions(-) create mode 100644 src/app/shared/loading/themed-loading.component.ts diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.html b/src/app/access-control/epeople-registry/epeople-registry.component.html index 7ef02a76cf..2d87f21d26 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.html +++ b/src/app/access-control/epeople-registry/epeople-registry.component.html @@ -45,7 +45,7 @@
- + - +
{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}
- + - +
- + diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html index 2321da0204..c0741891eb 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html @@ -35,7 +35,7 @@ (prev)="goPrev()" (next)="goNext()"> - + diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index 9d598a3b69..4f5422e300 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -57,8 +57,8 @@ - + @@ -75,7 +75,7 @@ - + diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html index b67ee9a1bd..847ccefdda 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html @@ -25,7 +25,7 @@ - +

{{ 'collection.edit.tabs.source.form.head' | translate }}

diff --git a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html index f8c5c92e96..37258cd057 100644 --- a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html +++ b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html @@ -6,7 +6,7 @@ - +
diff --git a/src/app/community-list-page/community-list/community-list.component.html b/src/app/community-list-page/community-list/community-list.component.html index f441dfa36e..5d53633c4e 100644 --- a/src/app/community-list-page/community-list/community-list.component.html +++ b/src/app/community-list-page/community-list/community-list.component.html @@ -1,4 +1,4 @@ - + {{ 'communityList.showMore' | translate }} - +
@@ -57,7 +57,7 @@ - +
diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index cf7282eb4b..95755fdaf1 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -41,5 +41,5 @@ - + diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html index 9928ebd18a..69f16ee3ac 100644 --- a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html +++ b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html @@ -9,5 +9,5 @@ - +
diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.html b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.html index 2d14dce60a..be2788a9f4 100644 --- a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.html +++ b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.html @@ -9,5 +9,5 @@ - + diff --git a/src/app/home-page/top-level-community-list/top-level-community-list.component.html b/src/app/home-page/top-level-community-list/top-level-community-list.component.html index dbea87a175..788db6ea4f 100644 --- a/src/app/home-page/top-level-community-list/top-level-community-list.component.html +++ b/src/app/home-page/top-level-community-list/top-level-community-list.component.html @@ -10,4 +10,4 @@ - + diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.html b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.html index 315498378a..0d6a609e74 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.html +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.html @@ -44,7 +44,7 @@ class="alert alert-info w-100 d-inline-block mt-4" role="alert"> {{'item.edit.bitstreams.empty' | translate}} - +
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.html 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.html index f7904f6cc8..f54aa73597 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.html +++ 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.html @@ -29,5 +29,5 @@
- + diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html index 86ab93662e..7cdc903f24 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html @@ -31,5 +31,5 @@
{{"item.edit.relationships.no-relationships" | translate}}
- + diff --git a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html index 4a53445a6d..f19ab21871 100644 --- a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html +++ b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html @@ -32,7 +32,7 @@ >
- +
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 d2655c4ad0..ea97a3957c 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -37,5 +37,5 @@
- + diff --git a/src/app/item-page/media-viewer/media-viewer.component.html b/src/app/item-page/media-viewer/media-viewer.component.html index b79b91629f..4259af5250 100644 --- a/src/app/item-page/media-viewer/media-viewer.component.html +++ b/src/app/item-page/media-viewer/media-viewer.component.html @@ -1,9 +1,9 @@ - + >
diff --git a/src/app/item-page/media-viewer/media-viewer.component.spec.ts b/src/app/item-page/media-viewer/media-viewer.component.spec.ts index cfd72bf416..39a35ebe61 100644 --- a/src/app/item-page/media-viewer/media-viewer.component.spec.ts +++ b/src/app/item-page/media-viewer/media-viewer.component.spec.ts @@ -110,7 +110,7 @@ describe('MediaViewerComponent', () => { }); it('should display a loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-loading')); + const loading = fixture.debugElement.query(By.css('ds-themed-loading')); expect(loading.nativeElement).toBeDefined(); }); }); diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.html b/src/app/item-page/simple/field-components/file-section/file-section.component.html index 3d093f83c9..5fa5e5096f 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.html +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.html @@ -6,7 +6,7 @@ ({{(file?.sizeBytes) | dsFileSize }}) - + 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 fdd6d14e7d..2d185aef9c 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 @@ -86,7 +86,7 @@ describe('FileSectionComponent', () => { }); it('should display a loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-loading')); + const loading = fixture.debugElement.query(By.css('ds-themed-loading')); expect(loading.nativeElement).toBeDefined(); }); }); diff --git a/src/app/item-page/simple/item-page.component.html b/src/app/item-page/simple/item-page.component.html index 74b61fd976..d91f4b501d 100644 --- a/src/app/item-page/simple/item-page.component.html +++ b/src/app/item-page/simple/item-page.component.html @@ -9,5 +9,5 @@
- + diff --git a/src/app/item-page/simple/item-page.component.spec.ts b/src/app/item-page/simple/item-page.component.spec.ts index ff5a1e38d5..0b0f1e1724 100644 --- a/src/app/item-page/simple/item-page.component.spec.ts +++ b/src/app/item-page/simple/item-page.component.spec.ts @@ -85,7 +85,7 @@ describe('ItemPageComponent', () => { }); it('should display a loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-loading')); + const loading = fixture.debugElement.query(By.css('ds-themed-loading')); expect(loading.nativeElement).toBeDefined(); }); }); diff --git a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html index d1281f450a..e1559cb129 100644 --- a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html +++ b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html @@ -4,7 +4,7 @@ - + - + diff --git a/src/app/request-copy/grant-request-copy/grant-request-copy.component.html b/src/app/request-copy/grant-request-copy/grant-request-copy.component.html index d2c2cfc3c8..5cb4dbac36 100644 --- a/src/app/request-copy/grant-request-copy/grant-request-copy.component.html +++ b/src/app/request-copy/grant-request-copy/grant-request-copy.component.html @@ -13,5 +13,5 @@ - + diff --git a/src/app/root/root.component.html b/src/app/root/root.component.html index 898074352c..d0465af69f 100644 --- a/src/app/root/root.component.html +++ b/src/app/root/root.component.html @@ -13,7 +13,7 @@
- +
@@ -25,6 +25,6 @@
- +
diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html index ac55a211e9..736d39d318 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html @@ -1,4 +1,4 @@ - +
{{(user$ | async)?.name}} ({{(user$ | async)?.email}}) {{'nav.profile' | translate}} diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index 6d1422293d..645a987abe 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -34,7 +34,7 @@
- + diff --git a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html index b0a319b4ff..440785b89b 100644 --- a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html @@ -13,7 +13,7 @@
- +
{{'comcol-role.edit.no-group' | translate}}
diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index 4b46d3bc3f..d4283246d8 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -31,7 +31,7 @@
diff --git a/src/app/shared/entity-dropdown/entity-dropdown.component.html b/src/app/shared/entity-dropdown/entity-dropdown.component.html index 59c242ef97..a4d539625f 100644 --- a/src/app/shared/entity-dropdown/entity-dropdown.component.html +++ b/src/app/shared/entity-dropdown/entity-dropdown.component.html @@ -21,8 +21,8 @@ diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html index 07ea131a00..62d34a8625 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html @@ -1,7 +1,7 @@
- + diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html index 15087d2553..ff91b18e1c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html @@ -1,7 +1,7 @@
- + diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html index 84bc0f4ffe..05f443aad5 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html @@ -57,7 +57,7 @@
- +
- + diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html index 74fc5fd06d..f26de733b2 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html @@ -6,6 +6,6 @@ - + [showMessage]="false"> diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html index 41d95b87af..2f0b04ef12 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html @@ -6,6 +6,6 @@ - + [showMessage]="false"> diff --git a/src/app/shared/object-select/collection-select/collection-select.component.html b/src/app/shared/object-select/collection-select/collection-select.component.html index 27ebcd9643..8ed41c5226 100644 --- a/src/app/shared/object-select/collection-select/collection-select.component.html +++ b/src/app/shared/object-select/collection-select/collection-select.component.html @@ -28,7 +28,7 @@ {{'collection.select.empty' | translate}}
- +
- + diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 715ee66a99..f9391afdb4 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -177,6 +177,7 @@ 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 { ThemedLoadingComponent } from './loading/themed-loading.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -234,6 +235,7 @@ const COMPONENTS = [ FileSectionComponent, LangSwitchComponent, LoadingComponent, + ThemedLoadingComponent, LogInComponent, LogOutComponent, NumberPickerComponent, diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.html b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.html index 3f57e6a4c3..39c62d6e53 100644 --- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.html +++ b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.html @@ -23,7 +23,7 @@
- +

{{'vocabulary-treeview.search.no-result' | translate}}

diff --git a/src/app/statistics-page/statistics-page/statistics-page.component.html b/src/app/statistics-page/statistics-page/statistics-page.component.html index 5cf1e9c8b5..c6938c7582 100644 --- a/src/app/statistics-page/statistics-page/statistics-page.component.html +++ b/src/app/statistics-page/statistics-page/statistics-page.component.html @@ -11,7 +11,7 @@ - + diff --git a/src/app/submission/form/submission-form.component.html b/src/app/submission/form/submission-form.component.html index c86d4e0195..a80fe35f4e 100644 --- a/src/app/submission/form/submission-form.component.html +++ b/src/app/submission/form/submission-form.component.html @@ -22,7 +22,7 @@
- +
- +
@@ -20,18 +20,10 @@ [fields]="['person.email']" [label]="'person.page.email'"> - - - - - - - -
+ +
{{"item.page.link.full" | translate}} diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts b/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts index 93b3cf208d..efbc48a209 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts @@ -17,24 +17,12 @@ const mockItem: Item = Object.assign(new Item(), { value: 'fake@email.com' } ], - // 'person.identifier.orcid': [ - // { - // language: 'en_US', - // value: 'ORCID-1' - // } - // ], 'person.birthDate': [ { language: 'en_US', value: '1993' } ], - // 'person.identifier.staffid': [ - // { - // language: 'en_US', - // value: '1' - // } - // ], 'person.jobTitle': [ { language: 'en_US', @@ -62,4 +50,42 @@ const mockItem: Item = Object.assign(new Item(), { } }); -describe('PersonComponent', getItemPageFieldsTest(mockItem, PersonComponent)); +const mockItemWithTitle: Item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: { + 'person.email': [ + { + language: 'en_US', + value: 'fake@email.com' + } + ], + 'person.birthDate': [ + { + language: 'en_US', + value: '1993' + } + ], + 'person.jobTitle': [ + { + language: 'en_US', + value: 'Developer' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Doe, John' + } + ] + }, + relationships: createRelationshipsObservable(), + _links: { + self : { + href: 'item-href' + } + } +}); + +describe('PersonComponent with family and given names', getItemPageFieldsTest(mockItem, PersonComponent)); + +describe('PersonComponent with dc.title', getItemPageFieldsTest(mockItemWithTitle, PersonComponent)); 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 33db18681f..27fdd2ab15 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,20 +1,10 @@ -import {Component, OnInit} from '@angular/core'; +import { Component } from '@angular/core'; import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component'; 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 {FeatureID} from '../../../../core/data/feature-authorization/feature-id'; -import {mergeMap, take} from 'rxjs/operators'; -import {getFirstSucceededRemoteData} from '../../../../core/shared/operators'; -import {RemoteData} from '../../../../core/data/remote-data'; -import {ResearcherProfile} from '../../../../core/profile/model/researcher-profile.model'; -import {isNotUndefined} from '../../../../shared/empty.util'; -import {BehaviorSubject, Observable} from 'rxjs'; -import {RouteService} from '../../../../core/services/route.service'; -import {AuthorizationDataService} from '../../../../core/data/feature-authorization/authorization-data.service'; -import {ResearcherProfileService} from '../../../../core/profile/researcher-profile.service'; -import {NotificationsService} from '../../../../shared/notifications/notifications.service'; -import {TranslateService} from '@ngx-translate/core'; +import { + listableObjectComponent +} from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; +import { MetadataValue } from '../../../../core/shared/metadata.models'; @listableObjectComponent('Person', ViewMode.StandalonePage) @Component({ @@ -25,76 +15,25 @@ import {TranslateService} from '@ngx-translate/core'; /** * The component for displaying metadata and relations of an item of the type Person */ -export class PersonComponent extends ItemComponent implements OnInit { - - claimable$: BehaviorSubject = new BehaviorSubject(false); - - constructor(protected routeService: RouteService, - protected authorizationService: AuthorizationDataService, - protected notificationsService: NotificationsService, - protected translate: TranslateService, - protected researcherProfileService: ResearcherProfileService) { - super(routeService); - } - - ngOnInit(): void { - super.ngOnInit(); - - this.authorizationService.isAuthorized(FeatureID.CanClaimItem, this.object._links.self.href).pipe( - take(1) - ).subscribe((isAuthorized: boolean) => { - this.claimable$.next(isAuthorized); - }); - - } - - /** - * Create a new researcher profile claiming the current item. - */ - claim() { - this.researcherProfileService.createFromExternalSource(this.object._links.self.href).pipe( - getFirstSucceededRemoteData(), - mergeMap((rd: RemoteData) => { - return this.researcherProfileService.findRelatedItemId(rd.payload); - })) - .subscribe((id: string) => { - if (isNotUndefined(id)) { - this.notificationsService.success(this.translate.get('researcherprofile.success.claim.title'), - this.translate.get('researcherprofile.success.claim.body')); - this.claimable$.next(false); - } else { - this.notificationsService.error( - this.translate.get('researcherprofile.error.claim.title'), - this.translate.get('researcherprofile.error.claim.body')); - } - }); - } - - /** - * Returns true if the item is claimable, false otherwise. - */ - isClaimable(): Observable { - return this.claimable$; - } +export class PersonComponent extends ItemComponent { /** * Returns the metadata values to be used for the page title. */ - getTitleMetadataValues(): MetadataValue[]{ + getTitleMetadataValues(): MetadataValue[] { const metadataValues = []; const familyName = this.object?.firstMetadata('person.familyName'); const givenName = this.object?.firstMetadata('person.givenName'); const title = this.object?.firstMetadata('dc.title'); - if (familyName){ + if (familyName) { metadataValues.push(familyName); } - if (givenName){ + if (givenName) { metadataValues.push(givenName); } - if (metadataValues.length === 0 && title){ + if (metadataValues.length === 0 && title) { metadataValues.push(title); } return metadataValues; } - } diff --git a/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.html b/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.html new file mode 100644 index 0000000000..12d5d2b47d --- /dev/null +++ b/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.html @@ -0,0 +1 @@ + diff --git a/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.scss b/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.spec.ts b/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.spec.ts new file mode 100644 index 0000000000..168517b47a --- /dev/null +++ b/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.spec.ts @@ -0,0 +1,186 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; + +import { of as observableOf } from 'rxjs'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { PersonPageClaimButtonComponent } from './person-page-claim-button.component'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; +import { ResearcherProfileService } from '../../../core/profile/researcher-profile.service'; +import { RouteService } from '../../../core/services/route.service'; +import { routeServiceStub } from '../../testing/route-service.stub'; +import { Item } from '../../../core/shared/item.model'; +import { ResearcherProfile } from '../../../core/profile/model/researcher-profile.model'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { getTestScheduler } from 'jasmine-marbles'; +import { TestScheduler } from 'rxjs/testing'; + +describe('PersonPageClaimButtonComponent', () => { + let scheduler: TestScheduler; + let component: PersonPageClaimButtonComponent; + let fixture: ComponentFixture; + + const mockItem: Item = Object.assign(new Item(), { + metadata: { + 'person.email': [ + { + language: 'en_US', + value: 'fake@email.com' + } + ], + 'person.birthDate': [ + { + language: 'en_US', + value: '1993' + } + ], + 'person.jobTitle': [ + { + language: 'en_US', + value: 'Developer' + } + ], + 'person.familyName': [ + { + language: 'en_US', + value: 'Doe' + } + ], + 'person.givenName': [ + { + language: 'en_US', + value: 'John' + } + ] + }, + _links: { + self: { + href: 'item-href' + } + } + }); + + const mockResearcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), { + id: 'test-id', + visible: true, + type: 'profile', + _links: { + item: { + href: 'https://rest.api/rest/api/profiles/test-id/item' + }, + self: { + href: 'https://rest.api/rest/api/profiles/test-id' + }, + } + }); + + const notificationsService = new NotificationsServiceStub(); + + const authorizationDataService = jasmine.createSpyObj('authorizationDataService', { + isAuthorized: jasmine.createSpy('isAuthorized') + }); + + const researcherProfileService = jasmine.createSpyObj('researcherProfileService', { + createFromExternalSource: jasmine.createSpy('createFromExternalSource'), + findRelatedItemId: jasmine.createSpy('findRelatedItemId'), + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }) + ], + declarations: [PersonPageClaimButtonComponent], + providers: [ + { provide: AuthorizationDataService, useValue: authorizationDataService }, + { provide: NotificationsService, useValue: notificationsService }, + { provide: ResearcherProfileService, useValue: researcherProfileService }, + { provide: RouteService, useValue: routeServiceStub }, + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PersonPageClaimButtonComponent); + component = fixture.componentInstance; + component.object = mockItem; + }); + + describe('when item can be claimed', () => { + beforeEach(() => { + authorizationDataService.isAuthorized.and.returnValue(observableOf(true)); + researcherProfileService.createFromExternalSource.calls.reset(); + researcherProfileService.findRelatedItemId.calls.reset(); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should create claim button', () => { + const btn = fixture.debugElement.query(By.css('[data-test="item-claim"]')); + expect(btn).toBeTruthy(); + }); + + describe('claim', () => { + describe('when successfully', () => { + beforeEach(() => { + scheduler = getTestScheduler(); + researcherProfileService.createFromExternalSource.and.returnValue(createSuccessfulRemoteDataObject$(mockResearcherProfile)); + researcherProfileService.findRelatedItemId.and.returnValue(observableOf('test-id')); + }); + + it('should display success notification', () => { + scheduler.schedule(() => component.claim()); + scheduler.flush(); + + expect(researcherProfileService.findRelatedItemId).toHaveBeenCalled(); + expect(notificationsService.success).toHaveBeenCalled(); + }); + }); + + describe('when not successfully', () => { + beforeEach(() => { + scheduler = getTestScheduler(); + researcherProfileService.createFromExternalSource.and.returnValue(createFailedRemoteDataObject$()); + }); + + it('should display success notification', () => { + scheduler.schedule(() => component.claim()); + scheduler.flush(); + + expect(researcherProfileService.findRelatedItemId).not.toHaveBeenCalled(); + expect(notificationsService.error).toHaveBeenCalled(); + }); + }); + }); + + }); + + describe('when item cannot be claimed', () => { + beforeEach(() => { + authorizationDataService.isAuthorized.and.returnValue(observableOf(false)); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should create claim button', () => { + const btn = fixture.debugElement.query(By.css('[data-test="item-claim"]')); + expect(btn).toBeFalsy(); + }); + + }); +}); diff --git a/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.ts b/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.ts new file mode 100644 index 0000000000..d2e8e888e1 --- /dev/null +++ b/src/app/shared/dso-page/person-page-claim-button/person-page-claim-button.component.ts @@ -0,0 +1,85 @@ +import { Component, Input, OnInit } from '@angular/core'; + +import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; +import { mergeMap, take } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; + +import { RouteService } from '../../../core/services/route.service'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { ResearcherProfileService } from '../../../core/profile/researcher-profile.service'; +import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { RemoteData } from '../../../core/data/remote-data'; +import { ResearcherProfile } from '../../../core/profile/model/researcher-profile.model'; +import { isNotEmpty } from '../../empty.util'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; + +@Component({ + selector: 'ds-person-page-claim-button', + templateUrl: './person-page-claim-button.component.html', + styleUrls: ['./person-page-claim-button.component.scss'] +}) +export class PersonPageClaimButtonComponent implements OnInit { + + /** + * The target person item to claim + */ + @Input() object: DSpaceObject; + + /** + * A boolean representing if item can be claimed or not + */ + claimable$: BehaviorSubject = new BehaviorSubject(false); + + constructor(protected routeService: RouteService, + protected authorizationService: AuthorizationDataService, + protected notificationsService: NotificationsService, + protected translate: TranslateService, + protected researcherProfileService: ResearcherProfileService) { + } + + ngOnInit(): void { + this.authorizationService.isAuthorized(FeatureID.CanClaimItem, this.object._links.self.href).pipe( + take(1) + ).subscribe((isAuthorized: boolean) => { + this.claimable$.next(isAuthorized); + }); + + } + + /** + * Create a new researcher profile claiming the current item. + */ + claim() { + this.researcherProfileService.createFromExternalSource(this.object._links.self.href).pipe( + getFirstCompletedRemoteData(), + mergeMap((rd: RemoteData) => { + console.log(rd); + if (rd.hasSucceeded) { + return this.researcherProfileService.findRelatedItemId(rd.payload); + } else { + return observableOf(null); + } + })) + .subscribe((id: string) => { + if (isNotEmpty(id)) { + this.notificationsService.success(this.translate.get('researcherprofile.success.claim.title'), + this.translate.get('researcherprofile.success.claim.body')); + this.claimable$.next(false); + } else { + this.notificationsService.error( + this.translate.get('researcherprofile.error.claim.title'), + this.translate.get('researcherprofile.error.claim.body')); + } + }); + } + + /** + * Returns true if the item is claimable, false otherwise. + */ + isClaimable(): Observable { + return this.claimable$; + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 847b3910e0..b2a36285bb 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -7,7 +7,12 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; import { NouisliderModule } from 'ng2-nouislider'; import { - NgbDatepickerModule, NgbDropdownModule, NgbNavModule, NgbPaginationModule, NgbTimepickerModule, NgbTooltipModule, + NgbDatepickerModule, + NgbDropdownModule, + NgbNavModule, + NgbPaginationModule, + NgbTimepickerModule, + NgbTooltipModule, NgbTypeaheadModule, } from '@ng-bootstrap/ng-bootstrap'; import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core'; @@ -16,7 +21,9 @@ import { FileUploadModule } from 'ng2-file-upload'; 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'; +import { + ExportMetadataSelectorComponent +} from './dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component'; import { FileDropzoneNoUploaderComponent } from './file-dropzone-no-uploader/file-dropzone-no-uploader.component'; import { ItemListElementComponent } from './object-list/item-list-element/item-types/item/item-list-element.component'; import { EnumKeysPipe } from './utils/enum-keys-pipe'; @@ -24,13 +31,21 @@ import { FileSizePipe } from './utils/file-size-pipe'; import { MetadataFieldValidator } from './utils/metadatafield-validator.directive'; import { SafeUrlPipe } from './utils/safe-url-pipe'; import { ConsolePipe } from './utils/console.pipe'; -import { CollectionListElementComponent } from './object-list/collection-list-element/collection-list-element.component'; +import { + CollectionListElementComponent +} from './object-list/collection-list-element/collection-list-element.component'; import { CommunityListElementComponent } from './object-list/community-list-element/community-list-element.component'; -import { SearchResultListElementComponent } from './object-list/search-result-list-element/search-result-list-element.component'; +import { + SearchResultListElementComponent +} from './object-list/search-result-list-element/search-result-list-element.component'; import { ObjectListComponent } from './object-list/object-list.component'; -import { CollectionGridElementComponent } from './object-grid/collection-grid-element/collection-grid-element.component'; +import { + CollectionGridElementComponent +} from './object-grid/collection-grid-element/collection-grid-element.component'; import { CommunityGridElementComponent } from './object-grid/community-grid-element/community-grid-element.component'; -import { AbstractListableElementComponent } from './object-collection/shared/object-collection-element/abstract-listable-element.component'; +import { + AbstractListableElementComponent +} from './object-collection/shared/object-collection-element/abstract-listable-element.component'; import { ObjectGridComponent } from './object-grid/object-grid.component'; import { ObjectCollectionComponent } from './object-collection/object-collection.component'; import { ErrorComponent } from './error/error.component'; @@ -38,7 +53,9 @@ import { LoadingComponent } from './loading/loading.component'; import { PaginationComponent } from './pagination/pagination.component'; import { ThumbnailComponent } from '../thumbnail/thumbnail.component'; import { SearchFormComponent } from './search-form/search-form.component'; -import { SearchResultGridElementComponent } from './object-grid/search-result-grid-element/search-result-grid-element.component'; +import { + SearchResultGridElementComponent +} from './object-grid/search-result-grid-element/search-result-grid-element.component'; import { ViewModeSwitchComponent } from './view-mode-switch/view-mode-switch.component'; import { VarDirective } from './utils/var.directive'; import { AuthNavMenuComponent } from './auth-nav-menu/auth-nav-menu.component'; @@ -53,21 +70,33 @@ import { ChipsComponent } from './chips/chips.component'; import { NumberPickerComponent } from './number-picker/number-picker.component'; import { MockAdminGuard } from './mocks/admin-guard.service.mock'; import { AlertComponent } from './alert/alert.component'; -import { SearchResultDetailElementComponent } from './object-detail/my-dspace-result-detail-element/search-result-detail-element.component'; +import { + SearchResultDetailElementComponent +} from './object-detail/my-dspace-result-detail-element/search-result-detail-element.component'; import { ClaimedTaskActionsComponent } from './mydspace-actions/claimed-task/claimed-task-actions.component'; import { PoolTaskActionsComponent } from './mydspace-actions/pool-task/pool-task-actions.component'; import { ObjectDetailComponent } from './object-detail/object-detail.component'; -import { ItemDetailPreviewComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component'; -import { MyDSpaceItemStatusComponent } from './object-collection/shared/mydspace-item-status/my-dspace-item-status.component'; +import { + ItemDetailPreviewComponent +} from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component'; +import { + MyDSpaceItemStatusComponent +} from './object-collection/shared/mydspace-item-status/my-dspace-item-status.component'; import { WorkspaceitemActionsComponent } from './mydspace-actions/workspaceitem/workspaceitem-actions.component'; import { WorkflowitemActionsComponent } from './mydspace-actions/workflowitem/workflowitem-actions.component'; import { ItemSubmitterComponent } from './object-collection/shared/mydspace-item-submitter/item-submitter.component'; import { ItemActionsComponent } from './mydspace-actions/item/item-actions.component'; -import { ClaimedTaskActionsApproveComponent } from './mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component'; -import { ClaimedTaskActionsRejectComponent } from './mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component'; +import { + ClaimedTaskActionsApproveComponent +} from './mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component'; +import { + ClaimedTaskActionsRejectComponent +} from './mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component'; import { ObjNgFor } from './utils/object-ngfor.pipe'; import { BrowseByComponent } from './browse-by/browse-by.component'; -import { BrowseEntryListElementComponent } from './object-list/browse-entry-list-element/browse-entry-list-element.component'; +import { + BrowseEntryListElementComponent +} from './object-list/browse-entry-list-element/browse-entry-list-element.component'; import { DebounceDirective } from './utils/debounce.directive'; import { ClickOutsideDirective } from './utils/click-outside.directive'; import { EmphasizePipe } from './utils/emphasize.pipe'; @@ -76,53 +105,105 @@ import { CapitalizePipe } from './utils/capitalize.pipe'; import { ObjectKeysPipe } from './utils/object-keys-pipe'; import { AuthorityConfidenceStateDirective } from './authority-confidence/authority-confidence-state.directive'; import { LangSwitchComponent } from './lang-switch/lang-switch.component'; -import { PlainTextMetadataListElementComponent } from './object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component'; -import { ItemMetadataListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-list-element.component'; -import { MetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/metadata-representation-list-element.component'; +import { + PlainTextMetadataListElementComponent +} from './object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component'; +import { + ItemMetadataListElementComponent +} from './object-list/metadata-representation-list-element/item/item-metadata-list-element.component'; +import { + MetadataRepresentationListElementComponent +} from './object-list/metadata-representation-list-element/metadata-representation-list-element.component'; import { ObjectValuesPipe } from './utils/object-values-pipe'; import { InListValidator } from './utils/in-list-validator.directive'; import { AutoFocusDirective } from './utils/auto-focus.directive'; import { StartsWithDateComponent } from './starts-with/date/starts-with-date.component'; import { StartsWithTextComponent } from './starts-with/text/starts-with-text.component'; import { DSOSelectorComponent } from './dso-selector/dso-selector/dso-selector.component'; -import { CreateCommunityParentSelectorComponent } from './dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component'; -import { CreateItemParentSelectorComponent } from './dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; -import { CreateCollectionParentSelectorComponent } from './dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component'; -import { CommunitySearchResultListElementComponent } from './object-list/search-result-list-element/community-search-result/community-search-result-list-element.component'; -import { CollectionSearchResultListElementComponent } from './object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component'; -import { EditItemSelectorComponent } from './dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; -import { EditCommunitySelectorComponent } from './dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; -import { EditCollectionSelectorComponent } from './dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; -import { ItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; -import { MetadataFieldWrapperComponent } from '../item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component'; +import { + CreateCommunityParentSelectorComponent +} from './dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component'; +import { + CreateItemParentSelectorComponent +} from './dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; +import { + CreateCollectionParentSelectorComponent +} from './dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component'; +import { + CommunitySearchResultListElementComponent +} from './object-list/search-result-list-element/community-search-result/community-search-result-list-element.component'; +import { + CollectionSearchResultListElementComponent +} from './object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component'; +import { + EditItemSelectorComponent +} from './dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; +import { + EditCommunitySelectorComponent +} from './dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; +import { + EditCollectionSelectorComponent +} from './dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; +import { + ItemListPreviewComponent +} from './object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; +import { + MetadataFieldWrapperComponent +} from '../item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component'; import { MetadataValuesComponent } from '../item-page/field-components/metadata-values/metadata-values.component'; import { RoleDirective } from './roles/role.directive'; import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component'; -import { ClaimedTaskActionsReturnToPoolComponent } from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component'; -import { ItemDetailPreviewFieldComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; -import { CollectionSearchResultGridElementComponent } from './object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component'; -import { CommunitySearchResultGridElementComponent } from './object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component'; +import { + ClaimedTaskActionsReturnToPoolComponent +} from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component'; +import { + ItemDetailPreviewFieldComponent +} from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; +import { + CollectionSearchResultGridElementComponent +} from './object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component'; +import { + CommunitySearchResultGridElementComponent +} from './object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component'; import { PageSizeSelectorComponent } from './page-size-selector/page-size-selector.component'; import { AbstractTrackableComponent } from './trackable/abstract-trackable.component'; -import { ComcolMetadataComponent } from './comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; +import { + ComcolMetadataComponent +} from './comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; import { ItemSelectComponent } from './object-select/item-select/item-select.component'; import { CollectionSelectComponent } from './object-select/collection-select/collection-select.component'; -import { FilterInputSuggestionsComponent } from './input-suggestions/filter-suggestions/filter-input-suggestions.component'; -import { DsoInputSuggestionsComponent } from './input-suggestions/dso-input-suggestions/dso-input-suggestions.component'; +import { + FilterInputSuggestionsComponent +} from './input-suggestions/filter-suggestions/filter-input-suggestions.component'; +import { + DsoInputSuggestionsComponent +} from './input-suggestions/dso-input-suggestions/dso-input-suggestions.component'; import { ItemGridElementComponent } from './object-grid/item-grid-element/item-types/item/item-grid-element.component'; import { TypeBadgeComponent } from './object-list/type-badge/type-badge.component'; -import { MetadataRepresentationLoaderComponent } from './metadata-representation/metadata-representation-loader.component'; +import { + MetadataRepresentationLoaderComponent +} from './metadata-representation/metadata-representation-loader.component'; import { MetadataRepresentationDirective } from './metadata-representation/metadata-representation.directive'; -import { ListableObjectComponentLoaderComponent } from './object-collection/shared/listable-object/listable-object-component-loader.component'; -import { ItemSearchResultListElementComponent } from './object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; +import { + ListableObjectComponentLoaderComponent +} from './object-collection/shared/listable-object/listable-object-component-loader.component'; +import { + ItemSearchResultListElementComponent +} from './object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; import { ListableObjectDirective } from './object-collection/shared/listable-object/listable-object.directive'; -import { ItemMetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component'; +import { + ItemMetadataRepresentationListElementComponent +} from './object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component'; import { PageWithSidebarComponent } from './sidebar/page-with-sidebar.component'; import { SidebarDropdownComponent } from './sidebar/sidebar-dropdown.component'; import { SidebarFilterComponent } from './sidebar/filter/sidebar-filter.component'; import { SidebarFilterSelectedOptionComponent } from './sidebar/filter/sidebar-filter-selected-option.component'; -import { SelectableListItemControlComponent } from './object-collection/shared/selectable-list-item-control/selectable-list-item-control.component'; -import { ImportableListItemControlComponent } from './object-collection/shared/importable-list-item-control/importable-list-item-control.component'; +import { + SelectableListItemControlComponent +} from './object-collection/shared/selectable-list-item-control/selectable-list-item-control.component'; +import { + ImportableListItemControlComponent +} from './object-collection/shared/importable-list-item-control/importable-list-item-control.component'; import { ItemVersionsComponent } from './item/item-versions/item-versions.component'; import { SortablejsModule } from 'ngx-sortablejs'; import { LogInContainerComponent } from './log-in/container/log-in-container.component'; @@ -135,10 +216,16 @@ import { ItemVersionsNoticeComponent } from './item/item-versions/notice/item-ve import { FileValidator } from './utils/require-file.validator'; import { FileValueAccessorDirective } from './utils/file-value-accessor.directive'; import { FileSectionComponent } from '../item-page/simple/field-components/file-section/file-section.component'; -import { ModifyItemOverviewComponent } from '../item-page/edit-item-page/modify-item-overview/modify-item-overview.component'; -import { ClaimedTaskActionsLoaderComponent } from './mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component'; +import { + ModifyItemOverviewComponent +} from '../item-page/edit-item-page/modify-item-overview/modify-item-overview.component'; +import { + ClaimedTaskActionsLoaderComponent +} from './mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component'; import { ClaimedTaskActionsDirective } from './mydspace-actions/claimed-task/switcher/claimed-task-actions.directive'; -import { ClaimedTaskActionsEditMetadataComponent } from './mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component'; +import { + ClaimedTaskActionsEditMetadataComponent +} from './mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component'; import { ImpersonateNavbarComponent } from './impersonate-navbar/impersonate-navbar.component'; import { NgForTrackByIdDirective } from './ng-for-track-by-id.directive'; import { FileDownloadLinkComponent } from './file-download-link/file-download-link.component'; @@ -146,34 +233,63 @@ import { CollectionDropdownComponent } from './collection-dropdown/collection-dr import { EntityDropdownComponent } from './entity-dropdown/entity-dropdown.component'; import { VocabularyTreeviewComponent } from './vocabulary-treeview/vocabulary-treeview.component'; import { CurationFormComponent } from '../curation-form/curation-form.component'; -import { PublicationSidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/item-types/publication/publication-sidebar-search-list-element.component'; -import { SidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/sidebar-search-list-element.component'; -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 { + PublicationSidebarSearchListElementComponent +} from './object-list/sidebar-search-list-element/item-types/publication/publication-sidebar-search-list-element.component'; +import { + SidebarSearchListElementComponent +} from './object-list/sidebar-search-list-element/sidebar-search-list-element.component'; +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 { + ValidationSuggestionsComponent +} from './input-suggestions/validation-suggestions/validation-suggestions.component'; import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component'; -import { ItemSearchResultGridElementComponent } from './object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component'; +import { + ItemSearchResultGridElementComponent +} from './object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component'; import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component'; -import { GenericItemPageFieldComponent } from '../item-page/simple/field-components/specific-field/generic/generic-item-page-field.component'; -import { MetadataRepresentationListComponent } from '../item-page/simple/metadata-representation-list/metadata-representation-list.component'; +import { + GenericItemPageFieldComponent +} from '../item-page/simple/field-components/specific-field/generic/generic-item-page-field.component'; +import { + MetadataRepresentationListComponent +} from '../item-page/simple/metadata-representation-list/metadata-representation-list.component'; import { RelatedItemsComponent } from '../item-page/simple/related-items/related-items-component'; import { LinkMenuItemComponent } from './menu/menu-item/link-menu-item.component'; import { OnClickMenuItemComponent } from './menu/menu-item/onclick-menu-item.component'; import { TextMenuItemComponent } from './menu/menu-item/text-menu-item.component'; import { SearchNavbarComponent } from '../search-navbar/search-navbar.component'; -import { ItemVersionsSummaryModalComponent } from './item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component'; -import { ItemVersionsDeleteModalComponent } from './item/item-versions/item-versions-delete-modal/item-versions-delete-modal.component'; +import { + ItemVersionsSummaryModalComponent +} from './item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component'; +import { + ItemVersionsDeleteModalComponent +} from './item/item-versions/item-versions-delete-modal/item-versions-delete-modal.component'; import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/scope-selector-modal.component'; -import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component'; +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 { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; -import { ClaimItemSelectorComponent } from './dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component'; +import { + ThemedItemListPreviewComponent +} from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; +import { + ClaimItemSelectorComponent +} from './dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component'; import { ExternalLinkMenuItemComponent } from './menu/menu-item/external-link-menu-item.component'; +import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -410,6 +526,7 @@ const SHARED_ITEM_PAGE_COMPONENTS = [ MetadataValuesComponent, DsoPageEditButtonComponent, DsoPageVersionButtonComponent, + PersonPageClaimButtonComponent, ItemAlertsComponent, GenericItemPageFieldComponent, MetadataRepresentationListComponent, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index e4875d153e..868f79e490 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2762,6 +2762,8 @@ "person.page.lastname": "Last Name", + "person.page.name": "Name", + "person.page.link.full": "Show all metadata", "person.page.orcid": "ORCID", From 853dcecfb8e5cad295ae7f15ecfaf5433ae408f1 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 10 May 2022 18:14:00 +0200 Subject: [PATCH 336/570] [CST-5307] Refactoring and adding missing unit tests --- .../core/breadcrumbs/dso-name.service.spec.ts | 31 +- .../researcher-profile.service.spec.ts | 290 +++++++++++++++++ .../profile/researcher-profile.service.ts | 208 ++++++------ .../profile-claim.service.spec.ts | 215 ++++++++++++ .../profile-claim/profile-claim.service.ts | 59 ++-- ...ile-page-researcher-form.component.spec.ts | 7 +- .../profile-page-researcher-form.component.ts | 20 +- .../profile-page.component.spec.ts | 306 +++++++++++------- .../profile-page/profile-page.component.ts | 21 +- yarn.lock | 5 - 10 files changed, 876 insertions(+), 286 deletions(-) create mode 100644 src/app/core/profile/researcher-profile.service.spec.ts create mode 100644 src/app/profile-page/profile-claim/profile-claim.service.spec.ts diff --git a/src/app/core/breadcrumbs/dso-name.service.spec.ts b/src/app/core/breadcrumbs/dso-name.service.spec.ts index 7a399ce748..9f2f76599a 100644 --- a/src/app/core/breadcrumbs/dso-name.service.spec.ts +++ b/src/app/core/breadcrumbs/dso-name.service.spec.ts @@ -78,15 +78,32 @@ describe(`DSONameService`, () => { }); describe(`factories.Person`, () => { - beforeEach(() => { - spyOn(mockPerson, 'firstMetadataValue').and.returnValues(...mockPersonName.split(', ')); + describe(`with person.familyName and person.givenName`, () => { + beforeEach(() => { + spyOn(mockPerson, 'firstMetadataValue').and.returnValues(...mockPersonName.split(', ')); + }); + + it(`should return 'person.familyName, person.givenName'`, () => { + const result = (service as any).factories.Person(mockPerson); + expect(result).toBe(mockPersonName); + expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName'); + expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName'); + expect(mockPerson.firstMetadataValue).not.toHaveBeenCalledWith('dc.title'); + }); }); - it(`should return 'person.familyName, person.givenName'`, () => { - const result = (service as any).factories.Person(mockPerson); - expect(result).toBe(mockPersonName); - expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName'); - expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName'); + describe(`without person.familyName and person.givenName`, () => { + beforeEach(() => { + spyOn(mockPerson, 'firstMetadataValue').and.returnValues(undefined, undefined, mockPersonName); + }); + + it(`should return dc.title`, () => { + const result = (service as any).factories.Person(mockPerson); + expect(result).toBe(mockPersonName); + expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName'); + expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName'); + expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('dc.title'); + }); }); }); diff --git a/src/app/core/profile/researcher-profile.service.spec.ts b/src/app/core/profile/researcher-profile.service.spec.ts new file mode 100644 index 0000000000..103bae2719 --- /dev/null +++ b/src/app/core/profile/researcher-profile.service.spec.ts @@ -0,0 +1,290 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; + +import { cold, getTestScheduler, hot } from 'jasmine-marbles'; +import { of as observableOf } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; + +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { RequestService } from '../data/request.service'; +import { PageInfo } from '../shared/page-info.model'; +import { buildPaginatedList } from '../data/paginated-list.model'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { RestResponse } from '../cache/response.models'; +import { RequestEntry } from '../data/request-entry.model'; +import { ResearcherProfileService } from './researcher-profile.service'; +import { RouterMock } from '../../shared/mocks/router.mock'; +import { ResearcherProfile } from './model/researcher-profile.model'; +import { Item } from '../shared/item.model'; +import { ReplaceOperation } from 'fast-json-patch'; +import { HttpOptions } from '../dspace-rest/dspace-rest.service'; +import { PostRequest } from '../data/request.models'; + +describe('ResearcherProfileService', () => { + let scheduler: TestScheduler; + let service: ResearcherProfileService; + let serviceAsAny: any; + let requestService: RequestService; + let rdbService: RemoteDataBuildService; + let objectCache: ObjectCacheService; + let halService: HALEndpointService; + let responseCacheEntry: RequestEntry; + + const researcherProfileId = 'beef9946-rt56-479e-8f11-b90cbe9f7241'; + const itemId = 'beef9946-rt56-479e-8f11-b90cbe9f7241'; + const researcherProfileItem: Item = Object.assign(new Item(), { + id: itemId, + _links: { + self: { + href: `https://rest.api/rest/api/items/${itemId}` + }, + } + }); + const researcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), { + id: researcherProfileId, + visible: false, + type: 'profile', + _links: { + item: { + href: `https://rest.api/rest/api/profiles/${researcherProfileId}/item` + }, + self: { + href: `https://rest.api/rest/api/profiles/${researcherProfileId}` + }, + } + }); + + const researcherProfilePatched: ResearcherProfile = Object.assign(new ResearcherProfile(), { + id: researcherProfileId, + visible: true, + type: 'profile', + _links: { + item: { + href: `https://rest.api/rest/api/profiles/${researcherProfileId}/item` + }, + self: { + href: `https://rest.api/rest/api/profiles/${researcherProfileId}` + }, + } + }); + + const researcherProfileId2 = 'agbf9946-f4ce-479e-8f11-b90cbe9f7241'; + const anotherResearcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), { + id: researcherProfileId2, + visible: false, + type: 'profile', + _links: { + self: { + href: `https://rest.api/rest/api/profiles/${researcherProfileId2}` + }, + } + }); + const endpointURL = `https://rest.api/rest/api/profiles`; + const sourceUri = `https://rest.api/rest/api/external-source/profile`; + const requestURL = `https://rest.api/rest/api/profiles/${researcherProfileId}`; + const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; + + const pageInfo = new PageInfo(); + const array = [researcherProfile, anotherResearcherProfile]; + const paginatedList = buildPaginatedList(pageInfo, array); + const researcherProfileRD = createSuccessfulRemoteDataObject(researcherProfile); + const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList); + + beforeEach(() => { + scheduler = getTestScheduler(); + + halService = jasmine.createSpyObj('halService', { + getEndpoint: cold('a', { a: endpointURL }) + }); + + responseCacheEntry = new RequestEntry(); + responseCacheEntry.request = { href: 'https://rest.api/' } as any; + responseCacheEntry.response = new RestResponse(true, 200, 'Success'); + + requestService = jasmine.createSpyObj('requestService', { + generateRequestId: requestUUID, + send: true, + removeByHrefSubstring: {}, + getByHref: observableOf(responseCacheEntry), + getByUUID: observableOf(responseCacheEntry), + setStaleByHrefSubstring: jasmine.createSpy('setStaleByHrefSubstring') + }); + rdbService = jasmine.createSpyObj('rdbService', { + buildSingle: hot('a|', { + a: researcherProfileRD + }), + buildList: hot('a|', { + a: paginatedListRD + }), + buildFromRequestUUID: hot('a|', { + a: researcherProfileRD + }) + }); + objectCache = {} as ObjectCacheService; + const notificationsService = {} as NotificationsService; + const http = {} as HttpClient; + const comparator = {} as any; + const routerStub: any = new RouterMock(); + const itemService = jasmine.createSpyObj('ItemService', { + findByHref: jasmine.createSpy('findByHref') + }); + + service = new ResearcherProfileService( + requestService, + rdbService, + objectCache, + halService, + notificationsService, + http, + routerStub, + comparator, + itemService + ); + serviceAsAny = service; + + spyOn((service as any).dataService, 'create').and.callThrough(); + spyOn((service as any).dataService, 'delete').and.callThrough(); + spyOn((service as any).dataService, 'update').and.callThrough(); + spyOn((service as any).dataService, 'findById').and.callThrough(); + spyOn((service as any).dataService, 'findByHref').and.callThrough(); + spyOn((service as any).dataService, 'searchBy').and.callThrough(); + spyOn((service as any).dataService, 'getLinkPath').and.returnValue(observableOf(endpointURL)); + + }); + + describe('findById', () => { + it('should proxy the call to dataservice.findById with eperson UUID', () => { + scheduler.schedule(() => service.findById(researcherProfileId)); + scheduler.flush(); + + expect((service as any).dataService.findById).toHaveBeenCalledWith(researcherProfileId, true, true); + }); + + it('should return a ResearcherProfile object with the given id', () => { + const result = service.findById(researcherProfileId); + const expected = cold('a|', { + a: researcherProfileRD + }); + expect(result).toBeObservable(expected); + }); + }); + + describe('create', () => { + it('should proxy the call to dataservice.create with eperson UUID', () => { + scheduler.schedule(() => service.create()); + scheduler.flush(); + + expect((service as any).dataService.create).toHaveBeenCalled(); + }); + + it('should return the RemoteData created', () => { + const result = service.create(); + const expected = cold('a|', { + a: researcherProfileRD + }); + expect(result).toBeObservable(expected); + }); + }); + + describe('delete', () => { + it('should proxy the call to dataservice.delete', () => { + scheduler.schedule(() => service.delete(researcherProfile)); + scheduler.flush(); + + expect((service as any).dataService.delete).toHaveBeenCalledWith(researcherProfile.id); + }); + }); + + describe('findRelatedItemId', () => { + describe('with a related item', () => { + + beforeEach(() => { + (service as any).itemService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfileItem)); + }); + + it('should proxy the call to dataservice.findById with eperson UUID', () => { + scheduler.schedule(() => service.findRelatedItemId(researcherProfile)); + scheduler.flush(); + + expect((service as any).itemService.findByHref).toHaveBeenCalledWith(researcherProfile._links.item.href, false); + }); + + it('should return a ResearcherProfile object with the given id', () => { + const result = service.findRelatedItemId(researcherProfile); + const expected = cold('(a|)', { + a: itemId + }); + expect(result).toBeObservable(expected); + }); + }); + + describe('without a related item', () => { + + beforeEach(() => { + (service as any).itemService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(null)); + }); + + it('should proxy the call to dataservice.findById with eperson UUID', () => { + scheduler.schedule(() => service.findRelatedItemId(researcherProfile)); + scheduler.flush(); + + expect((service as any).itemService.findByHref).toHaveBeenCalledWith(researcherProfile._links.item.href, false); + }); + + it('should return a ResearcherProfile object with the given id', () => { + const result = service.findRelatedItemId(researcherProfile); + const expected = cold('(a|)', { + a: undefined + }); + expect(result).toBeObservable(expected); + }); + }); + }); + + describe('setVisibility', () => { + let patchSpy; + beforeEach(() => { + spyOn((service as any), 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); + spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); + }); + + it('should proxy the call to dataservice.patch', () => { + const replaceOperation: ReplaceOperation = { + path: '/visible', + op: 'replace', + value: true + }; + + scheduler.schedule(() => service.setVisibility(researcherProfile, true)); + scheduler.flush(); + + expect((service as any).patch).toHaveBeenCalledWith(researcherProfile, [replaceOperation]); + }); + }); + + describe('createFromExternalSource', () => { + let patchSpy; + beforeEach(() => { + spyOn((service as any), 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); + spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); + }); + + it('should proxy the call to dataservice.patch', () => { + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'text/uri-list'); + options.headers = headers; + const request = new PostRequest(requestUUID, endpointURL, sourceUri, options); + + scheduler.schedule(() => service.createFromExternalSource(sourceUri)); + scheduler.flush(); + + expect((service as any).requestService.send).toHaveBeenCalledWith(request); + expect((service as any).rdbService.buildFromRequestUUID).toHaveBeenCalledWith(requestUUID); + + }); + }); + +}); diff --git a/src/app/core/profile/researcher-profile.service.ts b/src/app/core/profile/researcher-profile.service.ts index 0220afb964..eab6ea3d48 100644 --- a/src/app/core/profile/researcher-profile.service.ts +++ b/src/app/core/profile/researcher-profile.service.ts @@ -2,27 +2,25 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; + import { Store } from '@ngrx/store'; -import { Operation, RemoveOperation, ReplaceOperation } from 'fast-json-patch'; -import { combineLatest, Observable, of as observableOf } from 'rxjs'; +import { Operation, ReplaceOperation } from 'fast-json-patch'; +import { Observable, of as observableOf } from 'rxjs'; import { catchError, find, map, switchMap, tap } from 'rxjs/operators'; -import { environment } from '../../../environments/environment'; + import { NotificationsService } from '../../shared/notifications/notifications.service'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { ConfigurationDataService } from '../data/configuration-data.service'; import { DataService } from '../data/data.service'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { ItemDataService } from '../data/item-data.service'; import { RemoteData } from '../data/remote-data'; import { RequestService } from '../data/request.service'; -import { ConfigurationProperty } from '../shared/configuration-property.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { Item } from '../shared/item.model'; import { NoContent } from '../shared/NoContent.model'; import { - getFinishedRemoteData, + getAllCompletedRemoteData, getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../shared/operators'; @@ -31,25 +29,26 @@ import { RESEARCHER_PROFILE } from './model/researcher-profile.resource-type'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { PostRequest } from '../data/request.models'; import { hasValue } from '../../shared/empty.util'; -import {CoreState} from '../core-state.model'; +import { CoreState } from '../core-state.model'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; /** * A private DataService implementation to delegate specific methods to. */ class ResearcherProfileServiceImpl extends DataService { - protected linkPath = 'profiles'; + protected linkPath = 'profiles'; - constructor( - protected requestService: RequestService, - protected rdbService: RemoteDataBuildService, - protected store: Store, - protected objectCache: ObjectCacheService, - protected halService: HALEndpointService, - protected notificationsService: NotificationsService, - protected http: HttpClient, - protected comparator: DefaultChangeAnalyzer) { - super(); - } + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } } @@ -60,100 +59,102 @@ class ResearcherProfileServiceImpl extends DataService { @dataService(RESEARCHER_PROFILE) export class ResearcherProfileService { - dataService: ResearcherProfileServiceImpl; + dataService: ResearcherProfileServiceImpl; - responseMsToLive: number = 10 * 1000; + responseMsToLive: number = 10 * 1000; - constructor( - protected requestService: RequestService, - protected rdbService: RemoteDataBuildService, - protected store: Store, - protected objectCache: ObjectCacheService, - protected halService: HALEndpointService, - protected notificationsService: NotificationsService, - protected http: HttpClient, - protected router: Router, - protected comparator: DefaultChangeAnalyzer, - protected itemService: ItemDataService, - protected configurationService: ConfigurationDataService ) { + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected router: Router, + protected comparator: DefaultChangeAnalyzer, + protected itemService: ItemDataService) { - this.dataService = new ResearcherProfileServiceImpl(requestService, rdbService, store, objectCache, halService, - notificationsService, http, comparator); + this.dataService = new ResearcherProfileServiceImpl(requestService, rdbService, null, objectCache, halService, + notificationsService, http, comparator); - } + } - /** - * Find the researcher profile with the given uuid. - * - * @param uuid the profile uuid - */ - findById(uuid: string): Observable { - return this.dataService.findById(uuid, false) - .pipe ( getFinishedRemoteData(), - map((remoteData) => remoteData.payload)); - } + /** + * Find the researcher profile with the given uuid. + * + * @param uuid the profile uuid + * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's + * no valid cached version. Defaults to true + * @param reRequestOnStale Whether or not the request should automatically be re- + * requested after the response becomes stale + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which + * {@link HALLink}s should be automatically resolved + */ + public findById(uuid: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable> { + return this.dataService.findById(uuid, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe( + getAllCompletedRemoteData(), + ); + } - /** - * Create a new researcher profile for the current user. - */ - create(): Observable> { - return this.dataService.create( new ResearcherProfile()); - } + /** + * Create a new researcher profile for the current user. + */ + public create(): Observable> { + return this.dataService.create(new ResearcherProfile()); + } - /** - * Delete a researcher profile. - * - * @param researcherProfile the profile to delete - */ - delete(researcherProfile: ResearcherProfile): Observable { - return this.dataService.delete(researcherProfile.id).pipe( - getFirstCompletedRemoteData(), - tap((response: RemoteData) => { - if (response.isSuccess) { - this.requestService.setStaleByHrefSubstring(researcherProfile._links.self.href); - } + /** + * Delete a researcher profile. + * + * @param researcherProfile the profile to delete + */ + public delete(researcherProfile: ResearcherProfile): Observable { + return this.dataService.delete(researcherProfile.id).pipe( + getFirstCompletedRemoteData(), + tap((response: RemoteData) => { + if (response.isSuccess) { + this.requestService.setStaleByHrefSubstring(researcherProfile._links.self.href); + } + }), + map((response: RemoteData) => response.isSuccess) + ); + } + + /** + * Find the item id related to the given researcher profile. + * + * @param researcherProfile the profile to find for + */ + public findRelatedItemId(researcherProfile: ResearcherProfile): Observable { + return this.itemService.findByHref(researcherProfile._links.item.href, false) + .pipe(getFirstSucceededRemoteDataPayload(), + catchError((error) => { + console.debug(error); + return observableOf(null); }), - map((response: RemoteData) => response.isSuccess) + map((item) => item?.id) ); - } + } - /** - * Find the item id related to the given researcher profile. - * - * @param researcherProfile the profile to find for - */ - findRelatedItemId( researcherProfile: ResearcherProfile ): Observable { - return this.itemService.findByHref(researcherProfile._links.item.href, false) - .pipe (getFirstSucceededRemoteDataPayload(), - catchError((error) => { - console.debug(error); - return observableOf(null); - }), - map((item) => item != null ? item.id : null )); - } + /** + * Change the visibility of the given researcher profile setting the given value. + * + * @param researcherProfile the profile to update + * @param visible the visibility value to set + */ + public setVisibility(researcherProfile: ResearcherProfile, visible: boolean): Observable { - /** - * Change the visibility of the given researcher profile setting the given value. - * - * @param researcherProfile the profile to update - * @param visible the visibility value to set - */ - setVisibility(researcherProfile: ResearcherProfile, visible: boolean): Observable { + const replaceOperation: ReplaceOperation = { + path: '/visible', + op: 'replace', + value: visible + }; - const replaceOperation: ReplaceOperation = { - path: '/visible', - op: 'replace', - value: visible - }; - - return this.patch(researcherProfile, [replaceOperation]).pipe ( - switchMap( ( ) => this.findById(researcherProfile.id)) - ); - } - - patch(researcherProfile: ResearcherProfile, operations: Operation[]): Observable> { - return this.dataService.patch(researcherProfile, operations); - } + return this.patch(researcherProfile, [replaceOperation]).pipe( + switchMap(() => this.findById(researcherProfile.id)), + getFirstSucceededRemoteDataPayload() + ); + } /** * Creates a researcher profile starting from an external source URI @@ -179,4 +180,7 @@ export class ResearcherProfileService { return this.rdbService.buildFromRequestUUID(requestId); } + private patch(researcherProfile: ResearcherProfile, operations: Operation[]): Observable> { + return this.dataService.patch(researcherProfile, operations); + } } diff --git a/src/app/profile-page/profile-claim/profile-claim.service.spec.ts b/src/app/profile-page/profile-claim/profile-claim.service.spec.ts new file mode 100644 index 0000000000..4030c7900c --- /dev/null +++ b/src/app/profile-page/profile-claim/profile-claim.service.spec.ts @@ -0,0 +1,215 @@ +import { cold, getTestScheduler } from 'jasmine-marbles'; + +import { of as observableOf } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; + +import { ProfileClaimService } from './profile-claim.service'; +import { SearchService } from '../../core/shared/search/search.service'; +import { ItemSearchResult } from '../../shared/object-collection/shared/item-search-result.model'; +import { SearchObjects } from '../../shared/search/models/search-objects.model'; +import { Item } from '../../core/shared/item.model'; +import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { EPerson } from '../../core/eperson/models/eperson.model'; + +describe('ProfileClaimService', () => { + let scheduler: TestScheduler; + let service: ProfileClaimService; + let serviceAsAny: any; + let searchService: jasmine.SpyObj; + + const eperson: EPerson = Object.assign(new EPerson(), { + id: 'id', + metadata: { + 'eperson.firstname': [ + { + value: 'John' + } + ], + 'eperson.lastname': [ + { + value: 'Doe' + }, + ], + }, + email: 'fake@email.com' + }); + const item1: Item = Object.assign(new Item(), { + uuid: 'e1c51c69-896d-42dc-8221-1d5f2ad5516e', + metadata: { + 'person.email': [ + { + value: 'fake@email.com' + } + ], + 'person.familyName': [ + { + value: 'Doe' + } + ], + 'person.givenName': [ + { + value: 'John' + } + ] + }, + _links: { + self: { + href: 'item-href' + } + } + }); + const item2: Item = Object.assign(new Item(), { + uuid: 'c8279647-1acc-41ae-b036-951d5f65649b', + metadata: { + 'person.email': [ + { + value: 'fake2@email.com' + } + ], + 'dc.title': [ + { + value: 'John, Doe' + } + ] + }, + _links: { + self: { + href: 'item-href' + } + } + }); + const item3: Item = Object.assign(new Item(), { + uuid: 'c8279647-1acc-41ae-b036-951d5f65649b', + metadata: { + 'person.email': [ + { + value: 'fake3@email.com' + } + ], + 'dc.title': [ + { + value: 'John, Doe' + } + ] + }, + _links: { + self: { + href: 'item-href' + } + } + }); + + const searchResult1 = Object.assign(new ItemSearchResult(), { indexableObject: item1 }); + const searchResult2 = Object.assign(new ItemSearchResult(), { indexableObject: item2 }); + const searchResult3 = Object.assign(new ItemSearchResult(), { indexableObject: item3 }); + + const searchResult = Object.assign(new SearchObjects(), { + page: [searchResult1, searchResult2, searchResult3] + }); + const emptySearchResult = Object.assign(new SearchObjects(), { + page: [] + }); + const searchResultRD = createSuccessfulRemoteDataObject(searchResult); + const emptyRearchResultRD = createSuccessfulRemoteDataObject(emptySearchResult); + + beforeEach(() => { + scheduler = getTestScheduler(); + + searchService = jasmine.createSpyObj('SearchService', { + search: jasmine.createSpy('search') + }); + + service = new ProfileClaimService(searchService); + serviceAsAny = service; + }); + + describe('hasProfilesToSuggest', () => { + + describe('when has suggestions', () => { + beforeEach(() => { + spyOn(service, 'search').and.returnValue(observableOf(searchResultRD)); + }); + + it('should return true', () => { + const result = service.hasProfilesToSuggest(eperson); + const expected = cold('(a|)', { + a: true + }); + expect(result).toBeObservable(expected); + }); + + }); + + describe('when has not suggestions', () => { + beforeEach(() => { + spyOn(service, 'search').and.returnValue(observableOf(emptyRearchResultRD)); + }); + + it('should return false', () => { + const result = service.hasProfilesToSuggest(eperson); + const expected = cold('(a|)', { + a: false + }); + expect(result).toBeObservable(expected); + }); + + }); + + describe('when has not valid eperson', () => { + it('should return false', () => { + const result = service.hasProfilesToSuggest(null); + const expected = cold('(a|)', { + a: false + }); + expect(result).toBeObservable(expected); + }); + + }); + + }); + + describe('search', () => { + + describe('when has search results', () => { + beforeEach(() => { + searchService.search.and.returnValue(observableOf(searchResultRD)); + }); + + it('should return the proper search object', () => { + const result = service.search(eperson); + const expected = cold('(a|)', { + a: searchResultRD + }); + expect(result).toBeObservable(expected); + }); + + }); + + describe('when has not suggestions', () => { + beforeEach(() => { + searchService.search.and.returnValue(observableOf(emptyRearchResultRD)); + }); + + it('should return null', () => { + const result = service.search(eperson); + const expected = cold('(a|)', { + a: emptyRearchResultRD + }); + expect(result).toBeObservable(expected); + }); + + }); + + describe('when has not valid eperson', () => { + it('should return null', () => { + const result = service.search(null); + const expected = cold('(a|)', { + a: null + }); + expect(result).toBeObservable(expected); + }); + + }); + + }); +}); diff --git a/src/app/profile-page/profile-claim/profile-claim.service.ts b/src/app/profile-page/profile-claim/profile-claim.service.ts index 9ee2462778..a61404540b 100644 --- a/src/app/profile-page/profile-claim/profile-claim.service.ts +++ b/src/app/profile-page/profile-claim/profile-claim.service.ts @@ -1,16 +1,16 @@ import { Injectable } from '@angular/core'; + import { Observable, of } from 'rxjs'; -import { mergeMap, take } from 'rxjs/operators'; -import { ConfigurationDataService } from '../../core/data/configuration-data.service'; -import { PaginatedList } from '../../core/data/paginated-list.model'; +import { map } from 'rxjs/operators'; + import { RemoteData } from '../../core/data/remote-data'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { SearchService } from '../../core/shared/search/search.service'; -import { hasValue } from '../../shared/empty.util'; +import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; -import { SearchResult } from '../../shared/search/models/search-result.model'; -import { getFirstSucceededRemoteData } from './../../core/shared/operators'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { SearchObjects } from '../../shared/search/models/search-objects.model'; /** * Service that handle profiles claim. @@ -18,8 +18,7 @@ import { getFirstSucceededRemoteData } from './../../core/shared/operators'; @Injectable() export class ProfileClaimService { - constructor(private searchService: SearchService, - private configurationService: ConfigurationDataService) { + constructor(private searchService: SearchService) { } /** @@ -27,27 +26,21 @@ export class ProfileClaimService { * * @param eperson the eperson */ - canClaimProfiles(eperson: EPerson): Observable { - - const query = this.personQueryData(eperson); - - if (!hasValue(query) || query.length === 0) { - return of(false); - } - - return this.lookup(query).pipe( - mergeMap((rd: RemoteData>>) => of(rd.payload.totalElements > 0)) + hasProfilesToSuggest(eperson: EPerson): Observable { + return this.search(eperson).pipe( + map((rd: RemoteData>) => { + return isNotEmpty(rd) && rd.hasSucceeded && rd.payload?.page?.length > 0; + }) ); - } /** * Returns profiles that could be associated with the given user. * @param eperson the user */ - search(eperson: EPerson): Observable>>> { + search(eperson: EPerson): Observable>> { const query = this.personQueryData(eperson); - if (!hasValue(query) || query.length === 0) { + if (isEmpty(query)) { return of(null); } return this.lookup(query); @@ -57,21 +50,31 @@ export class ProfileClaimService { * Search object by the given query. * @param query the query for the search */ - private lookup(query: string): Observable>>> { - if (!hasValue(query)) { + private lookup(query: string): Observable>> { + if (isEmpty(query)) { return of(null); } return this.searchService.search(new PaginatedSearchOptions({ configuration: 'eperson_claims', query: query - })) - .pipe( - getFirstSucceededRemoteData(), - take(1)); + })).pipe( + getFirstCompletedRemoteData() + ); } + /** + * Return the search query for person lookup, from the given eperson + * + * @param eperson The eperson to use for the lookup + */ private personQueryData(eperson: EPerson): string { - return 'dc.title:' + eperson.name; + if (eperson) { + const firstname = eperson.firstMetadataValue('eperson.firstname'); + const lastname = eperson.firstMetadataValue('eperson.lastname'); + return 'dc.title:' + eperson.name + ' OR (person.familyName:' + lastname + ' AND person.givenName:' + firstname + ')'; + } else { + return null; + } } } diff --git a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.spec.ts b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.spec.ts index d12c445ce4..b928b20eef 100644 --- a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.spec.ts +++ b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.spec.ts @@ -16,6 +16,7 @@ import { ProfilePageResearcherFormComponent } from './profile-page-researcher-fo import { ProfileClaimService } from '../profile-claim/profile-claim.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { AuthService } from 'src/app/core/auth/auth.service'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; describe('ProfilePageResearcherFormComponent', () => { @@ -51,7 +52,7 @@ describe('ProfilePageResearcherFormComponent', () => { }); researcherProfileService = jasmine.createSpyObj('researcherProfileService', { - findById: observableOf(profile), + findById: createSuccessfulRemoteDataObject$(profile), create: observableOf(profile), setVisibility: observableOf(profile), delete: observableOf(true), @@ -61,7 +62,7 @@ describe('ProfilePageResearcherFormComponent', () => { notificationsServiceStub = new NotificationsServiceStub(); profileClaimService = jasmine.createSpyObj('profileClaimService', { - canClaimProfiles: observableOf(false), + hasProfilesToSuggest: observableOf(false), }); } @@ -91,7 +92,7 @@ describe('ProfilePageResearcherFormComponent', () => { }); it('should search the researcher profile for the current user', () => { - expect(researcherProfileService.findById).toHaveBeenCalledWith(user.id); + expect(researcherProfileService.findById).toHaveBeenCalledWith(user.id, false); }); describe('createProfile', () => { diff --git a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts index 9bb3028ff4..a1887f8b31 100644 --- a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts +++ b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts @@ -4,17 +4,18 @@ import { Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, Observable } from 'rxjs'; -import { filter, mergeMap, switchMap, take, tap } from 'rxjs/operators'; +import { mergeMap, switchMap, take, tap } from 'rxjs/operators'; -import { getFirstCompletedRemoteData } from '../../core/shared/operators'; -import { ClaimItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; +import { + ClaimItemSelectorComponent +} from '../../shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { AuthService } from '../../core/auth/auth.service'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model'; import { ResearcherProfileService } from '../../core/profile/researcher-profile.service'; import { ProfileClaimService } from '../profile-claim/profile-claim.service'; -import { isNotEmpty } from '../../shared/empty.util'; @Component({ selector: 'ds-profile-page-researcher-form', @@ -77,10 +78,10 @@ export class ProfilePageResearcherFormComponent implements OnInit { this.processingCreate$.next(true); this.authService.getAuthenticatedUserFromStore().pipe( - switchMap((currentUser) => this.profileClaimService.canClaimProfiles(currentUser))) - .subscribe((canClaimProfiles) => { + switchMap((currentUser) => this.profileClaimService.hasProfilesToSuggest(currentUser))) + .subscribe((hasProfilesToSuggest) => { - if (canClaimProfiles) { + if (hasProfilesToSuggest) { this.processingCreate$.next(false); const modal = this.modalService.open(ClaimItemSelectorComponent); modal.componentInstance.dso = this.user; @@ -174,9 +175,8 @@ export class ProfilePageResearcherFormComponent implements OnInit { * Initializes the researcherProfile and researcherProfileItemId attributes using the profile of the current user. */ private initResearchProfile(): void { - this.researcherProfileService.findById(this.user.id).pipe( - take(1), - filter((researcherProfile) => isNotEmpty(researcherProfile)), + this.researcherProfileService.findById(this.user.id, false).pipe( + getFirstSucceededRemoteDataPayload(), tap((researcherProfile) => this.researcherProfile$.next(researcherProfile)), mergeMap((researcherProfile) => this.researcherProfileService.findRelatedItemId(researcherProfile)), ).subscribe((itemId: string) => { diff --git a/src/app/profile-page/profile-page.component.spec.ts b/src/app/profile-page/profile-page.component.spec.ts index 84aec0c0f1..fcbd4d9e4a 100644 --- a/src/app/profile-page/profile-page.component.spec.ts +++ b/src/app/profile-page/profile-page.component.spec.ts @@ -11,17 +11,17 @@ import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model'; import { EPersonDataService } from '../core/eperson/eperson-data.service'; import { NotificationsService } from '../shared/notifications/notifications.service'; import { authReducer } from '../core/auth/auth.reducer'; -import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { createPaginatedList } from '../shared/testing/utils.test'; import { BehaviorSubject, of as observableOf } from 'rxjs'; import { AuthService } from '../core/auth/auth.service'; import { RestResponse } from '../core/cache/response.models'; import { provideMockStore } from '@ngrx/store/testing'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; -import { getTestScheduler } from 'jasmine-marbles'; +import { cold, getTestScheduler } from 'jasmine-marbles'; import { By } from '@angular/platform-browser'; -import {ConfigurationDataService} from '../core/data/configuration-data.service'; -import {ConfigurationProperty} from '../core/shared/configuration-property.model'; +import { ConfigurationDataService } from '../core/data/configuration-data.service'; +import { ConfigurationProperty } from '../core/shared/configuration-property.model'; describe('ProfilePageComponent', () => { let component: ProfilePageComponent; @@ -30,16 +30,28 @@ describe('ProfilePageComponent', () => { let initialState: any; let authService; + let authorizationService; let epersonService; let notificationsService; + let configurationService; const canChangePassword = new BehaviorSubject(true); + const validConfiguration = Object.assign(new ConfigurationProperty(), { + name: 'researcher-profile.entity-type', + values: [ + 'Person' + ] + }); + const emptyConfiguration = Object.assign(new ConfigurationProperty(), { + name: 'researcher-profile.entity-type', + values: [] + }); function init() { user = Object.assign(new EPerson(), { id: 'userId', groups: createSuccessfulRemoteDataObject$(createPaginatedList([])), - _links: {self: {href: 'test.com/uuid/1234567654321'}} + _links: { self: { href: 'test.com/uuid/1234567654321' } } }); initialState = { core: { @@ -54,7 +66,7 @@ describe('ProfilePageComponent', () => { } } }; - + authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: canChangePassword }); authService = jasmine.createSpyObj('authService', { getAuthenticatedUserFromStore: observableOf(user) }); @@ -67,6 +79,9 @@ describe('ProfilePageComponent', () => { error: {}, warning: {} }); + configurationService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: jasmine.createSpy('findByPropertyName') + }); } beforeEach(waitForAsync(() => { @@ -82,15 +97,8 @@ describe('ProfilePageComponent', () => { { provide: EPersonDataService, useValue: epersonService }, { provide: NotificationsService, useValue: notificationsService }, { provide: AuthService, useValue: authService }, - { provide: ConfigurationDataService, useValue: jasmine.createSpyObj('configurationDataService', { - findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { - name: 'researcher-profile.entity-type', - values: [ - 'Person' - ] - })) - })}, - { provide: AuthorizationDataService, useValue: jasmine.createSpyObj('authorizationService', { isAuthorized: canChangePassword }) }, + { provide: ConfigurationDataService, useValue: configurationService }, + { provide: AuthorizationDataService, useValue: authorizationService }, provideMockStore({ initialState }), ], schemas: [NO_ERRORS_SCHEMA] @@ -100,148 +108,206 @@ describe('ProfilePageComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ProfilePageComponent); component = fixture.componentInstance; - fixture.detectChanges(); }); - describe('updateProfile', () => { - describe('when the metadata form returns false and the security form returns true', () => { - beforeEach(() => { - component.metadataForm = jasmine.createSpyObj('metadataForm', { - updateProfile: false + describe('', () => { + + beforeEach(() => { + configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(validConfiguration)); + fixture.detectChanges(); + }); + + describe('updateProfile', () => { + describe('when the metadata form returns false and the security form returns true', () => { + beforeEach(() => { + component.metadataForm = jasmine.createSpyObj('metadataForm', { + updateProfile: false + }); + spyOn(component, 'updateSecurity').and.returnValue(true); + component.updateProfile(); + }); + + it('should not display a warning', () => { + expect(notificationsService.warning).not.toHaveBeenCalled(); }); - spyOn(component, 'updateSecurity').and.returnValue(true); - component.updateProfile(); }); - it('should not display a warning', () => { - expect(notificationsService.warning).not.toHaveBeenCalled(); + describe('when the metadata form returns true and the security form returns false', () => { + beforeEach(() => { + component.metadataForm = jasmine.createSpyObj('metadataForm', { + updateProfile: true + }); + component.updateProfile(); + }); + + it('should not display a warning', () => { + expect(notificationsService.warning).not.toHaveBeenCalled(); + }); + }); + + describe('when the metadata form returns true and the security form returns true', () => { + beforeEach(() => { + component.metadataForm = jasmine.createSpyObj('metadataForm', { + updateProfile: true + }); + component.updateProfile(); + }); + + it('should not display a warning', () => { + expect(notificationsService.warning).not.toHaveBeenCalled(); + }); + }); + + describe('when the metadata form returns false and the security form returns false', () => { + beforeEach(() => { + component.metadataForm = jasmine.createSpyObj('metadataForm', { + updateProfile: false + }); + component.updateProfile(); + }); + + it('should display a warning', () => { + expect(notificationsService.warning).toHaveBeenCalled(); + }); }); }); - describe('when the metadata form returns true and the security form returns false', () => { - beforeEach(() => { - component.metadataForm = jasmine.createSpyObj('metadataForm', { - updateProfile: true + describe('updateSecurity', () => { + describe('when no password value present', () => { + let result; + + beforeEach(() => { + component.setPasswordValue(''); + + result = component.updateSecurity(); + }); + + it('should return false', () => { + expect(result).toEqual(false); + }); + + it('should not call epersonService.patch', () => { + expect(epersonService.patch).not.toHaveBeenCalled(); }); - component.updateProfile(); }); - it('should not display a warning', () => { - expect(notificationsService.warning).not.toHaveBeenCalled(); + describe('when password is filled in, but the password is invalid', () => { + let result; + + beforeEach(() => { + component.setPasswordValue('test'); + component.setInvalid(true); + result = component.updateSecurity(); + }); + + it('should return true', () => { + expect(result).toEqual(true); + expect(epersonService.patch).not.toHaveBeenCalled(); + }); + }); + + describe('when password is filled in, and is valid', () => { + let result; + let operations; + + beforeEach(() => { + component.setPasswordValue('testest'); + component.setInvalid(false); + + operations = [{ op: 'add', path: '/password', value: 'testest' }]; + result = component.updateSecurity(); + }); + + it('should return true', () => { + expect(result).toEqual(true); + }); + + it('should return call epersonService.patch', () => { + expect(epersonService.patch).toHaveBeenCalledWith(user, operations); + }); }); }); - describe('when the metadata form returns true and the security form returns true', () => { - beforeEach(() => { - component.metadataForm = jasmine.createSpyObj('metadataForm', { - updateProfile: true + describe('canChangePassword$', () => { + describe('when the user is allowed to change their password', () => { + beforeEach(() => { + canChangePassword.next(true); }); - component.updateProfile(); - }); - it('should not display a warning', () => { - expect(notificationsService.warning).not.toHaveBeenCalled(); - }); - }); - - describe('when the metadata form returns false and the security form returns false', () => { - beforeEach(() => { - component.metadataForm = jasmine.createSpyObj('metadataForm', { - updateProfile: false + it('should contain true', () => { + getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: true }); + }); + + it('should show the security section on the page', () => { + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('.security-section'))).not.toBeNull(); }); - component.updateProfile(); }); - it('should display a warning', () => { - expect(notificationsService.warning).toHaveBeenCalled(); + describe('when the user is not allowed to change their password', () => { + beforeEach(() => { + canChangePassword.next(false); + }); + + it('should contain false', () => { + getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: false }); + }); + + it('should not show the security section on the page', () => { + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('.security-section'))).toBeNull(); + }); }); }); }); - describe('updateSecurity', () => { - describe('when no password value present', () => { - let result; + describe('isResearcherProfileEnabled', () => { + + describe('when configuration service return values', () => { beforeEach(() => { - component.setPasswordValue(''); + configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(validConfiguration)); + fixture.detectChanges(); + }); - result = component.updateSecurity(); + it('should return true', () => { + const result = component.isResearcherProfileEnabled(); + const expected = cold('a', { + a: true + }); + expect(result).toBeObservable(expected); + }); + }); + + describe('when configuration service return no values', () => { + + beforeEach(() => { + configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(emptyConfiguration)); + fixture.detectChanges(); }); it('should return false', () => { - expect(result).toEqual(false); - }); - - it('should not call epersonService.patch', () => { - expect(epersonService.patch).not.toHaveBeenCalled(); + const result = component.isResearcherProfileEnabled(); + const expected = cold('a', { + a: false + }); + expect(result).toBeObservable(expected); }); }); - describe('when password is filled in, but the password is invalid', () => { - let result; + describe('when configuration service return an error', () => { beforeEach(() => { - component.setPasswordValue('test'); - component.setInvalid(true); - result = component.updateSecurity(); - }); - - it('should return true', () => { - expect(result).toEqual(true); - expect(epersonService.patch).not.toHaveBeenCalled(); - }); - }); - - describe('when password is filled in, and is valid', () => { - let result; - let operations; - - beforeEach(() => { - component.setPasswordValue('testest'); - component.setInvalid(false); - - operations = [{ op: 'add', path: '/password', value: 'testest' }]; - result = component.updateSecurity(); - }); - - it('should return true', () => { - expect(result).toEqual(true); - }); - - it('should return call epersonService.patch', () => { - expect(epersonService.patch).toHaveBeenCalledWith(user, operations); - }); - }); - }); - - describe('canChangePassword$', () => { - describe('when the user is allowed to change their password', () => { - beforeEach(() => { - canChangePassword.next(true); - }); - - it('should contain true', () => { - getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: true }); - }); - - it('should show the security section on the page', () => { + configurationService.findByPropertyName.and.returnValue(createFailedRemoteDataObject$()); fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('.security-section'))).not.toBeNull(); - }); - }); - - describe('when the user is not allowed to change their password', () => { - beforeEach(() => { - canChangePassword.next(false); }); - it('should contain false', () => { - getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: false }); - }); - - it('should not show the security section on the page', () => { - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('.security-section'))).toBeNull(); + it('should return false', () => { + const result = component.isResearcherProfileEnabled(); + const expected = cold('a', { + a: false + }); + expect(result).toBeObservable(expected); }); }); }); diff --git a/src/app/profile-page/profile-page.component.ts b/src/app/profile-page/profile-page.component.ts index 9c22c8c950..374fa5220b 100644 --- a/src/app/profile-page/profile-page.component.ts +++ b/src/app/profile-page/profile-page.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import {BehaviorSubject, Observable} from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; import { EPerson } from '../core/eperson/models/eperson.model'; import { ProfilePageMetadataFormComponent } from './profile-page-metadata-form/profile-page-metadata-form.component'; import { NotificationsService } from '../shared/notifications/notifications.service'; @@ -9,18 +9,15 @@ import { RemoteData } from '../core/data/remote-data'; import { PaginatedList } from '../core/data/paginated-list.model'; import { filter, switchMap, tap } from 'rxjs/operators'; import { EPersonDataService } from '../core/eperson/eperson-data.service'; -import { - getAllSucceededRemoteData, - getRemoteDataPayload, - getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload -} from '../core/shared/operators'; +import { getAllSucceededRemoteData, getFirstCompletedRemoteData, getRemoteDataPayload } from '../core/shared/operators'; import { hasValue, isNotEmpty } from '../shared/empty.util'; import { followLink } from '../shared/utils/follow-link-config.model'; import { AuthService } from '../core/auth/auth.service'; import { Operation } from 'fast-json-patch'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import {ConfigurationDataService} from '../core/data/configuration-data.service'; +import { ConfigurationDataService } from '../core/data/configuration-data.service'; +import { ConfigurationProperty } from '../core/shared/configuration-property.model'; @Component({ selector: 'ds-profile-page', @@ -94,8 +91,10 @@ export class ProfilePageComponent implements OnInit { this.canChangePassword$ = this.user$.pipe(switchMap((user: EPerson) => this.authorizationService.isAuthorized(FeatureID.CanChangePassword, user._links.self.href))); this.configurationService.findByPropertyName('researcher-profile.entity-type').pipe( - getFirstSucceededRemoteDataPayload() - ).subscribe(() => this.isResearcherProfileEnabled$.next(true)); + getFirstCompletedRemoteData() + ).subscribe((configRD: RemoteData) => { + this.isResearcherProfileEnabled$.next(configRD.hasSucceeded && configRD.payload.values.length > 0); + }); } /** @@ -175,8 +174,8 @@ export class ProfilePageComponent implements OnInit { /** * Returns true if the researcher profile feature is enabled, false otherwise. */ - isResearcherProfileEnabled(){ - return this.isResearcherProfileEnabled$; + isResearcherProfileEnabled(): Observable { + return this.isResearcherProfileEnabled$.asObservable(); } } diff --git a/yarn.lock b/yarn.lock index c06853e625..65c1a61aba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8884,11 +8884,6 @@ ngx-ui-switch@^11.0.1: resolved "https://registry.yarnpkg.com/ngx-ui-switch/-/ngx-ui-switch-11.0.1.tgz#c7f1e97ebe698f827a26f49951b50492b22c7839" integrity sha512-N8QYT/wW+xJdyh/aeebTSLPA6Sgrwp69H6KAcW0XZueg/LF+FKiqyG6Po/gFHq2gDhLikwyJEMpny8sudTI08w== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - nice-napi@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b" From 5c9510993ca7e3db6c34b6b811d5296f527ef3da Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 10 May 2022 20:59:04 +0200 Subject: [PATCH 337/570] 91202: Themed ExpandableNavbarSectionComponent --- .../expandable-navbar-section.component.html | 0 .../expandable-navbar-section.component.scss | 0 .../expandable-navbar-section.component.ts | 23 +++++++++++++++++++ src/themes/custom/theme.module.ts | 6 ++++- 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html create mode 100644 src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.scss create mode 100644 src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts diff --git a/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.scss b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts new file mode 100644 index 0000000000..283b9ca6f6 --- /dev/null +++ b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts @@ -0,0 +1,23 @@ +import { Component } from '@angular/core'; +import { + ExpandableNavbarSectionComponent as BaseComponent +} from '../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component'; +import { slide } from '../../../../../app/shared/animations/slide'; +import { rendersSectionForMenu } from '../../../../../app/shared/menu/menu-section.decorator'; +import { MenuID } from '../../../../../app/shared/menu/initial-menus-state'; + +/** + * Represents an expandable section in the navbar + */ +@Component({ + /* tslint:disable:component-selector */ + selector: 'li[ds-expandable-navbar-section]', + // templateUrl: './expandable-navbar-section.component.html', + templateUrl: '../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component.html', + // styleUrls: ['./expandable-navbar-section.component.scss'], + styleUrls: ['../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component.scss'], + animations: [slide] +}) +@rendersSectionForMenu(MenuID.PUBLIC, true) +export class ExpandableNavbarSectionComponent extends BaseComponent { +} diff --git a/src/themes/custom/theme.module.ts b/src/themes/custom/theme.module.ts index e2e97b9087..7206e6e714 100644 --- a/src/themes/custom/theme.module.ts +++ b/src/themes/custom/theme.module.ts @@ -84,6 +84,9 @@ import { SearchModule } from '../../app/shared/search/search.module'; import { ResourcePoliciesModule } from '../../app/shared/resource-policies/resource-policies.module'; import { ComcolModule } from '../../app/shared/comcol/comcol.module'; import { FeedbackComponent } from './app/info/feedback/feedback.component'; +import { + ExpandableNavbarSectionComponent +} from './app/navbar/expandable-navbar-section/expandable-navbar-section.component'; const DECLARATIONS = [ FileSectionComponent, @@ -126,7 +129,8 @@ const DECLARATIONS = [ NavbarComponent, HeaderNavbarWrapperComponent, BreadcrumbsComponent, - FeedbackComponent + FeedbackComponent, + ExpandableNavbarSectionComponent, ]; @NgModule({ From 55005d3fa4419c3651fda4bfdf0e06e44907b5b1 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 11 May 2022 09:45:17 +0200 Subject: [PATCH 338/570] 86526: Themed AuthNavMenuComponent --- .../auth-nav-menu.component.html | 0 .../auth-nav-menu.component.scss | 0 .../auth-nav-menu/auth-nav-menu.component.ts | 19 +++++++++++++++++++ src/themes/custom/theme.module.ts | 4 +++- 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.html create mode 100644 src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.scss create mode 100644 src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.ts diff --git a/src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.scss b/src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.ts b/src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.ts new file mode 100644 index 0000000000..af54aacd44 --- /dev/null +++ b/src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { + AuthNavMenuComponent as BaseComponent, +} from '../../../../../app/shared/auth-nav-menu/auth-nav-menu.component'; +import { fadeInOut, fadeOut } from '../../../../../app/shared/animations/fade'; + +/** + * Component representing the {@link AuthNavMenuComponent} of a page + */ +@Component({ + selector: 'ds-auth-nav-menu', + // templateUrl: 'auth-nav-menu.component.html', + templateUrl: '../../../../../app/shared/auth-nav-menu/auth-nav-menu.component.html', + // styleUrls: ['auth-nav-menu.component.scss'], + styleUrls: ['../../../../../app/shared/auth-nav-menu/auth-nav-menu.component.scss'], + animations: [fadeInOut, fadeOut] +}) +export class AuthNavMenuComponent extends BaseComponent { +} diff --git a/src/themes/custom/theme.module.ts b/src/themes/custom/theme.module.ts index dac941546b..db42294675 100644 --- a/src/themes/custom/theme.module.ts +++ b/src/themes/custom/theme.module.ts @@ -79,6 +79,7 @@ import { HeaderComponent } from './app/header/header.component'; import { FooterComponent } from './app/footer/footer.component'; import { BreadcrumbsComponent } from './app/breadcrumbs/breadcrumbs.component'; import { HeaderNavbarWrapperComponent } from './app/header-nav-wrapper/header-navbar-wrapper.component'; +import { AuthNavMenuComponent } from './app/shared/auth-nav-menu/auth-nav-menu.component'; const DECLARATIONS = [ HomePageComponent, @@ -119,7 +120,8 @@ const DECLARATIONS = [ HeaderComponent, NavbarComponent, HeaderNavbarWrapperComponent, - BreadcrumbsComponent + BreadcrumbsComponent, + AuthNavMenuComponent, ]; @NgModule({ From cbf0db5342156f0053b719cc0a4d4c7bd39d96bf Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 11 May 2022 11:30:25 +0200 Subject: [PATCH 339/570] Add themed loading component in custom theme and fix issues --- src/app/navbar/navbar.component.html | 4 ++-- src/app/shared/menu/menu-section.decorator.ts | 2 +- src/app/shared/theme-support/themed.component.ts | 1 + .../app/shared/loading/loading.component.html | 0 .../app/shared/loading/loading.component.scss | 0 .../custom/app/shared/loading/loading.component.ts | 13 +++++++++++++ src/themes/custom/theme.module.ts | 4 +++- 7 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/themes/custom/app/shared/loading/loading.component.html create mode 100644 src/themes/custom/app/shared/loading/loading.component.scss create mode 100644 src/themes/custom/app/shared/loading/loading.component.ts diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index fc5d1a2ef3..8531543361 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -6,10 +6,10 @@
- \ No newline at end of file + diff --git a/src/app/shared/menu/menu-section.decorator.ts b/src/app/shared/menu/menu-section.decorator.ts index 9374ab3c0d..18eedd1cf4 100644 --- a/src/app/shared/menu/menu-section.decorator.ts +++ b/src/app/shared/menu/menu-section.decorator.ts @@ -1,6 +1,6 @@ import { DEFAULT_THEME } from '../object-collection/shared/listable-object/listable-object.decorator'; import { MenuID } from './initial-menus-state'; -import { hasNoValue, hasValue } from '../empty.util'; +import { hasValue } from '../empty.util'; const menuComponentMap = new Map(); diff --git a/src/app/shared/theme-support/themed.component.ts b/src/app/shared/theme-support/themed.component.ts index 2ff0713f46..79ed914c5d 100644 --- a/src/app/shared/theme-support/themed.component.ts +++ b/src/app/shared/theme-support/themed.component.ts @@ -70,6 +70,7 @@ export abstract class ThemedComponent implements OnInit, OnDestroy, OnChanges this.lazyLoadSub = this.resolveThemedComponent(this.themeService.getThemeName()).pipe( switchMap((themedFile: any) => { + console.log(themedFile); if (hasValue(themedFile) && hasValue(themedFile[this.getComponentName()])) { // if the file is not null, and exports a component with the specified name, // return that component diff --git a/src/themes/custom/app/shared/loading/loading.component.html b/src/themes/custom/app/shared/loading/loading.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/shared/loading/loading.component.scss b/src/themes/custom/app/shared/loading/loading.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/shared/loading/loading.component.ts b/src/themes/custom/app/shared/loading/loading.component.ts new file mode 100644 index 0000000000..43249a4624 --- /dev/null +++ b/src/themes/custom/app/shared/loading/loading.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { LoadingComponent as BaseComponent } from '../../../../../app/shared/loading/loading.component'; + +@Component({ + selector: 'ds-loading', + styleUrls: ['../../../../../app/shared/loading/loading.component.scss'], + // styleUrls: ['./loading.component.scss'], + templateUrl: '../../../../app/shared/loading/loading.component.html' + // templateUrl: './loading.component.html' +}) +export class LoadingComponent extends BaseComponent { + +} diff --git a/src/themes/custom/theme.module.ts b/src/themes/custom/theme.module.ts index e2e97b9087..67ad6028ce 100644 --- a/src/themes/custom/theme.module.ts +++ b/src/themes/custom/theme.module.ts @@ -84,6 +84,7 @@ import { SearchModule } from '../../app/shared/search/search.module'; import { ResourcePoliciesModule } from '../../app/shared/resource-policies/resource-policies.module'; import { ComcolModule } from '../../app/shared/comcol/comcol.module'; import { FeedbackComponent } from './app/info/feedback/feedback.component'; +import { LoadingComponent } from './app/shared/loading/loading.component'; const DECLARATIONS = [ FileSectionComponent, @@ -126,7 +127,8 @@ const DECLARATIONS = [ NavbarComponent, HeaderNavbarWrapperComponent, BreadcrumbsComponent, - FeedbackComponent + FeedbackComponent, + LoadingComponent ]; @NgModule({ From 1e9d393edf1e5ccb5c5849c1e4d9dfe4e8dfc5c6 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 11 May 2022 11:59:38 +0200 Subject: [PATCH 340/570] [CST-5307] Move profile-claim-item-modal.component to profile page module and add unit tests --- .../profile-claim-item-modal.component.html} | 4 +- ...profile-claim-item-modal.component.spec.ts | 223 ++++++++++++++++++ .../profile-claim-item-modal.component.ts | 105 +++++++++ src/app/profile-page/profile-page.module.ts | 6 +- .../claim-item-selector.component.spec.ts | 45 ---- .../claim-item-selector.component.ts | 69 ------ src/app/shared/shared.module.ts | 8 +- 7 files changed, 336 insertions(+), 124 deletions(-) rename src/app/{shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component.html => profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html} (93%) create mode 100644 src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.spec.ts create mode 100644 src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.ts delete mode 100644 src/app/shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component.spec.ts delete mode 100644 src/app/shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component.ts diff --git a/src/app/shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component.html b/src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html similarity index 93% rename from src/app/shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component.html rename to src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html index 9df49ba24b..eec9f437f1 100644 --- a/src/app/shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component.html +++ b/src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html @@ -14,7 +14,7 @@ @@ -26,7 +26,7 @@ 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 7c8ce67b88..6703634b6b 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 @@ -57,12 +57,12 @@ [relationTypes]="[{ label: 'isOrgUnitOfPerson', filter: 'isOrgUnitOfPerson', - configuration: 'person' + configuration: 'person-relationships' }, { label: 'isOrgUnitOfProject', filter: 'isOrgUnitOfProject', - configuration: 'project' + configuration: 'project-relationships' }]"> 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 6e71f775d6..4b7b3bed32 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 @@ -68,7 +68,8 @@ From 36208d13033f1b33d1bfaf15a84c18a7a3ccb593 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 11 May 2022 12:01:35 +0200 Subject: [PATCH 342/570] [CST-5307] Fix visibility toggle and address review feedback --- .../profile/researcher-profile.service.ts | 14 ++-- .../profile-claim.service.spec.ts | 16 ++--- .../profile-claim/profile-claim.service.ts | 8 ++- ...rofile-page-researcher-form.component.html | 9 ++- ...ile-page-researcher-form.component.spec.ts | 68 ++++++++++++++----- .../profile-page-researcher-form.component.ts | 19 +++--- src/assets/i18n/en.json5 | 2 + 7 files changed, 89 insertions(+), 47 deletions(-) diff --git a/src/app/core/profile/researcher-profile.service.ts b/src/app/core/profile/researcher-profile.service.ts index eab6ea3d48..0c39396950 100644 --- a/src/app/core/profile/researcher-profile.service.ts +++ b/src/app/core/profile/researcher-profile.service.ts @@ -6,7 +6,7 @@ import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; import { Operation, ReplaceOperation } from 'fast-json-patch'; import { Observable, of as observableOf } from 'rxjs'; -import { catchError, find, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, find, map, tap } from 'rxjs/operators'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { dataService } from '../cache/builders/build-decorators'; @@ -142,18 +142,14 @@ export class ResearcherProfileService { * @param researcherProfile the profile to update * @param visible the visibility value to set */ - public setVisibility(researcherProfile: ResearcherProfile, visible: boolean): Observable { - + public setVisibility(researcherProfile: ResearcherProfile, visible: boolean): Observable> { const replaceOperation: ReplaceOperation = { path: '/visible', op: 'replace', value: visible }; - return this.patch(researcherProfile, [replaceOperation]).pipe( - switchMap(() => this.findById(researcherProfile.id)), - getFirstSucceededRemoteDataPayload() - ); + return this.patch(researcherProfile, [replaceOperation]); } /** @@ -181,6 +177,8 @@ export class ResearcherProfileService { } private patch(researcherProfile: ResearcherProfile, operations: Operation[]): Observable> { - return this.dataService.patch(researcherProfile, operations); + return this.dataService.patch(researcherProfile, operations).pipe( + getFirstCompletedRemoteData() + ); } } diff --git a/src/app/profile-page/profile-claim/profile-claim.service.spec.ts b/src/app/profile-page/profile-claim/profile-claim.service.spec.ts index 4030c7900c..a06934231b 100644 --- a/src/app/profile-page/profile-claim/profile-claim.service.spec.ts +++ b/src/app/profile-page/profile-claim/profile-claim.service.spec.ts @@ -110,7 +110,7 @@ describe('ProfileClaimService', () => { page: [] }); const searchResultRD = createSuccessfulRemoteDataObject(searchResult); - const emptyRearchResultRD = createSuccessfulRemoteDataObject(emptySearchResult); + const emptySearchResultRD = createSuccessfulRemoteDataObject(emptySearchResult); beforeEach(() => { scheduler = getTestScheduler(); @@ -127,7 +127,7 @@ describe('ProfileClaimService', () => { describe('when has suggestions', () => { beforeEach(() => { - spyOn(service, 'search').and.returnValue(observableOf(searchResultRD)); + spyOn(service, 'searchForSuggestions').and.returnValue(observableOf(searchResultRD)); }); it('should return true', () => { @@ -142,7 +142,7 @@ describe('ProfileClaimService', () => { describe('when has not suggestions', () => { beforeEach(() => { - spyOn(service, 'search').and.returnValue(observableOf(emptyRearchResultRD)); + spyOn(service, 'searchForSuggestions').and.returnValue(observableOf(emptySearchResultRD)); }); it('should return false', () => { @@ -176,7 +176,7 @@ describe('ProfileClaimService', () => { }); it('should return the proper search object', () => { - const result = service.search(eperson); + const result = service.searchForSuggestions(eperson); const expected = cold('(a|)', { a: searchResultRD }); @@ -187,13 +187,13 @@ describe('ProfileClaimService', () => { describe('when has not suggestions', () => { beforeEach(() => { - searchService.search.and.returnValue(observableOf(emptyRearchResultRD)); + searchService.search.and.returnValue(observableOf(emptySearchResultRD)); }); it('should return null', () => { - const result = service.search(eperson); + const result = service.searchForSuggestions(eperson); const expected = cold('(a|)', { - a: emptyRearchResultRD + a: emptySearchResultRD }); expect(result).toBeObservable(expected); }); @@ -202,7 +202,7 @@ describe('ProfileClaimService', () => { describe('when has not valid eperson', () => { it('should return null', () => { - const result = service.search(null); + const result = service.searchForSuggestions(null); const expected = cold('(a|)', { a: null }); diff --git a/src/app/profile-page/profile-claim/profile-claim.service.ts b/src/app/profile-page/profile-claim/profile-claim.service.ts index a61404540b..62517c4b21 100644 --- a/src/app/profile-page/profile-claim/profile-claim.service.ts +++ b/src/app/profile-page/profile-claim/profile-claim.service.ts @@ -27,7 +27,7 @@ export class ProfileClaimService { * @param eperson the eperson */ hasProfilesToSuggest(eperson: EPerson): Observable { - return this.search(eperson).pipe( + return this.searchForSuggestions(eperson).pipe( map((rd: RemoteData>) => { return isNotEmpty(rd) && rd.hasSucceeded && rd.payload?.page?.length > 0; }) @@ -36,9 +36,10 @@ export class ProfileClaimService { /** * Returns profiles that could be associated with the given user. + * * @param eperson the user */ - search(eperson: EPerson): Observable>> { + searchForSuggestions(eperson: EPerson): Observable>> { const query = this.personQueryData(eperson); if (isEmpty(query)) { return of(null); @@ -48,6 +49,7 @@ export class ProfileClaimService { /** * Search object by the given query. + * * @param query the query for the search */ private lookup(query: string): Observable>> { @@ -71,7 +73,7 @@ export class ProfileClaimService { if (eperson) { const firstname = eperson.firstMetadataValue('eperson.firstname'); const lastname = eperson.firstMetadataValue('eperson.lastname'); - return 'dc.title:' + eperson.name + ' OR (person.familyName:' + lastname + ' AND person.givenName:' + firstname + ')'; + return '(dc.title:' + eperson.name + ') OR (person.familyName:' + lastname + ' AND person.givenName:' + firstname + ')'; } else { return null; } diff --git a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html index bb55418744..2d959c1dfe 100644 --- a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html +++ b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html @@ -3,14 +3,17 @@

{{'researcher.profile.associated' | translate}}

{{'researcher.profile.status' | translate}} - +

{{'researcher.profile.not.associated' | translate}}

- + diff --git a/src/app/shared/rss-feed/rss.component.html b/src/app/shared/rss-feed/rss.component.html new file mode 100644 index 0000000000..8868539b5c --- /dev/null +++ b/src/app/shared/rss-feed/rss.component.html @@ -0,0 +1,5 @@ + +
+ +
+
diff --git a/src/app/shared/rss-feed/rss.component.scss b/src/app/shared/rss-feed/rss.component.scss new file mode 100644 index 0000000000..929bb453ac --- /dev/null +++ b/src/app/shared/rss-feed/rss.component.scss @@ -0,0 +1,12 @@ +:host { + .dropdown-toggle::after { + display: none; + } + .dropdown-item { + padding-left: 20px; + } +} + +.margin-right { + margin-right: .5em; +} \ No newline at end of file diff --git a/src/app/shared/rss-feed/rss.component.spec.ts b/src/app/shared/rss-feed/rss.component.spec.ts new file mode 100644 index 0000000000..bbfd5442b3 --- /dev/null +++ b/src/app/shared/rss-feed/rss.component.spec.ts @@ -0,0 +1,109 @@ +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'; +import { PaginationService } from '../../core/pagination/pagination.service'; +import { LinkHeadService } from '../../core/services/link-head.service'; +import { Collection } from '../../core/shared/collection.model'; +import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; +import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; +import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { PaginatedSearchOptions } from '../search/paginated-search-options.model'; +import { PaginationServiceStub } from '../testing/pagination-service.stub'; +import { createPaginatedList } from '../testing/utils.test'; +import { RSSComponent } from './rss.component'; +import { of as observableOf } from 'rxjs'; + + + +describe('RssComponent', () => { + let comp: RSSComponent; + let options: SortOptions; + let fixture: ComponentFixture; + let uuid: string; + let query: string; + let groupDataService: GroupDataService; + let linkHeadService: LinkHeadService; + let configurationDataService: ConfigurationDataService; + let paginationService; + + beforeEach(waitForAsync(() => { + const mockCollection: Collection = Object.assign(new Collection(), { + id: 'ce41d451-97ed-4a9c-94a1-7de34f16a9f4', + name: 'test-collection', + _links: { + mappedItems: { + href: 'https://rest.api/collections/ce41d451-97ed-4a9c-94a1-7de34f16a9f4/mappedItems' + }, + self: { + href: 'https://rest.api/collections/ce41d451-97ed-4a9c-94a1-7de34f16a9f4' + } + } + }); + configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + const mockCollectionRD: RemoteData = createSuccessfulRemoteDataObject(mockCollection); + const mockSearchOptions = observableOf(new PaginatedSearchOptions({ + pagination: Object.assign(new PaginationComponentOptions(), { + id: 'search-page-configuration', + pageSize: 10, + currentPage: 1 + }), + sort: new SortOptions('dc.title', SortDirection.ASC), + scope: mockCollection.id + })); + groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '' + }); + paginationService = new PaginationServiceStub(); + const searchConfigService = { + paginatedSearchOptions: mockSearchOptions + }; + TestBed.configureTestingModule({ + providers: [ + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: SearchConfigurationService, useValue: searchConfigService}, + { provide: PaginationService, useValue: paginationService } + ], + declarations: [RSSComponent] + }).compileComponents(); + })); + + beforeEach(() => { + options = new SortOptions('dc.title', SortDirection.DESC); + uuid = '2cfcf65e-0a51-4bcb-8592-b8db7b064790'; + query = 'test'; + fixture = TestBed.createComponent(RSSComponent); + comp = fixture.componentInstance; + }); + + it('should formulate the correct url given params in url', () => { + const route = comp.formulateRoute(uuid, 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, 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, options, null); + expect(route).toBe('/opensearch/search?format=atom&sort=dc.title&sort_direction=DESC&query=*'); + }); +}); + diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts new file mode 100644 index 0000000000..036148b368 --- /dev/null +++ b/src/app/shared/rss-feed/rss.component.ts @@ -0,0 +1,94 @@ +import { + ChangeDetectionStrategy, + Component, + OnDestroy, + OnInit, + ViewEncapsulation +} from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +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 { 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'; + + +/** + * The default pagination controls component. + */ +@Component({ + exportAs: 'rssComponent', + selector: 'ds-rss', + styleUrls: ['rss.component.scss'], + templateUrl: 'rss.component.html', + changeDetection: ChangeDetectionStrategy.Default, + encapsulation: ViewEncapsulation.Emulated +}) +export class RSSComponent implements OnInit, OnDestroy { + + route$: BehaviorSubject; + + isEnabled$: BehaviorSubject = new BehaviorSubject(false); + + uuid: string; + configuration$: Observable; + sortOption$: Observable; + + constructor(private groupDataService: GroupDataService, + private linkHeadService: LinkHeadService, + private configurationService: ConfigurationDataService, + private searchConfigurationService: SearchConfigurationService, + protected paginationService: PaginationService) { + } + ngOnDestroy(): void { + this.linkHeadService.removeTag("rel='alternate'"); + } + + ngOnInit(): void { + this.configuration$ = this.searchConfigurationService.getCurrentConfiguration('default'); + + this.configurationService.findByPropertyName('websvc.opensearch.enable').pipe( + getFirstCompletedRemoteData(), + ).subscribe((result) => { + const enabled = Boolean(result.payload.values[0]); + this.isEnabled$.next(enabled); + }); + + this.searchConfigurationService.getCurrentQuery('').subscribe((query) => { + this.sortOption$ = this.paginationService.getCurrentSort(this.searchConfigurationService.paginationID, null, true); + this.sortOption$.subscribe((sort) => { + this.uuid = this.groupDataService.getUUIDFromString(window.location.href); + + const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, sort, query); + + this.linkHeadService.addTag({ + href: route, + type: 'application/atom+xml', + rel: 'alternate', + title: 'Sitewide Atom feed' + }); + this.route$ = new BehaviorSubject(route); + }); + }); + } + + formulateRoute(uuid: string, sort: SortOptions, query: string): string { + let route = 'search?format=atom'; + if (uuid) { + route += `&scope=${uuid}`; + } + if (sort.direction && sort.field) { + route += `&sort=${sort.field}&sort_direction=${sort.direction}`; + } + if (query) { + route += `&query=${query}`; + } else { + route += `&query=*`; + } + route = '/opensearch/' + route; + return route; + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 12b6a482dc..c70aff6192 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -173,10 +173,9 @@ import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-p import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; -import { ExternalLinkMenuItemComponent } from './menu/menu-item/external-link-menu-item.component'; +import { RSSComponent } from './rss-feed/rss.component'; const MODULES = [ - // Do NOT include UniversalModule, HttpModule, or JsonpModule here CommonModule, SortablejsModule, FileUploadModule, @@ -239,6 +238,7 @@ const COMPONENTS = [ AbstractListableElementComponent, ObjectCollectionComponent, PaginationComponent, + RSSComponent, SearchFormComponent, PageWithSidebarComponent, SidebarDropdownComponent, diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts new file mode 100644 index 0000000000..999abd32ee --- /dev/null +++ b/src/environments/environment.dev.ts @@ -0,0 +1,17 @@ +export const environment = { + ui: { + ssl: false, + host: 'localhost', + port: 18080, + nameSpace: '/' + }, + rest: { + ssl: false, + host: 'localhost', + port: 8080, + nameSpace: '/server' + }, + universal: { + preboot: false + } +}; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100644 index 0000000000..c31da7b791 --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,17 @@ +export const environment = { + ui: { + ssl: false, + host: 'localhost', + port: 18080, + nameSpace: '/' + }, + rest: { + ssl: false, + host: 'localhost', + port: 8080, + nameSpace: '/server' + }, + universal: { + preboot: true + } +}; From b8a96e48a7b885cc4424b720bfb9dbf012e82f23 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 10 Dec 2021 12:25:51 -0500 Subject: [PATCH 352/570] Fix test files to pass down necessary providers --- .../collection-item-mapper.component.spec.ts | 28 ++++++++++++++++- ...page-sub-collection-list.component.spec.ts | 31 +++++++++++++++++++ ...-page-sub-community-list.component.spec.ts | 30 ++++++++++++++++++ src/app/core/services/link-head.service.ts | 8 +---- ...top-level-community-list.component.spec.ts | 30 ++++++++++++++++++ .../edit-relationship-list.component.spec.ts | 29 +++++++++++++++++ .../collection-select.component.spec.ts | 31 ++++++++++++++++++- .../item-select/item-select.component.spec.ts | 30 +++++++++++++++++- src/app/shared/rss-feed/rss.component.scss | 2 +- src/app/shared/rss-feed/rss.component.spec.ts | 7 +++-- .../search-configuration-service.stub.ts | 4 +++ 11 files changed, 216 insertions(+), 14 deletions(-) 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 7f0e6815ed..142604c9b2 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 @@ -43,6 +43,10 @@ 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 { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; +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 { ConfigurationProperty } from '../../core/shared/configuration-property.model'; describe('CollectionItemMapperComponent', () => { let comp: CollectionItemMapperComponent; @@ -143,6 +147,25 @@ describe('CollectionItemMapperComponent', () => { isAuthorized: observableOf(true) }); + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule], @@ -159,7 +182,10 @@ describe('CollectionItemMapperComponent', () => { { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() }, { provide: RouteService, useValue: routeServiceStub }, - { provide: AuthorizationDataService, useValue: authorizationDataService } + { provide: AuthorizationDataService, useValue: authorizationDataService }, + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, ] }).overrideComponent(CollectionItemMapperComponent, { set: { 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 ec61fac613..c0ce5369ff 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 @@ -25,6 +25,14 @@ import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { FindListOptions } from '../../core/data/find-list-options.model'; +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'; describe('CommunityPageSubCollectionList Component', () => { let comp: CommunityPageSubCollectionListComponent; @@ -122,6 +130,25 @@ describe('CommunityPageSubCollectionList Component', () => { themeService = getMockThemeService(); + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ @@ -138,6 +165,10 @@ describe('CommunityPageSubCollectionList Component', () => { { provide: PaginationService, useValue: paginationService }, { provide: SelectableListService, useValue: {} }, { provide: ThemeService, useValue: themeService }, + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); 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 2bc829a3b0..3392ada994 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 @@ -25,6 +25,13 @@ import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { FindListOptions } from '../../core/data/find-list-options.model'; +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 { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; +import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; +import { createPaginatedList } from '../../shared/testing/utils.test'; describe('CommunityPageSubCommunityListComponent Component', () => { let comp: CommunityPageSubCommunityListComponent; @@ -119,6 +126,25 @@ describe('CommunityPageSubCommunityListComponent Component', () => { } }; + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + const paginationService = new PaginationServiceStub(); themeService = getMockThemeService(); @@ -139,6 +165,10 @@ describe('CommunityPageSubCommunityListComponent Component', () => { { provide: PaginationService, useValue: paginationService }, { provide: SelectableListService, useValue: {} }, { provide: ThemeService, useValue: themeService }, + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/core/services/link-head.service.ts b/src/app/core/services/link-head.service.ts index 29ab62ff13..12c3ff197d 100644 --- a/src/app/core/services/link-head.service.ts +++ b/src/app/core/services/link-head.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Optional, RendererFactory2, ViewEncapsulation, Inject } from '@angular/core'; +import { Injectable, RendererFactory2, ViewEncapsulation, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; @Injectable() @@ -22,7 +22,6 @@ export class LinkHeadService { const link = renderer.createElement('link'); const head = this.document.head; - const selector = this._parseSelector(tag); if (head === null) { throw new Error(' not found within DOCUMENT.'); @@ -61,11 +60,6 @@ export class LinkHeadService { } } } - - private _parseSelector(tag: LinkDefinition): string { - const attr: string = tag.rel ? 'rel' : 'hreflang'; - return `${attr}="${tag[attr]}"`; - } } export declare type LinkDefinition = { 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 eb52ca9243..2561770942 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 @@ -25,6 +25,13 @@ import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { FindListOptions } from '../../core/data/find-list-options.model'; +import { ConfigurationDataService } from '../../core/data/configuration-data.service'; +import { GroupDataService } from '../../core/eperson/group-data.service'; +import { LinkHeadService } from '../../core/services/link-head.service'; +import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; +import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; +import { createPaginatedList } from '../../shared/testing/utils.test'; +import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; describe('TopLevelCommunityList Component', () => { let comp: TopLevelCommunityListComponent; @@ -114,6 +121,25 @@ describe('TopLevelCommunityList Component', () => { themeService = getMockThemeService(); + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ @@ -130,6 +156,10 @@ describe('TopLevelCommunityList Component', () => { { provide: PaginationService, useValue: paginationService }, { provide: SelectableListService, useValue: {} }, { provide: ThemeService, useValue: themeService }, + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index b0d8046cf4..f9e889bba5 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -23,6 +23,12 @@ import { PaginationComponent } from '../../../../shared/pagination/pagination.co import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; import { RelationshipTypeService } from '../../../../core/data/relationship-type.service'; import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; +import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { ConfigurationDataService } from '../../../../core/data/configuration-data.service'; +import { LinkHeadService } from '../../../../core/services/link-head.service'; +import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service.stub'; +import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model'; let comp: EditRelationshipListComponent; let fixture: ComponentFixture; @@ -174,6 +180,25 @@ describe('EditRelationshipListComponent', () => { hostWindowService = new HostWindowServiceStub(1200); + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + TestBed.configureTestingModule({ imports: [SharedModule, TranslateModule.forRoot()], declarations: [EditRelationshipListComponent], @@ -185,6 +210,10 @@ describe('EditRelationshipListComponent', () => { { provide: PaginationService, useValue: paginationService }, { provide: HostWindowService, useValue: hostWindowService }, { provide: RelationshipTypeService, useValue: relationshipTypeService }, + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, ], schemas: [ NO_ERRORS_SCHEMA ] diff --git a/src/app/shared/object-select/collection-select/collection-select.component.spec.ts b/src/app/shared/object-select/collection-select/collection-select.component.spec.ts index 5714db2385..1b89fd32c1 100644 --- a/src/app/shared/object-select/collection-select/collection-select.component.spec.ts +++ b/src/app/shared/object-select/collection-select/collection-select.component.spec.ts @@ -17,6 +17,12 @@ import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; import { of as observableOf } from 'rxjs'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { LinkHeadService } from '../../../core/services/link-head.service'; +import { GroupDataService } from '../../../core/eperson/group-data.service'; +import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; +import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; +import { ConfigurationProperty } from '../../../core/shared/configuration-property.model'; describe('CollectionSelectComponent', () => { let comp: CollectionSelectComponent; @@ -44,6 +50,25 @@ describe('CollectionSelectComponent', () => { isAuthorized: observableOf(true) }); + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + const paginationService = new PaginationServiceStub(); beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -53,7 +78,11 @@ describe('CollectionSelectComponent', () => { { provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockCollectionList[1].id]) }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: PaginationService, useValue: paginationService }, - { provide: AuthorizationDataService, useValue: authorizationDataService } + { provide: AuthorizationDataService, useValue: authorizationDataService }, + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/shared/object-select/item-select/item-select.component.spec.ts b/src/app/shared/object-select/item-select/item-select.component.spec.ts index de52f1c3c2..25340ee862 100644 --- a/src/app/shared/object-select/item-select/item-select.component.spec.ts +++ b/src/app/shared/object-select/item-select/item-select.component.spec.ts @@ -18,6 +18,12 @@ import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; +import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; +import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { LinkHeadService } from '../../../core/services/link-head.service'; +import { GroupDataService } from '../../../core/eperson/group-data.service'; +import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; +import { ConfigurationProperty } from '../../../core/shared/configuration-property.model'; describe('ItemSelectComponent', () => { let comp: ItemSelectComponent; @@ -70,6 +76,24 @@ describe('ItemSelectComponent', () => { const authorizationDataService = new AuthorizationDataService(null, null, null, null, null, null, null, null, null, null); + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -79,7 +103,11 @@ describe('ItemSelectComponent', () => { { provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockItemList[1].id]) }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: PaginationService, useValue: paginationService }, - { provide: AuthorizationDataService, useValue: authorizationDataService } + { provide: AuthorizationDataService, useValue: authorizationDataService }, + { provide: GroupDataService, useValue: groupDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/shared/rss-feed/rss.component.scss b/src/app/shared/rss-feed/rss.component.scss index 929bb453ac..91310eddcb 100644 --- a/src/app/shared/rss-feed/rss.component.scss +++ b/src/app/shared/rss-feed/rss.component.scss @@ -9,4 +9,4 @@ .margin-right { margin-right: .5em; -} \ No newline at end of file +} diff --git a/src/app/shared/rss-feed/rss.component.spec.ts b/src/app/shared/rss-feed/rss.component.spec.ts index bbfd5442b3..cbdc2a2d0e 100644 --- a/src/app/shared/rss-feed/rss.component.spec.ts +++ b/src/app/shared/rss-feed/rss.component.spec.ts @@ -15,6 +15,7 @@ import { PaginationServiceStub } from '../testing/pagination-service.stub'; import { createPaginatedList } from '../testing/utils.test'; import { RSSComponent } from './rss.component'; import { of as observableOf } from 'rxjs'; +import { SearchConfigurationServiceStub } from '../testing/search-configuration-service.stub'; @@ -61,11 +62,11 @@ describe('RssComponent', () => { currentPage: 1 }), sort: new SortOptions('dc.title', SortDirection.ASC), - scope: mockCollection.id })); groupDataService = jasmine.createSpyObj('groupsDataService', { findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), - getGroupRegistryRouterLink: '' + getGroupRegistryRouterLink: '', + getUUIDFromString: '', }); paginationService = new PaginationServiceStub(); const searchConfigService = { @@ -76,7 +77,7 @@ describe('RssComponent', () => { { provide: GroupDataService, useValue: groupDataService }, { provide: LinkHeadService, useValue: linkHeadService }, { provide: ConfigurationDataService, useValue: configurationDataService }, - { provide: SearchConfigurationService, useValue: searchConfigService}, + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, { provide: PaginationService, useValue: paginationService } ], declarations: [RSSComponent] diff --git a/src/app/shared/testing/search-configuration-service.stub.ts b/src/app/shared/testing/search-configuration-service.stub.ts index 80744ba59a..78b358f0d4 100644 --- a/src/app/shared/testing/search-configuration-service.stub.ts +++ b/src/app/shared/testing/search-configuration-service.stub.ts @@ -17,6 +17,10 @@ export class SearchConfigurationServiceStub { return observableOf('test-id'); } + getCurrentQuery(a) { + return observableOf(a); + } + getCurrentConfiguration(a) { return observableOf(a); } From b5943b48b47009a7f771db99530d57d5be2fd1fc Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 25 Feb 2022 12:14:16 -0500 Subject: [PATCH 353/570] w2p-86403 fix opensearch is disabled, router issues and links --- src/app/core/services/link-head.service.ts | 3 ++ src/app/shared/rss-feed/rss.component.ts | 32 +++++++++++++++++----- src/app/shared/shared.module.ts | 4 +++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/app/core/services/link-head.service.ts b/src/app/core/services/link-head.service.ts index 12c3ff197d..af5dddec8d 100644 --- a/src/app/core/services/link-head.service.ts +++ b/src/app/core/services/link-head.service.ts @@ -1,6 +1,9 @@ import { Injectable, RendererFactory2, ViewEncapsulation, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; +/** + * LinkHead Service injects tag into the head element during runtime. + */ @Injectable() export class LinkHeadService { constructor( diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 036148b368..11286e19e3 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -14,10 +14,11 @@ 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'; /** - * The default pagination controls component. + * The Rss feed button componenet. */ @Component({ exportAs: 'rssComponent', @@ -41,6 +42,7 @@ export class RSSComponent implements OnInit, OnDestroy { private linkHeadService: LinkHeadService, private configurationService: ConfigurationDataService, private searchConfigurationService: SearchConfigurationService, + private router: Router, protected paginationService: PaginationService) { } ngOnDestroy(): void { @@ -53,22 +55,22 @@ export class RSSComponent implements OnInit, OnDestroy { this.configurationService.findByPropertyName('websvc.opensearch.enable').pipe( getFirstCompletedRemoteData(), ).subscribe((result) => { - const enabled = Boolean(result.payload.values[0]); + const enabled = (result.payload.values[0] === 'true'); this.isEnabled$.next(enabled); }); this.searchConfigurationService.getCurrentQuery('').subscribe((query) => { this.sortOption$ = this.paginationService.getCurrentSort(this.searchConfigurationService.paginationID, null, true); this.sortOption$.subscribe((sort) => { - this.uuid = this.groupDataService.getUUIDFromString(window.location.href); + this.uuid = this.groupDataService.getUUIDFromString(this.router.url); const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, sort, query); - + this.addLinks(route); this.linkHeadService.addTag({ - href: route, + href: environment.rest.baseUrl + '/opensearch/service', type: 'application/atom+xml', - rel: 'alternate', - title: 'Sitewide Atom feed' + rel: 'search', + title: 'Dspace' }); this.route$ = new BehaviorSubject(route); }); @@ -91,4 +93,20 @@ export class RSSComponent implements OnInit, OnDestroy { route = '/opensearch/' + route; return route; } + + addLinks(route: string): void { + this.linkHeadService.addTag({ + href: route, + type: 'application/atom+xml', + rel: 'alternate', + title: 'Sitewide Atom feed' + }); + route = route.replace('format=atom', 'format=rss'); + this.linkHeadService.addTag({ + href: route, + type: 'application/rss+xml', + rel: 'alternate', + title: 'Sitewide RSS feed' + }); + } } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c70aff6192..c6cc3d2bd9 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -174,6 +174,10 @@ import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; import { RSSComponent } from './rss-feed/rss.component'; +import { SearchObjects } from './search/models/search-objects.model'; +import { FacetConfigResponse } from './search/models/facet-config-response.model'; +import { SearchResult } from './search/models/search-result.model'; +import { FacetValues } from './search/models/facet-values.model'; const MODULES = [ CommonModule, From efb76ea88328df3d03706ad3c5f0411dcf7077a9 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 25 Feb 2022 12:35:17 -0500 Subject: [PATCH 354/570] Fix tests now that router is apart of the rss component --- .../edit-relationship-list.component.spec.ts | 3 +++ src/app/shared/rss-feed/rss.component.spec.ts | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index f9e889bba5..2403d8f443 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -29,6 +29,8 @@ import { LinkHeadService } from '../../../../core/services/link-head.service'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service.stub'; import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model'; +import { Router } from '@angular/router'; +import { RouterMock } from '../../../../shared/mocks/router.mock'; let comp: EditRelationshipListComponent; let fixture: ComponentFixture; @@ -211,6 +213,7 @@ describe('EditRelationshipListComponent', () => { { provide: HostWindowService, useValue: hostWindowService }, { provide: RelationshipTypeService, useValue: relationshipTypeService }, { provide: GroupDataService, useValue: groupDataService }, + { provide: Router, useValue: new RouterMock() }, { provide: LinkHeadService, useValue: linkHeadService }, { provide: ConfigurationDataService, useValue: configurationDataService }, { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, diff --git a/src/app/shared/rss-feed/rss.component.spec.ts b/src/app/shared/rss-feed/rss.component.spec.ts index cbdc2a2d0e..b304abda83 100644 --- a/src/app/shared/rss-feed/rss.component.spec.ts +++ b/src/app/shared/rss-feed/rss.component.spec.ts @@ -10,12 +10,14 @@ import { ConfigurationProperty } from '../../core/shared/configuration-property. import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; -import { PaginatedSearchOptions } from '../search/paginated-search-options.model'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; import { createPaginatedList } from '../testing/utils.test'; import { RSSComponent } from './rss.component'; import { of as observableOf } from 'rxjs'; import { SearchConfigurationServiceStub } from '../testing/search-configuration-service.stub'; +import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model'; +import { Router } from '@angular/router'; +import { RouterMock } from '../mocks/router.mock'; @@ -78,7 +80,8 @@ describe('RssComponent', () => { { provide: LinkHeadService, useValue: linkHeadService }, { provide: ConfigurationDataService, useValue: configurationDataService }, { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: Router, useValue: new RouterMock() } ], declarations: [RSSComponent] }).compileComponents(); From ffb34da3e74ce1354f3a29559adf36b856947344 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 25 Feb 2022 14:56:36 -0500 Subject: [PATCH 355/570] Create link head spec file --- .../core/services/link-head.service.spec.ts | 45 +++++++++++++++++++ src/app/core/services/link-head.service.ts | 17 +++++-- 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/app/core/services/link-head.service.spec.ts diff --git a/src/app/core/services/link-head.service.spec.ts b/src/app/core/services/link-head.service.spec.ts new file mode 100644 index 0000000000..017fe6af03 --- /dev/null +++ b/src/app/core/services/link-head.service.spec.ts @@ -0,0 +1,45 @@ +import { DOCUMENT } from '@angular/common'; +import { Renderer2, RendererFactory2 } from '@angular/core'; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { MockProvider } from 'ng-mocks'; +import { LinkHeadService } from './link-head.service'; + +describe('LinkHeadService', () => { + + let service: LinkHeadService; + + const renderer2: Renderer2 = { + createRenderer: jasmine.createSpy('createRenderer'), + createElement: jasmine.createSpy('createElement'), + setAttribute: jasmine.createSpy('setAttribute'), + appendChild: jasmine.createSpy('appendChild') + } as unknown as Renderer2; + + beforeEach(waitForAsync(() => { + return TestBed.configureTestingModule({ + providers: [ + MockProvider(RendererFactory2, { + createRenderer: () => renderer2 + }), + { provide: Document, useExisting: DOCUMENT }, + ] + }); + })); + + beforeEach(() => { + service = new LinkHeadService(TestBed.inject(RendererFactory2), TestBed.inject(DOCUMENT)); + }); + + describe('link', () => { + it('should create a link tag', () => { + const link = service.addTag({ + href: 'test', + type: 'application/atom+xml', + rel: 'alternate', + title: 'Sitewide Atom feed' + }); + expect(link).not.toBeUndefined(); + }); + }); + +}); diff --git a/src/app/core/services/link-head.service.ts b/src/app/core/services/link-head.service.ts index af5dddec8d..39552a44d3 100644 --- a/src/app/core/services/link-head.service.ts +++ b/src/app/core/services/link-head.service.ts @@ -12,7 +12,13 @@ export class LinkHeadService { ) { } - addTag(tag: LinkDefinition, forceCreation?: boolean) { + + /** + * Method to create a Link tag in the HEAD of the html. + * @param tag LinkDefition is the paramaters to define a link tag. + * @returns Link tag that was created + */ + addTag(tag: LinkDefinition) { try { const renderer = this.rendererFactory.createRenderer(this.document, { @@ -23,7 +29,8 @@ export class LinkHeadService { }); const link = renderer.createElement('link'); - + console.log(tag); + console.log(link); const head = this.document.head; if (head === null) { @@ -35,12 +42,16 @@ export class LinkHeadService { }); renderer.appendChild(head, link); - + return renderer; } catch (e) { console.error('Error within linkService : ', e); } } + /** + * Removes a link tag in header based on the given attrSelector. + * @param attrSelector The attr assigned to a link tag which will be used to determine what link to remove. + */ removeTag(attrSelector: string) { if (attrSelector) { try { From 2b9383d2d59c47d6f90a2f5a43fdabd43d351d14 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Mon, 18 Apr 2022 12:33:29 -0400 Subject: [PATCH 356/570] remove unused import from rebase --- src/app/shared/shared.module.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c6cc3d2bd9..c70aff6192 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -174,10 +174,6 @@ import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; import { RSSComponent } from './rss-feed/rss.component'; -import { SearchObjects } from './search/models/search-objects.model'; -import { FacetConfigResponse } from './search/models/facet-config-response.model'; -import { SearchResult } from './search/models/search-result.model'; -import { FacetValues } from './search/models/facet-values.model'; const MODULES = [ CommonModule, From 0bf0e1f2742aa253d4a442a83eebb64ea680902a Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Mon, 18 Apr 2022 13:30:35 -0400 Subject: [PATCH 357/570] add providers to browseby --- .../browse-by/browse-by.component.spec.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index d71d100ca5..8bda44b11c 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -38,6 +38,13 @@ import { HostWindowService } from '../host-window.service'; import { RouteService } from '../../core/services/route.service'; import { routeServiceStub } from '../testing/route-service.stub'; import SpyObj = jasmine.SpyObj; +import { GroupDataService } from '../../core/eperson/group-data.service'; +import { createPaginatedList } from '../testing/utils.test'; +import { LinkHeadService } from '../../core/services/link-head.service'; +import { ConfigurationDataService } from '../../core/data/configuration-data.service'; +import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; +import { SearchConfigurationServiceStub } from '../testing/search-configuration-service.stub'; +import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; @listableObjectComponent(BrowseEntry, ViewMode.ListElement, DEFAULT_CONTEXT, 'custom') @Component({ @@ -73,6 +80,25 @@ describe('BrowseByComponent', () => { ]; const mockItemsRD$ = createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), mockItems)); + const groupDataService = jasmine.createSpyObj('groupsDataService', { + findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), + getGroupRegistryRouterLink: '', + getUUIDFromString: '', + }); + + const linkHeadService = jasmine.createSpyObj('linkHeadService', { + addTag: '' + }); + + const configurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'test', + values: [ + 'org.dspace.ctask.general.ProfileFormats = test' + ] + })) + }); + const paginationConfig = Object.assign(new PaginationComponentOptions(), { id: 'test-pagination', currentPage: 1, @@ -103,6 +129,10 @@ describe('BrowseByComponent', () => { ], declarations: [], providers: [ + { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, + { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: LinkHeadService, useValue: linkHeadService }, + { provide: GroupDataService, useValue: groupDataService }, { provide: PaginationService, useValue: paginationService }, { provide: MockThemedBrowseEntryListElementComponent }, { provide: ThemeService, useValue: themeService }, From be8a8f5f6bf9359eecc8034b22c522fe9ac1389f Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Wed, 27 Apr 2022 16:13:01 -0400 Subject: [PATCH 358/570] Add typedocs and websvc.opensearch.svccontext --- src/app/core/services/link-head.service.ts | 2 - src/app/shared/rss-feed/rss.component.spec.ts | 6 +-- src/app/shared/rss-feed/rss.component.ts | 53 +++++++++++++------ src/app/shared/shared.module.ts | 1 + src/environments/environment.dev.ts | 17 ------ src/environments/environment.prod.ts | 17 ------ 6 files changed, 41 insertions(+), 55 deletions(-) delete mode 100644 src/environments/environment.dev.ts delete mode 100644 src/environments/environment.prod.ts diff --git a/src/app/core/services/link-head.service.ts b/src/app/core/services/link-head.service.ts index 39552a44d3..d608618ca4 100644 --- a/src/app/core/services/link-head.service.ts +++ b/src/app/core/services/link-head.service.ts @@ -29,8 +29,6 @@ export class LinkHeadService { }); const link = renderer.createElement('link'); - console.log(tag); - console.log(link); const head = this.document.head; if (head === null) { diff --git a/src/app/shared/rss-feed/rss.component.spec.ts b/src/app/shared/rss-feed/rss.component.spec.ts index b304abda83..fc19c65e60 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, options, query); + const route = comp.formulateRoute(uuid, 'opensearch', 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, options, query); + const route = comp.formulateRoute(null, 'opensearch', 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, options, null); + const route = comp.formulateRoute(null, 'opensearch', options, null); expect(route).toBe('/opensearch/search?format=atom&sort=dc.title&sort_direction=DESC&query=*'); }); }); diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 11286e19e3..456e627941 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -45,10 +45,17 @@ export class RSSComponent implements OnInit, OnDestroy { private router: Router, protected paginationService: PaginationService) { } + /** + * Removes the linktag created when the component gets removed from the page. + */ ngOnDestroy(): void { this.linkHeadService.removeTag("rel='alternate'"); } + + /** + * Generates the link tags and the url to opensearch when the component is loaded. + */ ngOnInit(): void { this.configuration$ = this.searchConfigurationService.getCurrentConfiguration('default'); @@ -58,26 +65,36 @@ export class RSSComponent implements OnInit, OnDestroy { const enabled = (result.payload.values[0] === 'true'); this.isEnabled$.next(enabled); }); - - this.searchConfigurationService.getCurrentQuery('').subscribe((query) => { - this.sortOption$ = this.paginationService.getCurrentSort(this.searchConfigurationService.paginationID, null, true); - this.sortOption$.subscribe((sort) => { - this.uuid = this.groupDataService.getUUIDFromString(this.router.url); - - const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, sort, query); - this.addLinks(route); - this.linkHeadService.addTag({ - href: environment.rest.baseUrl + '/opensearch/service', - type: 'application/atom+xml', - rel: 'search', - title: 'Dspace' + this.configurationService.findByPropertyName('websvc.opensearch.svccontext').pipe( + getFirstCompletedRemoteData(), + ).subscribe((url) => { + this.searchConfigurationService.getCurrentQuery('').subscribe((query) => { + this.sortOption$ = this.paginationService.getCurrentSort(this.searchConfigurationService.paginationID, null, true); + this.sortOption$.subscribe((sort) => { + this.uuid = this.groupDataService.getUUIDFromString(this.router.url); + const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, url.payload.values[0], sort, query); + this.addLinks(route); + this.linkHeadService.addTag({ + href: environment.rest.baseUrl + '/' + url.payload.values[0] + '/service', + type: 'application/atom+xml', + rel: 'search', + title: 'Dspace' + }); + this.route$ = new BehaviorSubject(route); }); - this.route$ = new BehaviorSubject(route); }); }); } - formulateRoute(uuid: string, sort: SortOptions, query: string): string { + /** + * 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 { let route = 'search?format=atom'; if (uuid) { route += `&scope=${uuid}`; @@ -90,10 +107,14 @@ export class RSSComponent implements OnInit, OnDestroy { } else { route += `&query=*`; } - route = '/opensearch/' + route; + route = '/' + opensearch +'/' + route; return route; } + /** + * Creates tags in the header of the page + * @param route The composed url to opensearch + */ addLinks(route: string): void { this.linkHeadService.addTag({ href: route, diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c70aff6192..7c6fe6657a 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -174,6 +174,7 @@ import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; import { RSSComponent } from './rss-feed/rss.component'; +import { ExternalLinkMenuItemComponent } from './menu/menu-item/external-link-menu-item.component'; const MODULES = [ CommonModule, diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts deleted file mode 100644 index 999abd32ee..0000000000 --- a/src/environments/environment.dev.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const environment = { - ui: { - ssl: false, - host: 'localhost', - port: 18080, - nameSpace: '/' - }, - rest: { - ssl: false, - host: 'localhost', - port: 8080, - nameSpace: '/server' - }, - universal: { - preboot: false - } -}; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts deleted file mode 100644 index c31da7b791..0000000000 --- a/src/environments/environment.prod.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const environment = { - ui: { - ssl: false, - host: 'localhost', - port: 18080, - nameSpace: '/' - }, - rest: { - ssl: false, - host: 'localhost', - port: 8080, - nameSpace: '/server' - }, - universal: { - preboot: true - } -}; From 90f1fc186c12719fe5f62f2606ef6daf29d22777 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Thu, 28 Apr 2022 09:36:09 -0400 Subject: [PATCH 359/570] Link fixes --- 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 456e627941..d397019995 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -89,7 +89,7 @@ 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 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 @@ -107,7 +107,7 @@ export class RSSComponent implements OnInit, OnDestroy { } else { route += `&query=*`; } - route = '/' + opensearch +'/' + route; + route = '/' + opensearch + '/' + route; return route; } From 8785270363a4d54ec1bc22d8c90571b7f9ba081d Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Thu, 28 Apr 2022 15:51:21 -0400 Subject: [PATCH 360/570] w2p-86403 Changed to subscribe and unsubscribe on destory --- src/app/shared/rss-feed/rss.component.ts | 53 ++++++++++++++---------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index d397019995..56ed052a9f 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -5,7 +5,7 @@ import { OnInit, ViewEncapsulation } from '@angular/core'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { GroupDataService } from '../../core/eperson/group-data.service'; import { LinkHeadService } from '../../core/services/link-head.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; @@ -15,6 +15,9 @@ import { SearchConfigurationService } from '../../core/shared/search/search-conf 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'; +import { RemoteData } from '../../core/data/remote-data'; +import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model'; /** @@ -32,12 +35,14 @@ export class RSSComponent implements OnInit, OnDestroy { route$: BehaviorSubject; - isEnabled$: BehaviorSubject = new BehaviorSubject(false); + isEnabled$: BehaviorSubject = new BehaviorSubject(null); uuid: string; configuration$: Observable; sortOption$: Observable; + subs: Subscription[] = []; + constructor(private groupDataService: GroupDataService, private linkHeadService: LinkHeadService, private configurationService: ConfigurationDataService, @@ -50,6 +55,9 @@ export class RSSComponent implements OnInit, OnDestroy { */ ngOnDestroy(): void { this.linkHeadService.removeTag("rel='alternate'"); + this.subs.forEach(sub => { + sub.unsubscribe(); + }); } @@ -59,31 +67,32 @@ export class RSSComponent implements OnInit, OnDestroy { ngOnInit(): void { this.configuration$ = this.searchConfigurationService.getCurrentConfiguration('default'); - this.configurationService.findByPropertyName('websvc.opensearch.enable').pipe( + this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.enable').pipe( getFirstCompletedRemoteData(), ).subscribe((result) => { const enabled = (result.payload.values[0] === 'true'); this.isEnabled$.next(enabled); - }); - this.configurationService.findByPropertyName('websvc.opensearch.svccontext').pipe( + })); + this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.svccontext').pipe( getFirstCompletedRemoteData(), - ).subscribe((url) => { - this.searchConfigurationService.getCurrentQuery('').subscribe((query) => { - this.sortOption$ = this.paginationService.getCurrentSort(this.searchConfigurationService.paginationID, null, true); - this.sortOption$.subscribe((sort) => { - this.uuid = this.groupDataService.getUUIDFromString(this.router.url); - const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, url.payload.values[0], sort, query); - this.addLinks(route); - this.linkHeadService.addTag({ - href: environment.rest.baseUrl + '/' + url.payload.values[0] + '/service', - type: 'application/atom+xml', - rel: 'search', - title: 'Dspace' - }); - this.route$ = new BehaviorSubject(route); - }); + map((response: RemoteData) => response.payload.values[0]), + switchMap((openSearchUri: string) => + this.searchConfigurationService.paginatedSearchOptions.pipe( + map((searchOptions: PaginatedSearchOptions) => ({ openSearchUri, searchOptions })) + ) + ), + ).subscribe(({ openSearchUri, searchOptions }) => { + this.uuid = this.groupDataService.getUUIDFromString(this.router.url); + const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, openSearchUri, searchOptions.sort, searchOptions.query); + this.addLinks(route); + this.linkHeadService.addTag({ + href: environment.rest.baseUrl + '/' + openSearchUri + '/service', + type: 'application/atom+xml', + rel: 'search', + title: 'Dspace' }); - }); + this.route$ = new BehaviorSubject(route); + })); } /** @@ -99,7 +108,7 @@ export class RSSComponent implements OnInit, OnDestroy { if (uuid) { route += `&scope=${uuid}`; } - if (sort.direction && sort.field) { + if (sort && sort.direction && sort.field) { route += `&sort=${sort.field}&sort_direction=${sort.direction}`; } if (query) { From 6cd22fa004b52077ad63c02cd302c1c4d4fb1342 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Wed, 4 May 2022 15:27:10 -0400 Subject: [PATCH 361/570] w2p-86403 prevent from displaying on pages where rss feed doesn't make sense --- src/app/shared/rss-feed/rss.component.html | 2 +- src/app/shared/rss-feed/rss.component.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/app/shared/rss-feed/rss.component.html b/src/app/shared/rss-feed/rss.component.html index 8868539b5c..895a9c2f97 100644 --- a/src/app/shared/rss-feed/rss.component.html +++ b/src/app/shared/rss-feed/rss.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 56ed052a9f..20fb040013 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -120,6 +120,17 @@ export class RSSComponent implements OnInit, OnDestroy { return route; } + /** + * Check if the router url contains the specified route + * + * @param {string} route + * @returns + * @memberof MyComponent + */ + hasRoute(route: string) { + return this.router.url.includes(route); + } + /** * Creates tags in the header of the page * @param route The composed url to opensearch From eacbcfd15d803ea02dede2530934458f336a0e46 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 6 May 2022 10:15:51 -0400 Subject: [PATCH 362/570] Change to withNotEmptyPayload to pass through if its empty --- src/app/shared/rss-feed/rss.component.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 20fb040013..880ed8b558 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -9,7 +9,7 @@ import { BehaviorSubject, Observable, Subscription } from 'rxjs'; 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 { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataWithNotEmptyPayload } 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'; @@ -68,14 +68,14 @@ export class RSSComponent implements OnInit, OnDestroy { this.configuration$ = this.searchConfigurationService.getCurrentConfiguration('default'); this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.enable').pipe( - getFirstCompletedRemoteData(), + getFirstSucceededRemoteDataWithNotEmptyPayload(), ).subscribe((result) => { - const enabled = (result.payload.values[0] === 'true'); + const enabled = (result.values[0] === 'true'); this.isEnabled$.next(enabled); })); this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.svccontext').pipe( - getFirstCompletedRemoteData(), - map((response: RemoteData) => response.payload.values[0]), + getFirstSucceededRemoteDataWithNotEmptyPayload(), + map((result) => result.values[0]), switchMap((openSearchUri: string) => this.searchConfigurationService.paginatedSearchOptions.pipe( map((searchOptions: PaginatedSearchOptions) => ({ openSearchUri, searchOptions })) From cb1b7ceb0a8e134fc1b277de97b83c34931b2115 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 6 May 2022 10:17:30 -0400 Subject: [PATCH 363/570] remove rss feed from search --- src/app/shared/rss-feed/rss.component.html | 2 +- src/app/shared/rss-feed/rss.component.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/shared/rss-feed/rss.component.html b/src/app/shared/rss-feed/rss.component.html index 895a9c2f97..e86e72d15a 100644 --- a/src/app/shared/rss-feed/rss.component.html +++ b/src/app/shared/rss-feed/rss.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 880ed8b558..1d76daabf1 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -9,14 +9,13 @@ import { BehaviorSubject, Observable, Subscription } from 'rxjs'; 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 { getFirstCompletedRemoteData, getFirstSucceededRemoteDataWithNotEmptyPayload } from '../../core/shared/operators'; +import { getFirstSucceededRemoteDataWithNotEmptyPayload } 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'; -import { RemoteData } from '../../core/data/remote-data'; import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model'; From 79bf27c659aff1fb798954ce826dcb949dcdbf69 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 6 May 2022 14:03:14 -0400 Subject: [PATCH 364/570] Revert to getFirstCompletedRemoteData but check if payload has completed --- src/app/shared/rss-feed/rss.component.ts | 25 +++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 1d76daabf1..9a101e8310 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -9,7 +9,7 @@ import { BehaviorSubject, Observable, Subscription } from 'rxjs'; 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 { getFirstSucceededRemoteDataWithNotEmptyPayload } from '../../core/shared/operators'; +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'; @@ -17,6 +17,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { Router } from '@angular/router'; import { map, switchMap } from 'rxjs/operators'; import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model'; +import { RemoteData } from '../../core/data/remote-data'; /** @@ -67,20 +68,30 @@ export class RSSComponent implements OnInit, OnDestroy { this.configuration$ = this.searchConfigurationService.getCurrentConfiguration('default'); this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.enable').pipe( - getFirstSucceededRemoteDataWithNotEmptyPayload(), + getFirstCompletedRemoteData(), ).subscribe((result) => { - const enabled = (result.values[0] === 'true'); - this.isEnabled$.next(enabled); + if (result.hasSucceeded) { + const enabled = (result.payload.values[0] === 'true'); + this.isEnabled$.next(enabled); + } })); this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.svccontext').pipe( - getFirstSucceededRemoteDataWithNotEmptyPayload(), - map((result) => result.values[0]), - switchMap((openSearchUri: string) => + getFirstCompletedRemoteData(), + map((result: RemoteData) => { + if (result.hasSucceeded) { + return result.payload.values[0]; + } + return null; + }), + switchMap((openSearchUri: string) => this.searchConfigurationService.paginatedSearchOptions.pipe( map((searchOptions: PaginatedSearchOptions) => ({ openSearchUri, searchOptions })) ) ), ).subscribe(({ openSearchUri, searchOptions }) => { + if (!openSearchUri) { + return null; + } this.uuid = this.groupDataService.getUUIDFromString(this.router.url); const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, openSearchUri, searchOptions.sort, searchOptions.query); this.addLinks(route); From 0791287cf9265ff9d329085f5dd9172335c75ca6 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 6 May 2022 14:09:08 -0400 Subject: [PATCH 365/570] lint fixes --- src/app/core/core.module.ts | 3 --- src/app/shared/rss-feed/rss.component.ts | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index f6cde90253..27e2326818 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -164,12 +164,9 @@ import { SequenceService } from './shared/sequence.service'; import { CoreState } from './core-state.model'; import { GroupDataService } from './eperson/group-data.service'; import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; -<<<<<<< HEAD import { AccessStatusObject } from '../shared/object-list/access-status-badge/access-status.model'; import { AccessStatusDataService } from './data/access-status-data.service'; -======= import { LinkHeadService } from './services/link-head.service'; ->>>>>>> 354768d98 (w2p-85140 ds-rss component now adds a button to all search pages and community and collection, link-head service adds the rss to the head element) /** * When not in production, endpoint responses can be mocked for testing purposes diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index 9a101e8310..b694280e3c 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -77,13 +77,13 @@ export class RSSComponent implements OnInit, OnDestroy { })); this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.svccontext').pipe( getFirstCompletedRemoteData(), - map((result: RemoteData) => { + map((result: RemoteData) => { if (result.hasSucceeded) { return result.payload.values[0]; } return null; }), - switchMap((openSearchUri: string) => + switchMap((openSearchUri: string) => this.searchConfigurationService.paginatedSearchOptions.pipe( map((searchOptions: PaginatedSearchOptions) => ({ openSearchUri, searchOptions })) ) From 42c459a51d5ebcf2eff3423fe239968e24dfcd9c Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 12 May 2022 16:13:43 +0200 Subject: [PATCH 366/570] [CST-5303] added labels into i18n --- src/assets/i18n/en.json5 | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 9bdf41346d..bb2bcf8ebc 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2158,6 +2158,22 @@ "item.preview.dc.title": "Title:", + "item.preview.dc.type": "Type:", + + "item.preview.oaire.citation.issue" : "Issue", + + "item.preview.oaire.citation.volume" : "Volume", + + "item.preview.dc.relation.issn" : "ISSN", + + "item.preview.dc.identifier.isbn" : "ISBN", + + "item.preview.dc.identifier": "Identifier:", + + "item.preview.dc.relation.ispartof" : "Journal or Serie", + + "item.preview.dc.identifier.doi" : "DOI", + "item.preview.person.familyName": "Surname:", "item.preview.person.givenName": "Name:", @@ -3568,6 +3584,22 @@ "submission.import-external.source.arxiv": "arXiv", + "submission.import-external.source.ads": "NASA/ADS", + + "submission.import-external.source.cinii": "CiNii", + + "submission.import-external.source.crossref": "CrossRef", + + "submission.import-external.source.scielo": "SciELO", + + "submission.import-external.source.scopus": "Scopus", + + "submission.import-external.source.vufind": "VuFind", + + "submission.import-external.source.wos": "Web Of Science", + + "submission.import-external.source.epo": "European Patent Office (EPO)", + "submission.import-external.source.loading": "Loading ...", "submission.import-external.source.sherpaJournal": "SHERPA Journals", @@ -3582,10 +3614,14 @@ "submission.import-external.source.pubmed": "Pubmed", + "submission.import-external.source.pubmedeu": "Pubmed Europe", + "submission.import-external.source.lcname": "Library of Congress Names", "submission.import-external.preview.title": "Item Preview", + "submission.import-external.preview.title.Publication": "Publication Preview", + "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.button.import": "Start submission", @@ -3824,6 +3860,18 @@ "submission.sections.describe.relationship-lookup.selection-tab.title.arxiv": "Search Results", + "submission.sections.describe.relationship-lookup.selection-tab.title.crossref": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title.epo": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title.scopus": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title.scielo": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title.wos": "Search Results", + + "submission.sections.describe.relationship-lookup.selection-tab.title": "Search Results", + "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.confirm": "Save a new name variant", From c1f64ff1ef9a6846b9926968ccfd0d8ec2d548f5 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 12 May 2022 18:50:25 +0200 Subject: [PATCH 367/570] [CST-5329] Add help text to validate only checkbox --- .../metadata-import-page.component.html | 15 +++++++++++---- src/assets/i18n/en.json5 | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html index 2c710935d5..24901cc11d 100644 --- a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html +++ b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.html @@ -1,10 +1,17 @@

{{'admin.metadata-import.page.help' | translate}}

-

- - {{'admin.metadata-import.page.validateOnly' | translate}} -

+
+
+ + +
+ + {{'admin.metadata-import.page.validateOnly.hint' | translate}} + +
Date: Fri, 13 May 2022 13:45:49 +0200 Subject: [PATCH 368/570] 91272: Fixed import statements and position of expandable navbar button --- .../themed-expandable-navbar-section.component.ts | 4 ++-- src/app/navbar/navbar.component.html | 4 ++-- .../expandable-navbar-section.component.ts | 4 ++-- src/themes/dspace/app/navbar/navbar.component.html | 4 ++-- src/themes/dspace/styles/_global-styles.scss | 3 +-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts b/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts index 744eeb5a02..e33dca4104 100644 --- a/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts +++ b/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts @@ -2,13 +2,13 @@ import { Component } from '@angular/core'; import { ThemedComponent } from '../../shared/theme-support/themed.component'; import { ExpandableNavbarSectionComponent } from './expandable-navbar-section.component'; import { rendersSectionForMenu } from '../../shared/menu/menu-section.decorator'; -import { MenuID } from '../../shared/menu/initial-menus-state'; +import { MenuID } from '../../shared/menu/menu-id.model'; /** * Themed wrapper for ExpandableNavbarSectionComponent */ @Component({ - /* tslint:disable:component-selector */ + /* eslint-disable @angular-eslint/component-selector */ selector: 'li[ds-themed-expandable-navbar-section]', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 63f308fb32..b41f2ea706 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -4,7 +4,7 @@
-
- \ No newline at end of file + diff --git a/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts index 283b9ca6f6..f7efd1fdca 100644 --- a/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts +++ b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts @@ -4,13 +4,13 @@ import { } from '../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component'; import { slide } from '../../../../../app/shared/animations/slide'; import { rendersSectionForMenu } from '../../../../../app/shared/menu/menu-section.decorator'; -import { MenuID } from '../../../../../app/shared/menu/initial-menus-state'; +import { MenuID } from '../../../../../app/shared/menu/menu-id.model'; /** * Represents an expandable section in the navbar */ @Component({ - /* tslint:disable:component-selector */ + /* eslint-disable @angular-eslint/component-selector */ selector: 'li[ds-expandable-navbar-section]', // templateUrl: './expandable-navbar-section.component.html', templateUrl: '../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component.html', diff --git a/src/themes/dspace/app/navbar/navbar.component.html b/src/themes/dspace/app/navbar/navbar.component.html index 218fc368ba..6b71728494 100644 --- a/src/themes/dspace/app/navbar/navbar.component.html +++ b/src/themes/dspace/app/navbar/navbar.component.html @@ -6,7 +6,7 @@
-
- \ No newline at end of file + diff --git a/src/themes/dspace/styles/_global-styles.scss b/src/themes/dspace/styles/_global-styles.scss index 8682e3dcdf..3271f15bf2 100644 --- a/src/themes/dspace/styles/_global-styles.scss +++ b/src/themes/dspace/styles/_global-styles.scss @@ -23,8 +23,7 @@ } header { - li > .navbar-section, - li > .expandable-navbar-section { + .navbar-navigation > li { display: flex; flex-direction: column; justify-content: center; From e197e496b51aad5d05e4e6684a84cf435a1e15d4 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 13 May 2022 17:19:42 +0200 Subject: [PATCH 369/570] 91272: Fixed accessibility violation --- .../admin-sidebar-section/admin-sidebar-section.component.ts | 2 ++ .../expandable-admin-sidebar-section.component.ts | 2 ++ .../expandable-navbar-section.component.ts | 3 +-- src/app/navbar/navbar-section/navbar-section.component.ts | 2 ++ .../expandable-navbar-section.component.ts | 3 +-- 5 files changed, 8 insertions(+), 4 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 47f693cb99..422431d2c5 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 @@ -14,6 +14,8 @@ import { Router } from '@angular/router'; @Component({ /* eslint-disable @angular-eslint/component-selector */ selector: 'li[ds-admin-sidebar-section]', + // To theme this element remove the surrounding li[] in this component but leave it in the new + // ThemedAdminSidebarSectionComponent templateUrl: './admin-sidebar-section.component.html', styleUrls: ['./admin-sidebar-section.component.scss'], 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 7cd20b15d2..28cadcac5f 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 @@ -17,6 +17,8 @@ import { Router } from '@angular/router'; @Component({ /* eslint-disable @angular-eslint/component-selector */ selector: 'li[ds-expandable-admin-sidebar-section]', + // To theme this element remove the surrounding li[] in this component but leave it in the new + // ThemedExpandableAdminSidebarSectionComponent templateUrl: './expandable-admin-sidebar-section.component.html', styleUrls: ['./expandable-admin-sidebar-section.component.scss'], animations: [rotate, slide, bgColor] diff --git a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts index a6d3a52cd2..5bc69bcbb4 100644 --- a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts +++ b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts @@ -11,8 +11,7 @@ import { MenuID } from '../../shared/menu/menu-id.model'; * Represents an expandable section in the navbar */ @Component({ - /* eslint-disable @angular-eslint/component-selector */ - selector: 'li[ds-expandable-navbar-section]', + selector: 'ds-expandable-navbar-section', templateUrl: './expandable-navbar-section.component.html', styleUrls: ['./expandable-navbar-section.component.scss'], animations: [slide] diff --git a/src/app/navbar/navbar-section/navbar-section.component.ts b/src/app/navbar/navbar-section/navbar-section.component.ts index 9f75a96f6e..20d5299584 100644 --- a/src/app/navbar/navbar-section/navbar-section.component.ts +++ b/src/app/navbar/navbar-section/navbar-section.component.ts @@ -10,6 +10,8 @@ import { MenuID } from '../../shared/menu/menu-id.model'; @Component({ /* eslint-disable @angular-eslint/component-selector */ selector: 'li[ds-navbar-section]', + // To theme this element remove the surrounding li[] in this component but leave it in the new + // ThemedNavbarSectionComponent templateUrl: './navbar-section.component.html', styleUrls: ['./navbar-section.component.scss'] }) diff --git a/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts index f7efd1fdca..3e11271bf0 100644 --- a/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts +++ b/src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts @@ -10,8 +10,7 @@ import { MenuID } from '../../../../../app/shared/menu/menu-id.model'; * Represents an expandable section in the navbar */ @Component({ - /* eslint-disable @angular-eslint/component-selector */ - selector: 'li[ds-expandable-navbar-section]', + selector: 'ds-expandable-navbar-section', // templateUrl: './expandable-navbar-section.component.html', templateUrl: '../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component.html', // styleUrls: ['./expandable-navbar-section.component.scss'], From 460efa42c732dc80979e8733793c439282141c1e Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 13 May 2022 19:33:40 +0200 Subject: [PATCH 370/570] 91272: Fix merge request --- src/app/admin/admin-sidebar/admin-sidebar.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index 5febf93deb..d4071b91ed 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -41,7 +41,6 @@ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { MenuID } from '../../shared/menu/menu-id.model'; import { MenuItemType } from '../../shared/menu/menu-item-type.model'; import { ActivatedRoute } from '@angular/router'; -import { Router, ActivatedRoute } from '@angular/router'; import { ThemeService } from '../../shared/theme-support/theme.service'; /** From 3a6112532edc27b438f7c995e64c2ba05626e6db Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 13 May 2022 13:32:33 -0400 Subject: [PATCH 371/570] for rss sort id should be date.accessioned so defaulted to that when its the case --- src/app/shared/rss-feed/rss.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts index b694280e3c..3fdb859bdc 100644 --- a/src/app/shared/rss-feed/rss.component.ts +++ b/src/app/shared/rss-feed/rss.component.ts @@ -118,7 +118,7 @@ export class RSSComponent implements OnInit, OnDestroy { if (uuid) { route += `&scope=${uuid}`; } - if (sort && sort.direction && sort.field) { + if (sort && sort.direction && sort.field && sort.field !== 'id') { route += `&sort=${sort.field}&sort_direction=${sort.direction}`; } if (query) { From 743513cf848a7db3c5ec1fc97817a07fd6cdc134 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 13 May 2022 20:35:08 +0200 Subject: [PATCH 372/570] 89676: Added empty SearchResultsComponent files --- .../shared/search/search-results/search-results.component.html | 0 .../shared/search/search-results/search-results.component.scss | 0 .../app/shared/search/search-results/search-results.component.ts | 1 + 3 files changed, 1 insertion(+) create mode 100644 src/themes/custom/app/shared/search/search-results/search-results.component.html create mode 100644 src/themes/custom/app/shared/search/search-results/search-results.component.scss diff --git a/src/themes/custom/app/shared/search/search-results/search-results.component.html b/src/themes/custom/app/shared/search/search-results/search-results.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/shared/search/search-results/search-results.component.scss b/src/themes/custom/app/shared/search/search-results/search-results.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/shared/search/search-results/search-results.component.ts b/src/themes/custom/app/shared/search/search-results/search-results.component.ts index bdfeea15a5..5084d4d405 100644 --- a/src/themes/custom/app/shared/search/search-results/search-results.component.ts +++ b/src/themes/custom/app/shared/search/search-results/search-results.component.ts @@ -6,6 +6,7 @@ import { fadeIn, fadeInOut } from '../../../../../../app/shared/animations/fade' selector: 'ds-search-results', // templateUrl: './search-results.component.html', templateUrl: '../../../../../../app/shared/search/search-results/search-results.component.html', + // styleUrls: ['./search-results.component.scss'], animations: [ fadeIn, fadeInOut From 913d128e165e5ef3f3784a2e25dd0bccab26503d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 13 May 2022 16:57:28 -0500 Subject: [PATCH 373/570] Fix accessibility issue: link has no discernible text --- src/app/shared/rss-feed/rss.component.html | 2 +- src/assets/i18n/en.json5 | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/shared/rss-feed/rss.component.html b/src/app/shared/rss-feed/rss.component.html index e86e72d15a..91140c50c5 100644 --- a/src/app/shared/rss-feed/rss.component.html +++ b/src/app/shared/rss-feed/rss.component.html @@ -1,5 +1,5 @@
- +
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 110b66c176..520d38cf69 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1401,6 +1401,9 @@ "error.validation.groupExists": "This group already exists", + "feed.description": "Syndication feed", + + "file-section.error.header": "Error obtaining files for this item", From 4c5c99d05d3a5ba0b5abd2add953f14082ebea55 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 May 2022 16:44:35 +0200 Subject: [PATCH 374/570] [CST-5535] Add missing i18n label --- src/assets/i18n/en.json5 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 44d2d07305..c129187023 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2503,13 +2503,15 @@ "menu.section.icon.find": "Find menu section", + "menu.section.icon.health": "Health check menu section", + "menu.section.icon.import": "Import menu section", "menu.section.icon.new": "New menu section", "menu.section.icon.pin": "Pin sidebar", - "menu.section.icon.processes": "Processes menu section", + "menu.section.icon.processes": "Processes Health", "menu.section.icon.registries": "Registries menu section", From 0de3c2ed48d6f5861ae008eba84757888e7860fe Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 May 2022 16:45:00 +0200 Subject: [PATCH 375/570] [CST-5535] Replace icons --- .../health-info-component.component.html | 6 +++--- .../health-component/health-component.component.html | 4 ++-- .../health-page/health-panel/health-panel.component.html | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.html b/src/app/health-page/health-info/health-info-component/health-info-component.component.html index 55c1b3372f..8ce7595980 100644 --- a/src/app/health-page/health-info/health-info-component/health-info-component.component.html +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.html @@ -1,13 +1,13 @@
-
+
- - + +
diff --git a/src/app/health-page/health-panel/health-component/health-component.component.html b/src/app/health-page/health-panel/health-component/health-component.component.html index 8171917767..4569d06dad 100644 --- a/src/app/health-page/health-panel/health-component/health-component.component.html +++ b/src/app/health-page/health-panel/health-component/health-component.component.html @@ -6,8 +6,8 @@ {{ entry.key | titlecase }}
- - + +
diff --git a/src/app/health-page/health-panel/health-panel.component.html b/src/app/health-page/health-panel/health-panel.component.html index d582fb77f3..eebcfe55ec 100644 --- a/src/app/health-page/health-panel/health-panel.component.html +++ b/src/app/health-page/health-panel/health-panel.component.html @@ -1,14 +1,14 @@

{{'health-page.status' | translate}} :

-
+
- - + +
From 80ff8a517ce95c16ab21b376d2e4b664dbe4b5d4 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 May 2022 16:46:33 +0200 Subject: [PATCH 376/570] [CST-5535] Rename health-data.service --- src/app/health-page/health-page.component.spec.ts | 4 ++-- src/app/health-page/health-page.component.ts | 4 ++-- .../health-page/{health-data.service.ts => health.service.ts} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/app/health-page/{health-data.service.ts => health.service.ts} (97%) diff --git a/src/app/health-page/health-page.component.spec.ts b/src/app/health-page/health-page.component.spec.ts index 205af8036a..f3847ab092 100644 --- a/src/app/health-page/health-page.component.spec.ts +++ b/src/app/health-page/health-page.component.spec.ts @@ -7,7 +7,7 @@ import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { HealthPageComponent } from './health-page.component'; -import { HealthDataService } from './health-data.service'; +import { HealthService } from './health.service'; import { HealthInfoResponseObj, HealthResponseObj } from '../shared/mocks/health-endpoint.mocks'; import { RawRestResponse } from '../core/dspace-rest/raw-rest-response.model'; import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; @@ -47,7 +47,7 @@ describe('HealthPageComponent', () => { ], declarations: [ HealthPageComponent ], providers: [ - { provide: HealthDataService, useValue: healthService } + { provide: HealthService, useValue: healthService } ] }) .compileComponents(); diff --git a/src/app/health-page/health-page.component.ts b/src/app/health-page/health-page.component.ts index e4f4be7a03..eb07b63add 100644 --- a/src/app/health-page/health-page.component.ts +++ b/src/app/health-page/health-page.component.ts @@ -3,7 +3,7 @@ import { Component, OnInit } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; -import { HealthDataService } from './health-data.service'; +import { HealthService } from './health.service'; import { HealthInfoResponse, HealthResponse } from './models/health-component.model'; @Component({ @@ -23,7 +23,7 @@ export class HealthPageComponent implements OnInit { */ healthResponse: BehaviorSubject = new BehaviorSubject(null); - constructor(private healthDataService: HealthDataService) { + constructor(private healthDataService: HealthService) { } /** diff --git a/src/app/health-page/health-data.service.ts b/src/app/health-page/health.service.ts similarity index 97% rename from src/app/health-page/health-data.service.ts rename to src/app/health-page/health.service.ts index bd905006aa..7c238769a1 100644 --- a/src/app/health-page/health-data.service.ts +++ b/src/app/health-page/health.service.ts @@ -8,7 +8,7 @@ import { HALEndpointService } from '../core/shared/hal-endpoint.service'; @Injectable({ providedIn: 'root' }) -export class HealthDataService { +export class HealthService { constructor(protected halService: HALEndpointService, protected restService: DspaceRestService) { } From 4f7e37d348dfeccca1cc7e1f8b7efeb526b11207 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 May 2022 18:17:35 +0200 Subject: [PATCH 377/570] [CST-5535] Use accordion for health status components --- .../health-info-component.component.html | 38 ++++++++++++++--- .../health-info-component.component.spec.ts | 4 +- .../health-info/health-info.component.html | 30 ++++++++++++- .../health-info/health-info.component.spec.ts | 8 +++- .../health-info/health-info.component.ts | 12 +++++- .../health-component.component.html | 3 +- .../health-panel/health-panel.component.html | 42 ++++++++++--------- .../health-panel.component.spec.ts | 9 ++-- .../health-panel/health-panel.component.ts | 11 +++-- src/app/shared/mocks/health-endpoint.mocks.ts | 24 ++++++++++- 10 files changed, 139 insertions(+), 42 deletions(-) diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.html b/src/app/health-page/health-info/health-info-component/health-info-component.component.html index 8ce7595980..b16e88564f 100644 --- a/src/app/health-page/health-info/health-info-component/health-info-component.component.html +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.html @@ -1,4 +1,34 @@ - +
+
+
+ +
+ + +
+
+
+
+
+ +
+
+
+
+ +

{{ entry.key | titlecase }} : {{entry.value}}

+
+
+ + + + diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts b/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts index 2297007cd5..437d53a953 100644 --- a/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts @@ -46,7 +46,9 @@ describe('HealthInfoComponentComponent', () => { }); it('should display property', () => { - const components = fixture.debugElement.queryAll(By.css('[data-test="component"]')); + const properties = fixture.debugElement.queryAll(By.css('[data-test="property"]')); + expect(properties.length).toBe(14); + const components = fixture.debugElement.queryAll(By.css('[data-test="info-component"]')); expect(components.length).toBe(4); }); diff --git a/src/app/health-page/health-info/health-info.component.html b/src/app/health-page/health-info/health-info.component.html index e4d29adf54..be69df23b4 100644 --- a/src/app/health-page/health-info/health-info.component.html +++ b/src/app/health-page/health-info/health-info.component.html @@ -1,7 +1,33 @@ -
+ + + +
+ +
+ +
+ + +
+
+
+
+ + + +
+
+ + + + diff --git a/src/app/health-page/health-info/health-info.component.spec.ts b/src/app/health-page/health-info/health-info.component.spec.ts index 3af1d71db3..a7f319b88b 100644 --- a/src/app/health-page/health-info/health-info.component.spec.ts +++ b/src/app/health-page/health-info/health-info.component.spec.ts @@ -4,6 +4,8 @@ import { HealthInfoComponent } from './health-info.component'; import { HealthInfoResponseObj } from '../../shared/mocks/health-endpoint.mocks'; import { ObjNgFor } from '../../shared/utils/object-ngfor.pipe'; import { By } from '@angular/platform-browser'; +import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('HealthInfoComponent', () => { let component: HealthInfoComponent; @@ -11,10 +13,14 @@ describe('HealthInfoComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ + imports: [ + NgbAccordionModule, + ], declarations: [ HealthInfoComponent, ObjNgFor - ] + ], + schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); }); diff --git a/src/app/health-page/health-info/health-info.component.ts b/src/app/health-page/health-info/health-info.component.ts index a5fb0b282b..9fddaeb7e4 100644 --- a/src/app/health-page/health-info/health-info.component.ts +++ b/src/app/health-page/health-info/health-info.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { HealthInfoResponse } from '../models/health-component.model'; @@ -7,8 +7,16 @@ import { HealthInfoResponse } from '../models/health-component.model'; templateUrl: './health-info.component.html', styleUrls: ['./health-info.component.scss'] }) -export class HealthInfoComponent { +export class HealthInfoComponent implements OnInit { @Input() healthInfoResponse: HealthInfoResponse; + /** + * The first active panel id + */ + activeId: string; + + ngOnInit(): void { + this.activeId = Object.keys(this.healthInfoResponse)[0]; + } } diff --git a/src/app/health-page/health-panel/health-component/health-component.component.html b/src/app/health-page/health-panel/health-component/health-component.component.html index 4569d06dad..7089fe25c6 100644 --- a/src/app/health-page/health-panel/health-component/health-component.component.html +++ b/src/app/health-page/health-panel/health-component/health-component.component.html @@ -22,6 +22,7 @@
- {{ item.key | titlecase }} : {{item.value}} +

{{ item.key | titlecase }} : {{item.value}}

+
diff --git a/src/app/health-page/health-panel/health-panel.component.html b/src/app/health-page/health-panel/health-panel.component.html index eebcfe55ec..d47a73d820 100644 --- a/src/app/health-page/health-panel/health-panel.component.html +++ b/src/app/health-page/health-panel/health-panel.component.html @@ -1,21 +1,25 @@

{{'health-page.status' | translate}} :

-
-
- -
- - - -
-
-
-
-
- + + + +
+ +
+ +
+ + +
+
-
-
-
+ + + + + + + + diff --git a/src/app/health-page/health-panel/health-panel.component.spec.ts b/src/app/health-page/health-panel/health-panel.component.spec.ts index da392f7ba8..1d9c856ddb 100644 --- a/src/app/health-page/health-panel/health-panel.component.spec.ts +++ b/src/app/health-page/health-panel/health-panel.component.spec.ts @@ -5,14 +5,14 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { NgbCollapseModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbAccordionModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { HealthPanelComponent } from './health-panel.component'; import { HealthResponseObj } from '../../shared/mocks/health-endpoint.mocks'; import { ObjNgFor } from '../../shared/utils/object-ngfor.pipe'; -describe('HealthComponent', () => { +describe('HealthPanelComponent', () => { let component: HealthPanelComponent; let fixture: ComponentFixture; @@ -20,7 +20,7 @@ describe('HealthComponent', () => { await TestBed.configureTestingModule({ imports: [ NgbNavModule, - NgbCollapseModule, + NgbAccordionModule, CommonModule, BrowserAnimationsModule, TranslateModule.forRoot({ @@ -42,7 +42,6 @@ describe('HealthComponent', () => { fixture = TestBed.createComponent(HealthPanelComponent); component = fixture.componentInstance; component.healthResponse = HealthResponseObj; - component.isCollapsed = false; fixture.detectChanges(); }); @@ -50,7 +49,7 @@ describe('HealthComponent', () => { expect(component).toBeTruthy(); }); - it('should render a card for each component', () => { + it('should render a panel for each component', () => { const components = fixture.debugElement.queryAll(By.css('[data-test="component"]')); expect(components.length).toBe(5); }); diff --git a/src/app/health-page/health-panel/health-panel.component.ts b/src/app/health-page/health-panel/health-panel.component.ts index 549544c370..8bb670e67f 100644 --- a/src/app/health-page/health-panel/health-panel.component.ts +++ b/src/app/health-page/health-panel/health-panel.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { HealthResponse } from '../models/health-component.model'; @Component({ @@ -6,7 +6,7 @@ import { HealthResponse } from '../models/health-component.model'; templateUrl: './health-panel.component.html', styleUrls: ['./health-panel.component.scss'] }) -export class HealthPanelComponent { +export class HealthPanelComponent implements OnInit { /** * Health endpoint response @@ -14,8 +14,11 @@ export class HealthPanelComponent { @Input() healthResponse: HealthResponse; /** - * A boolean representing if div should start collapsed + * The first active panel id */ - public isCollapsed = true; + activeId: string; + ngOnInit(): void { + this.activeId = Object.keys(this.healthResponse.components)[0]; + } } diff --git a/src/app/shared/mocks/health-endpoint.mocks.ts b/src/app/shared/mocks/health-endpoint.mocks.ts index 9bd3956139..a9246d91a1 100644 --- a/src/app/shared/mocks/health-endpoint.mocks.ts +++ b/src/app/shared/mocks/health-endpoint.mocks.ts @@ -134,7 +134,27 @@ export const HealthInfoComponentOne: HealthInfoComponent = { 'name': 'DSpace at My University', 'dir': '/home/giuseppe/development/java/install/dspace7-review', 'url': 'http://localhost:8080/server', - 'db': 'jdbc:postgresql://localhost:5432/dspace7' + 'db': 'jdbc:postgresql://localhost:5432/dspace7', + 'solr': { + 'server': 'http://localhost:8983/solr', + 'prefix': '' + }, + 'mail': { + 'server': 'smtp.example.com', + 'from-address': 'dspace-noreply@myu.edu', + 'feedback-recipient': 'dspace-help@myu.edu', + 'mail-admin': 'dspace-help@myu.edu', + 'mail-helpdesk': 'dspace-help@myu.edu', + 'alert-recipient': 'dspace-help@myu.edu' + }, + 'cors': { + 'allowed-origins': 'http://localhost:4000' + }, + 'ui': { + 'url': 'http://localhost:4000' + } }; -export const HealthInfoComponentTwo = '7.3-SNAPSHOT'; +export const HealthInfoComponentTwo = { + 'version': '7.3-SNAPSHOT' +}; From c508e0035e46c977bb7b0fad2163eff5109c3d81 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 May 2022 18:49:11 +0200 Subject: [PATCH 378/570] [CST-5535] Add tooltip for status icons --- .../health-status/health-status.component.html | 12 +++++++++--- src/assets/i18n/en.json5 | 8 ++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/app/health-page/health-panel/health-status/health-status.component.html b/src/app/health-page/health-panel/health-status/health-status.component.html index fdd726cddf..38a6f72601 100644 --- a/src/app/health-page/health-panel/health-status/health-status.component.html +++ b/src/app/health-page/health-panel/health-status/health-status.component.html @@ -1,6 +1,12 @@ - - - + + + diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index c129187023..78661ced76 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1567,6 +1567,14 @@ + "health-page.status.ok.info": "Operational", + + "health-page.status.error.info": "Problems detected", + + "health-page.status.warning.info": "Possible issues detected", + + + "home.description": "", "home.breadcrumbs": "Home", From 73573793a0eacc52789c4df6b7a653040393f407 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 May 2022 18:50:07 +0200 Subject: [PATCH 379/570] [CST-5535] Add i18n keys for section's headers --- .../health-info/health-info.component.html | 2 +- .../health-panel/health-panel.component.html | 2 +- src/assets/i18n/en.json5 | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/app/health-page/health-info/health-info.component.html b/src/app/health-page/health-info/health-info.component.html index be69df23b4..12764ead45 100644 --- a/src/app/health-page/health-info/health-info.component.html +++ b/src/app/health-page/health-info/health-info.component.html @@ -5,7 +5,7 @@
diff --git a/src/app/health-page/health-panel/health-panel.component.html b/src/app/health-page/health-panel/health-panel.component.html index d47a73d820..d095ce2f6d 100644 --- a/src/app/health-page/health-panel/health-panel.component.html +++ b/src/app/health-page/health-panel/health-panel.component.html @@ -5,7 +5,7 @@
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 78661ced76..c22ec1c3ee 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1566,6 +1566,21 @@ "grant-request-copy.success": "Successfully granted item request", + "health-page.section.db.title": "Database", + + "health-page.section.geoIp.title": "GeoIp", + + "health-page.section.solrAuthorityCore.title": "Sor: authority core", + + "health-page.section.solrOaiCore.title": "Sor: oai core", + + "health-page.section.solrSearchCore.title": "Sor: search core", + + "health-page.section.solrStatisticsCore.title": "Sor: statistics core", + + "health-page.section-info.app.title": "Application Backend", + + "health-page.section-info.java.title": "Java", "health-page.status.ok.info": "Operational", From 74b68a5e154f1285ad3e7ec9d13fcb30590378d4 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 May 2022 19:19:00 +0200 Subject: [PATCH 380/570] [CST-5535] Add fallback message on rest request error --- .../health-page/health-page.component.html | 45 ++++++++++--------- src/app/health-page/health-page.component.ts | 11 +++-- src/assets/i18n/en.json5 | 2 + 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/app/health-page/health-page.component.html b/src/app/health-page/health-page.component.html index 6ec9abddcb..0647620c73 100644 --- a/src/app/health-page/health-page.component.html +++ b/src/app/health-page/health-page.component.html @@ -1,21 +1,26 @@ -
- -
+
+ +
+ + diff --git a/src/app/health-page/health-page.component.ts b/src/app/health-page/health-page.component.ts index eb07b63add..a92e72744b 100644 --- a/src/app/health-page/health-page.component.ts +++ b/src/app/health-page/health-page.component.ts @@ -30,12 +30,15 @@ export class HealthPageComponent implements OnInit { * Retrieve responses from rest */ ngOnInit(): void { - this.healthDataService.getHealth().pipe(take(1)).subscribe((data: any) => { - this.healthResponse.next(data.payload); + this.healthDataService.getHealth().pipe(take(1)).subscribe({ + next: (data: any) => { this.healthResponse.next(data.payload); }, + error: () => { this.healthResponse.next(null); } }); - this.healthDataService.getInfo().pipe(take(1)).subscribe((data) => { - this.healthInfoResponse.next(data.payload); + this.healthDataService.getInfo().pipe(take(1)).subscribe({ + next: (data: any) => { this.healthInfoResponse.next(data.payload); }, + error: () => { this.healthInfoResponse.next(null); } }); + } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 4fcdbff335..216b29fcd0 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1583,6 +1583,8 @@ "grant-request-copy.success": "Successfully granted item request", + "health-page.error.msg": "The health check service is temporarily unavailable", + "health-page.section.db.title": "Database", "health-page.section.geoIp.title": "GeoIp", From 1f9b8ba92a4fcf09e2f0c6cb8661f7b8b6fdd637 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 17 May 2022 11:16:46 +0200 Subject: [PATCH 381/570] Fix issue where you'd get stuck in an infinite redirect loop when going to the page of an item with an entity type containing certain special characters --- src/app/item-page/item-page.resolver.spec.ts | 87 ++++++++++++++++++++ src/app/item-page/item-page.resolver.ts | 10 ++- 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/app/item-page/item-page.resolver.spec.ts diff --git a/src/app/item-page/item-page.resolver.spec.ts b/src/app/item-page/item-page.resolver.spec.ts new file mode 100644 index 0000000000..533f97d0c0 --- /dev/null +++ b/src/app/item-page/item-page.resolver.spec.ts @@ -0,0 +1,87 @@ +import { ItemPageResolver } from './item-page.resolver'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { DSpaceObject } from '../core/shared/dspace-object.model'; +import { MetadataValueFilter } from '../core/shared/metadata.models'; +import { first } from 'rxjs/operators'; +import { Router } from '@angular/router'; +import { TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +describe('ItemPageResolver', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([{ + path: 'entities/:entity-type/:id', + component: {} as any + }])] + }); + }); + + describe('resolve', () => { + let resolver: ItemPageResolver; + let itemService: any; + let store: any; + let router: any; + + const uuid = '1234-65487-12354-1235'; + let item: DSpaceObject; + + function runTestsWithEntityType(entityType: string) { + beforeEach(() => { + router = TestBed.inject(Router); + item = Object.assign(new DSpaceObject(), { + uuid: uuid, + firstMetadataValue(_keyOrKeys: string | string[], _valueFilter?: MetadataValueFilter): string { + return entityType; + } + }); + itemService = { + findById: (_id: string) => createSuccessfulRemoteDataObject$(item) + }; + store = jasmine.createSpyObj('store', { + dispatch: {}, + }); + resolver = new ItemPageResolver(itemService, store, router); + }); + + it('should redirect to the correct route for the entity type', (done) => { + spyOn(item, 'firstMetadataValue').and.returnValue(entityType); + spyOn(router, 'navigateByUrl').and.callThrough(); + + resolver.resolve({ params: { id: uuid } } as any, { url: router.parseUrl(`/items/${uuid}`).toString() } as any) + .pipe(first()) + .subscribe( + () => { + expect(router.navigateByUrl).toHaveBeenCalledWith(router.parseUrl(`/entities/${entityType}/${uuid}`).toString()); + done(); + } + ); + }); + + it('should not redirect if we’re already on the correct route', (done) => { + spyOn(item, 'firstMetadataValue').and.returnValue(entityType); + spyOn(router, 'navigateByUrl').and.callThrough(); + + resolver.resolve({ params: { id: uuid } } as any, { url: router.parseUrl(`/entities/${entityType}/${uuid}`).toString() } as any) + .pipe(first()) + .subscribe( + () => { + expect(router.navigateByUrl).not.toHaveBeenCalled(); + done(); + } + ); + }); + } + + describe('when normal entity type is provided', () => { + runTestsWithEntityType('publication'); + }); + + describe('when entity type contains a special character', () => { + runTestsWithEntityType('alligator,loki'); + runTestsWithEntityType('🐊'); + runTestsWithEntityType(' '); + }); + + }); +}); diff --git a/src/app/item-page/item-page.resolver.ts b/src/app/item-page/item-page.resolver.ts index 7edffc5357..e9b287406e 100644 --- a/src/app/item-page/item-page.resolver.ts +++ b/src/app/item-page/item-page.resolver.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; import { RemoteData } from '../core/data/remote-data'; import { ItemDataService } from '../core/data/item-data.service'; @@ -35,8 +35,14 @@ export class ItemPageResolver extends ItemResolver { return super.resolve(route, state).pipe( map((rd: RemoteData) => { if (rd.hasSucceeded && hasValue(rd.payload)) { - const itemRoute = getItemPageRoute(rd.payload); const thisRoute = state.url; + + // Angular uses a custom function for encodeURIComponent, (e.g. it doesn't encode commas + // or semicolons) and thisRoute has been encoded with that function. If we want to compare + // it with itemRoute, we have to run itemRoute through Angular's version as well to ensure + // the same characters are encoded the same way. + const itemRoute = this.router.parseUrl(getItemPageRoute(rd.payload)).toString(); + if (!thisRoute.startsWith(itemRoute)) { const itemId = rd.payload.uuid; const subRoute = thisRoute.substring(thisRoute.indexOf(itemId) + itemId.length, thisRoute.length); From 881af6449542ee174206d5b10ec27b87f0254219 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 17 May 2022 16:36:40 +0200 Subject: [PATCH 382/570] [CST-5674] POST replaced with PUT --- .../resource-policy.service.ts | 4 +-- .../form/resource-policy-form.component.html | 34 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/app/core/resource-policy/resource-policy.service.ts b/src/app/core/resource-policy/resource-policy.service.ts index ca3951109a..8411647bea 100644 --- a/src/app/core/resource-policy/resource-policy.service.ts +++ b/src/app/core/resource-policy/resource-policy.service.ts @@ -29,7 +29,7 @@ import { getFirstCompletedRemoteData } from '../shared/operators'; import { CoreState } from '../core-state.model'; import { FindListOptions } from '../data/find-list-options.model'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; -import { PostRequest } from '../data/request.models'; +import { PutRequest } from '../data/request.models'; import { GenericConstructor } from '../shared/generic-constructor'; import { ResponseParsingService } from '../data/parsing.service'; import { StatusCodeOnlyResponseParsingService } from '../data/status-code-only-response-parsing.service'; @@ -260,7 +260,7 @@ export class ResourcePolicyService { return targetEndpoint$.pipe(switchMap((targetEndpoint) => { const resourceEndpoint = resourcePolicyHref + '/' + type; - const request = new PostRequest(requestId, resourceEndpoint, targetEndpoint, options); + const request = new PutRequest(requestId, resourceEndpoint, targetEndpoint, options); Object.assign(request, { getResponseParser(): GenericConstructor { return StatusCodeOnlyResponseParsingService; diff --git a/src/app/shared/resource-policies/form/resource-policy-form.component.html b/src/app/shared/resource-policies/form/resource-policy-form.component.html index f7aad55ce8..66c1fc400e 100644 --- a/src/app/shared/resource-policies/form/resource-policy-form.component.html +++ b/src/app/shared/resource-policies/form/resource-policy-form.component.html @@ -8,24 +8,22 @@
- - -
-
+ +

From 618ff0ce19607900bfd165aec9ae7ef01a9b19a2 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Tue, 17 May 2022 17:45:27 +0200 Subject: [PATCH 383/570] [CST-5303] added missing labels --- src/assets/i18n/en.json5 | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index bb2bcf8ebc..5bae41d0a1 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3644,6 +3644,26 @@ "submission.sections.describe.relationship-lookup.external-source.import-button-title.isProjectOfPublication": "Project", + "submission.sections.describe.relationship-lookup.external-source.import-button-title.none = "Import remote item", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Event": "Import remote event", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Product": "Import remote product", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Equipment": "Import remote equipment", + + "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.Funding": "Import remote fund", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Person = "Import remote person", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Patent": "Import remote patent", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Project = "Import remote project", + + "submission.sections.describe.relationship-lookup.external-source.import-button-title.Publication = "Import remote publication", + "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.title": "Project", From a732f1534de03fbdc89aad33af769dc50f40fb72 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 18 May 2022 09:33:08 +0200 Subject: [PATCH 384/570] [CST-5303] fix wrong format --- src/assets/i18n/en.json5 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 5bae41d0a1..dffbbd4b7e 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3644,7 +3644,7 @@ "submission.sections.describe.relationship-lookup.external-source.import-button-title.isProjectOfPublication": "Project", - "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": "Import remote item", "submission.sections.describe.relationship-lookup.external-source.import-button-title.Event": "Import remote event", @@ -3652,17 +3652,17 @@ "submission.sections.describe.relationship-lookup.external-source.import-button-title.Equipment": "Import remote equipment", - "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": "Import remote organizational unit", "submission.sections.describe.relationship-lookup.external-source.import-button-title.Funding": "Import remote fund", - "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": "Import remote person", "submission.sections.describe.relationship-lookup.external-source.import-button-title.Patent": "Import remote patent", - "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": "Import remote project", - "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": "Import remote publication", "submission.sections.describe.relationship-lookup.external-source.import-modal.isProjectOfPublication.added.new-entity": "New Entity Added!", From d4dc176870d46506ad72670ba391f9f041e1c495 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 18 May 2022 13:04:04 +0200 Subject: [PATCH 385/570] [CST-5535] Add Possibility to have translation also for properties --- .../health-info-component.component.html | 4 ++-- .../health-info-component.component.scss | 3 +++ .../health-info-component.component.spec.ts | 10 +++++++++- .../health-info-component.component.ts | 12 ++++++++++-- .../health-info/health-info.component.html | 4 ++-- .../health-info/health-info.component.scss | 3 +++ .../health-info/health-info.component.spec.ts | 8 ++++++++ .../health-info/health-info.component.ts | 15 +++++++++++++++ .../health-component.component.html | 4 ++-- .../health-component.component.scss | 3 +++ .../health-component.component.spec.ts | 10 +++++++++- .../health-component.component.ts | 17 ++++++++++++++++- .../health-panel/health-panel.component.html | 4 ++-- .../health-panel/health-panel.component.scss | 3 +++ .../health-panel/health-panel.component.ts | 16 ++++++++++++++++ .../health-status.component.spec.ts | 12 ++++++++++++ src/assets/i18n/en.json5 | 2 ++ 17 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.html b/src/app/health-page/health-info/health-info-component/health-info-component.component.html index b16e88564f..b607d95f45 100644 --- a/src/app/health-page/health-info/health-info-component/health-info-component.component.html +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.html @@ -1,6 +1,6 @@
-
+
-

{{ entry.key | titlecase }} : {{entry.value}}

+

{{ getPropertyLabel(entry.key) | titlecase }} : {{entry.value}}

diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.scss b/src/app/health-page/health-info/health-info-component/health-info-component.component.scss index e69de29bb2..a6f0e73413 100644 --- a/src/app/health-page/health-info/health-info-component/health-info-component.component.scss +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.scss @@ -0,0 +1,3 @@ +.collapse-toggle { + cursor: pointer; +} diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts b/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts index 437d53a953..b4532415b8 100644 --- a/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts @@ -8,6 +8,8 @@ import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; import { HealthInfoComponentComponent } from './health-info-component.component'; import { HealthInfoComponentOne, HealthInfoComponentTwo } from '../../../shared/mocks/health-endpoint.mocks'; import { ObjNgFor } from '../../../shared/utils/object-ngfor.pipe'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock'; describe('HealthInfoComponentComponent', () => { let component: HealthInfoComponentComponent; @@ -18,7 +20,13 @@ describe('HealthInfoComponentComponent', () => { imports: [ CommonModule, NgbCollapseModule, - NoopAnimationsModule + NoopAnimationsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }) ], declarations: [ HealthInfoComponentComponent, diff --git a/src/app/health-page/health-info/health-info-component/health-info-component.component.ts b/src/app/health-page/health-info/health-info-component/health-info-component.component.ts index b6c31214c8..159462cd6d 100644 --- a/src/app/health-page/health-info/health-info-component/health-info-component.component.ts +++ b/src/app/health-page/health-info/health-info-component/health-info-component.component.ts @@ -1,13 +1,14 @@ import { Component, Input } from '@angular/core'; import { HealthInfoComponent } from '../../models/health-component.model'; +import { HealthComponentComponent } from '../../health-panel/health-component/health-component.component'; @Component({ selector: 'ds-health-info-component', templateUrl: './health-info-component.component.html', styleUrls: ['./health-info-component.component.scss'] }) -export class HealthInfoComponentComponent { +export class HealthInfoComponentComponent extends HealthComponentComponent { /** * The HealthInfoComponent object to display @@ -27,9 +28,16 @@ export class HealthInfoComponentComponent { /** * A boolean representing if div should start collapsed */ - public isCollapsed = true; + public isCollapsed = false; + /** + * Check if the HealthInfoComponent is has only string property or contains object + * + * @param entry The HealthInfoComponent to check + * @return boolean + */ isPlainProperty(entry: HealthInfoComponent | string): boolean { return typeof entry === 'string'; } + } diff --git a/src/app/health-page/health-info/health-info.component.html b/src/app/health-page/health-info/health-info.component.html index 12764ead45..47e4cfb4d2 100644 --- a/src/app/health-page/health-info/health-info.component.html +++ b/src/app/health-page/health-info/health-info.component.html @@ -2,10 +2,10 @@ -
+
diff --git a/src/app/health-page/health-info/health-info.component.scss b/src/app/health-page/health-info/health-info.component.scss index e69de29bb2..a6f0e73413 100644 --- a/src/app/health-page/health-info/health-info.component.scss +++ b/src/app/health-page/health-info/health-info.component.scss @@ -0,0 +1,3 @@ +.collapse-toggle { + cursor: pointer; +} diff --git a/src/app/health-page/health-info/health-info.component.spec.ts b/src/app/health-page/health-info/health-info.component.spec.ts index a7f319b88b..5a9b8bf0aa 100644 --- a/src/app/health-page/health-info/health-info.component.spec.ts +++ b/src/app/health-page/health-info/health-info.component.spec.ts @@ -6,6 +6,8 @@ import { ObjNgFor } from '../../shared/utils/object-ngfor.pipe'; import { By } from '@angular/platform-browser'; import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; describe('HealthInfoComponent', () => { let component: HealthInfoComponent; @@ -15,6 +17,12 @@ describe('HealthInfoComponent', () => { await TestBed.configureTestingModule({ imports: [ NgbAccordionModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }) ], declarations: [ HealthInfoComponent, diff --git a/src/app/health-page/health-info/health-info.component.ts b/src/app/health-page/health-info/health-info.component.ts index 9fddaeb7e4..d8c629636b 100644 --- a/src/app/health-page/health-info/health-info.component.ts +++ b/src/app/health-page/health-info/health-info.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { HealthInfoResponse } from '../models/health-component.model'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'ds-health-info', @@ -16,7 +17,21 @@ export class HealthInfoComponent implements OnInit { */ activeId: string; + constructor(private translate: TranslateService) { + } + ngOnInit(): void { this.activeId = Object.keys(this.healthInfoResponse)[0]; } + /** + * Return translated label if exist for the given property + * + * @param property + */ + public getPanelLabel(panelKey: string): string { + const translationKey = `health-page.section-info.${panelKey}.title`; + const translation = this.translate.instant(translationKey); + + return (translation === translationKey) ? panelKey : translation; + } } diff --git a/src/app/health-page/health-panel/health-component/health-component.component.html b/src/app/health-page/health-panel/health-component/health-component.component.html index 7089fe25c6..c254f128d9 100644 --- a/src/app/health-page/health-panel/health-component/health-component.component.html +++ b/src/app/health-page/health-panel/health-component/health-component.component.html @@ -1,6 +1,6 @@
-
+
diff --git a/src/app/health-page/health-panel/health-panel.component.scss b/src/app/health-page/health-panel/health-panel.component.scss index e69de29bb2..a6f0e73413 100644 --- a/src/app/health-page/health-panel/health-panel.component.scss +++ b/src/app/health-page/health-panel/health-panel.component.scss @@ -0,0 +1,3 @@ +.collapse-toggle { + cursor: pointer; +} diff --git a/src/app/health-page/health-panel/health-panel.component.ts b/src/app/health-page/health-panel/health-panel.component.ts index 8bb670e67f..3137334d6f 100644 --- a/src/app/health-page/health-panel/health-panel.component.ts +++ b/src/app/health-page/health-panel/health-panel.component.ts @@ -1,5 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { HealthResponse } from '../models/health-component.model'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'ds-health-panel', @@ -18,7 +19,22 @@ export class HealthPanelComponent implements OnInit { */ activeId: string; + constructor(private translate: TranslateService) { + } + ngOnInit(): void { this.activeId = Object.keys(this.healthResponse.components)[0]; } + + /** + * Return translated label if exist for the given property + * + * @param property + */ + public getPanelLabel(panelKey: string): string { + const translationKey = `health-page.section.${panelKey}.title`; + const translation = this.translate.instant(translationKey); + + return (translation === translationKey) ? panelKey : translation; + } } diff --git a/src/app/health-page/health-panel/health-status/health-status.component.spec.ts b/src/app/health-page/health-panel/health-status/health-status.component.spec.ts index 13df9c23e3..f0f61ebdbb 100644 --- a/src/app/health-page/health-panel/health-status/health-status.component.spec.ts +++ b/src/app/health-page/health-panel/health-status/health-status.component.spec.ts @@ -3,6 +3,9 @@ import { By } from '@angular/platform-browser'; import { HealthStatusComponent } from './health-status.component'; import { HealthStatus } from '../../models/health-component.model'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock'; +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; describe('HealthStatusComponent', () => { let component: HealthStatusComponent; @@ -10,6 +13,15 @@ describe('HealthStatusComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ + imports: [ + NgbTooltipModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }) + ], declarations: [ HealthStatusComponent ] }) .compileComponents(); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 216b29fcd0..18a406b77b 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1585,6 +1585,8 @@ "health-page.error.msg": "The health check service is temporarily unavailable", + "health-page.property.status": "Status code", + "health-page.section.db.title": "Database", "health-page.section.geoIp.title": "GeoIp", From ef332af17ea62afce19ef83763ac30b356e63289 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 18 May 2022 13:31:09 +0200 Subject: [PATCH 386/570] [CST-5535] Fix issue with error notice message on health page loading --- .../health-page/health-page.component.html | 2 +- src/app/health-page/health-page.component.ts | 30 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/app/health-page/health-page.component.html b/src/app/health-page/health-page.component.html index 0647620c73..605927dc55 100644 --- a/src/app/health-page/health-page.component.html +++ b/src/app/health-page/health-page.component.html @@ -1,4 +1,4 @@ -
+