From fc6678515a62c4d1d1b05eeb0dd85c1f633489f1 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Thu, 4 Nov 2021 14:21:12 +0100 Subject: [PATCH 001/102] [DSC-287] Created new page error 500, added guard to check for initial api request, redirect to page 500 --- src/app/app-routing-paths.ts | 6 ++ src/app/app-routing.module.ts | 58 +++++++++++-------- src/app/app.module.ts | 6 +- .../server-check/server-check.guard.spec.ts | 16 +++++ .../core/server-check/server-check.guard.ts | 30 ++++++++++ .../page-internal-server-error.component.html | 10 ++++ .../page-internal-server-error.component.scss | 0 .../page-internal-server-error.component.ts | 33 +++++++++++ ...ed-page-internal-server-error.component.ts | 26 +++++++++ src/assets/i18n/en.json5 | 5 ++ 10 files changed, 164 insertions(+), 26 deletions(-) create mode 100644 src/app/core/server-check/server-check.guard.spec.ts create mode 100644 src/app/core/server-check/server-check.guard.ts create mode 100644 src/app/page-internal-server-error/page-internal-server-error.component.html create mode 100644 src/app/page-internal-server-error/page-internal-server-error.component.scss create mode 100644 src/app/page-internal-server-error/page-internal-server-error.component.ts create mode 100644 src/app/page-internal-server-error/themed-page-internal-server-error.component.ts diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index db6b22a023..d9c3410931 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -89,6 +89,12 @@ export function getPageNotFoundRoute() { return `/${PAGE_NOT_FOUND_PATH}`; } +export const INTERNAL_SERVER_ERROR = '500'; + +export function getPageInternalServerErrorRoute() { + return `/${INTERNAL_SERVER_ERROR}`; +} + export const INFO_MODULE_PATH = 'info'; export function getInfoModulePath() { return `/${INFO_MODULE_PATH}`; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 157ada622d..7941adc35c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -26,6 +26,9 @@ import { SiteRegisterGuard } from './core/data/feature-authorization/feature-aut 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 { ServerCheckGuard } from './core/server-check/server-check.guard'; + @NgModule({ imports: [ @@ -33,157 +36,160 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu path: '', canActivate: [AuthBlockingGuard], children: [ { path: '', redirectTo: '/home', pathMatch: 'full' }, - { path: 'reload/:rnd', component: ThemedPageNotFoundComponent, pathMatch: 'full', canActivate: [ReloadGuard] }, + { path: 'reload/:rnd', component: ThemedPageNotFoundComponent, pathMatch: 'full', canActivate: [ServerCheckGuard, ReloadGuard] }, { path: 'home', loadChildren: () => import('./home-page/home-page.module') .then((m) => m.HomePageModule), data: { showBreadcrumbs: false }, - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'community-list', loadChildren: () => import('./community-list-page/community-list-page.module') .then((m) => m.CommunityListPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'id', loadChildren: () => import('./lookup-by-id/lookup-by-id.module') .then((m) => m.LookupIdModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'handle', loadChildren: () => import('./lookup-by-id/lookup-by-id.module') .then((m) => m.LookupIdModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: REGISTER_PATH, loadChildren: () => import('./register-page/register-page.module') .then((m) => m.RegisterPageModule), - canActivate: [SiteRegisterGuard] + canActivate: [ServerCheckGuard, SiteRegisterGuard] }, { path: FORGOT_PASSWORD_PATH, loadChildren: () => import('./forgot-password/forgot-password.module') .then((m) => m.ForgotPasswordModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: COMMUNITY_MODULE_PATH, loadChildren: () => import('./community-page/community-page.module') .then((m) => m.CommunityPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: COLLECTION_MODULE_PATH, loadChildren: () => import('./collection-page/collection-page.module') .then((m) => m.CollectionPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: ITEM_MODULE_PATH, loadChildren: () => import('./item-page/item-page.module') .then((m) => m.ItemPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'entities/:entity-type', loadChildren: () => import('./item-page/item-page.module') .then((m) => m.ItemPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: LEGACY_BITSTREAM_MODULE_PATH, loadChildren: () => import('./bitstream-page/bitstream-page.module') .then((m) => m.BitstreamPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: BITSTREAM_MODULE_PATH, loadChildren: () => import('./bitstream-page/bitstream-page.module') .then((m) => m.BitstreamPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'mydspace', loadChildren: () => import('./my-dspace-page/my-dspace-page.module') .then((m) => m.MyDSpacePageModule), - canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] }, { path: 'search', loadChildren: () => import('./search-page/search-page-routing.module') .then((m) => m.SearchPageRoutingModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'browse', loadChildren: () => import('./browse-by/browse-by-page.module') .then((m) => m.BrowseByPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: ADMIN_MODULE_PATH, loadChildren: () => import('./admin/admin.module') .then((m) => m.AdminModule), - canActivate: [SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] }, { path: 'login', loadChildren: () => import('./login-page/login-page.module') .then((m) => m.LoginPageModule), + canActivate: [ServerCheckGuard] }, { path: 'logout', loadChildren: () => import('./logout-page/logout-page.module') .then((m) => m.LogoutPageModule), + canActivate: [ServerCheckGuard] }, { path: 'submit', loadChildren: () => import('./submit-page/submit-page.module') .then((m) => m.SubmitPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'import-external', loadChildren: () => import('./import-external-page/import-external-page.module') .then((m) => m.ImportExternalPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: 'workspaceitems', loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page.module') .then((m) => m.WorkspaceitemsEditPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: WORKFLOW_ITEM_MODULE_PATH, loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page.module') .then((m) => m.WorkflowItemsEditPageModule), - canActivate: [EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] }, { path: PROFILE_MODULE_PATH, loadChildren: () => import('./profile-page/profile-page.module') .then((m) => m.ProfilePageModule), - canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] }, { path: PROCESS_MODULE_PATH, loadChildren: () => import('./process-page/process-page.module') .then((m) => m.ProcessPageModule), - canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] }, { path: INFO_MODULE_PATH, loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), + canActivate: [ServerCheckGuard] }, { path: REQUEST_COPY_MODULE_PATH, loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), - canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] }, { path: FORBIDDEN_PATH, @@ -193,12 +199,14 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu path: 'statistics', loadChildren: () => import('./statistics-page/statistics-page-routing.module') .then((m) => m.StatisticsPageRoutingModule), + canActivate: [ServerCheckGuard] }, { path: ACCESS_CONTROL_MODULE_PATH, loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), - canActivate: [GroupAdministratorGuard], + canActivate: [ServerCheckGuard, GroupAdministratorGuard], }, + { path: '500', component: ThemedPageInternalServerErrorComponent }, { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, ]} ],{ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e2cb10691b..d288721014 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -53,6 +53,8 @@ import { ThemedFooterComponent } from './footer/themed-footer.component'; import { ThemedBreadcrumbsComponent } from './breadcrumbs/themed-breadcrumbs.component'; import { ThemedHeaderNavbarWrapperComponent } from './header-nav-wrapper/themed-header-navbar-wrapper.component'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; +import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component'; +import { PageInternalServerErrorComponent } from './page-internal-server-error/page-internal-server-error.component'; import { UUIDService } from './core/shared/uuid.service'; import { CookieService } from './core/services/cookie.service'; @@ -187,7 +189,9 @@ const DECLARATIONS = [ ThemedBreadcrumbsComponent, ForbiddenComponent, ThemedForbiddenComponent, - IdleModalComponent + IdleModalComponent, + ThemedPageInternalServerErrorComponent, + PageInternalServerErrorComponent ]; const EXPORTS = [ diff --git a/src/app/core/server-check/server-check.guard.spec.ts b/src/app/core/server-check/server-check.guard.spec.ts new file mode 100644 index 0000000000..fc072cd73c --- /dev/null +++ b/src/app/core/server-check/server-check.guard.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ServerCheckGuard } from './server-check.guard'; + +describe('ServerCheckGuard', () => { + let guard: ServerCheckGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(ServerCheckGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/src/app/core/server-check/server-check.guard.ts b/src/app/core/server-check/server-check.guard.ts new file mode 100644 index 0000000000..32ff57ebb3 --- /dev/null +++ b/src/app/core/server-check/server-check.guard.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { Observable } from 'rxjs'; +import { RootDataService } from '../data/root-data.service'; +import { map, tap } from 'rxjs/operators'; +import { RemoteData } from '../data/remote-data'; +import { getPageInternalServerErrorRoute } from '../../app-routing-paths'; +import { Router } from '@angular/router'; + +@Injectable({ + providedIn: 'root' +}) +export class ServerCheckGuard implements CanActivate { + constructor(private router: Router,private rootDataService: RootDataService) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + + return this.rootDataService.findRoot().pipe( + map( (res: RemoteData) => res.hasSucceeded ), + tap( (responsehasSucceeded: boolean) => { + if (!responsehasSucceeded) { + this.router.navigateByUrl(getPageInternalServerErrorRoute()); + } + }), + ); + + } +} diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.html b/src/app/page-internal-server-error/page-internal-server-error.component.html new file mode 100644 index 0000000000..ab4f3788f3 --- /dev/null +++ b/src/app/page-internal-server-error/page-internal-server-error.component.html @@ -0,0 +1,10 @@ +
+

500

+

{{"500.page-internal-server-error" | translate}}

+
+

{{"500.help" | translate}}

+
+ +
\ No newline at end of file diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.scss b/src/app/page-internal-server-error/page-internal-server-error.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.ts b/src/app/page-internal-server-error/page-internal-server-error.component.ts new file mode 100644 index 0000000000..2c4e37d30f --- /dev/null +++ b/src/app/page-internal-server-error/page-internal-server-error.component.ts @@ -0,0 +1,33 @@ +import { ServerResponseService } from '../core/services/server-response.service'; +import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; +import { AuthService } from '../core/auth/auth.service'; + +/** + * This component representing the `PageNotFound` DSpace page. + */ +@Component({ + selector: 'ds-page-internal-server-error', + styleUrls: ['./page-internal-server-error.component.scss'], + templateUrl: './page-internal-server-error.component.html', + changeDetection: ChangeDetectionStrategy.Default +}) +export class PageInternalServerErrorComponent implements OnInit { + + /** + * Initialize instance variables + * + * @param {AuthService} authservice + * @param {ServerResponseService} responseService + */ + constructor(private authservice: AuthService, private responseService: ServerResponseService) { + this.responseService.setNotFound(); + } + + /** + * Remove redirect url from the state + */ + ngOnInit(): void { + this.authservice.clearRedirectUrl(); + } + +} diff --git a/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts b/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts new file mode 100644 index 0000000000..e8792c4789 --- /dev/null +++ b/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { ThemedComponent } from '../shared/theme-support/themed.component'; +import { PageInternalServerErrorComponent } from './page-internal-server-error.component'; + +/** + * Themed wrapper for PageInternalServerErrorComponent + */ +@Component({ + selector: 'ds-themed-search-page', + styleUrls: [], + templateUrl: '../shared/theme-support/themed.component.html', +}) +export class ThemedPageInternalServerErrorComponent extends ThemedComponent { + + protected getComponentName(): string { + return 'PageInternalServerErrorComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../themes/${themeName}/app/page-internal-server-error/page-internal-server-error.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./page-internal-server-error.component`); + } +} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 4b06895664..61c5ec77cc 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -14,6 +14,11 @@ "403.forbidden": "forbidden", + "500.page-internal-server-error": "Internal Server Error", + + "500.help": "There was an error with the server.", + + "500.link.home-page": "Take me to the 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. ", From 04548237fd08fc1a856af86e0b59988662cd481f Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Thu, 4 Nov 2021 14:50:26 +0100 Subject: [PATCH 002/102] [DSC-287] Fixes UI and changed the texts in i18n --- .../page-internal-server-error.component.html | 3 --- src/assets/i18n/en.json5 | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.html b/src/app/page-internal-server-error/page-internal-server-error.component.html index ab4f3788f3..8e30236c4f 100644 --- a/src/app/page-internal-server-error/page-internal-server-error.component.html +++ b/src/app/page-internal-server-error/page-internal-server-error.component.html @@ -4,7 +4,4 @@

{{"500.help" | translate}}


- \ No newline at end of file diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 61c5ec77cc..6eca2c1f5d 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -14,9 +14,9 @@ "403.forbidden": "forbidden", - "500.page-internal-server-error": "Internal Server Error", + "500.page-internal-server-error": "Service Unavailable", - "500.help": "There was an error with the server.", + "500.help": "The server is temporarily unable to service yourrequest due to maintenance downtime or capacityproblems. Please try again later.", "500.link.home-page": "Take me to the home page", From 2151d1af58d7f1acaaefa95d6127d2d2dd0fcdd8 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 15 Nov 2021 18:49:13 +0100 Subject: [PATCH 003/102] [DSC-287] Fix error message --- src/assets/i18n/en.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index ebdb2ef4bc..fede29a047 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -16,7 +16,7 @@ "500.page-internal-server-error": "Service Unavailable", - "500.help": "The server is temporarily unable to service yourrequest due to maintenance downtime or capacityproblems. Please try again later.", + "500.help": "The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.", "500.link.home-page": "Take me to the home page", From 25a51c97644a81cef0504faf11859074711a477b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 15 Nov 2021 18:52:33 +0100 Subject: [PATCH 004/102] [DSC-287] Add test to guard --- .../server-check/server-check.guard.spec.ts | 47 +++++++++++++++++-- .../core/server-check/server-check.guard.ts | 20 ++++---- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/app/core/server-check/server-check.guard.spec.ts b/src/app/core/server-check/server-check.guard.spec.ts index fc072cd73c..976021dcdb 100644 --- a/src/app/core/server-check/server-check.guard.spec.ts +++ b/src/app/core/server-check/server-check.guard.spec.ts @@ -1,16 +1,55 @@ -import { TestBed } from '@angular/core/testing'; - import { ServerCheckGuard } from './server-check.guard'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { first } from 'rxjs/operators'; +import { getPageInternalServerErrorRoute } from '../../app-routing-paths'; describe('ServerCheckGuard', () => { let guard: ServerCheckGuard; + let router; + let rootDataServiceStub: any; + + rootDataServiceStub = jasmine.createSpyObj('RootDataService', { + findRoot: jasmine.createSpy('findRoot') + }); + router = jasmine.createSpyObj('Router', { + navigateByUrl: jasmine.createSpy('navigateByUrl') + }); beforeEach(() => { - TestBed.configureTestingModule({}); - guard = TestBed.inject(ServerCheckGuard); + guard = new ServerCheckGuard(router, rootDataServiceStub); }); it('should be created', () => { expect(guard).toBeTruthy(); }); + + describe('when root endpoint has succeeded', () => { + beforeEach(() => { + rootDataServiceStub.findRoot.and.returnValue(createSuccessfulRemoteDataObject$({})); + }); + + it('should not redirect to error page', () => { + guard.canActivate({} as any, {} as any).pipe( + first() + ).subscribe((canActivate: boolean) => { + expect(canActivate).toEqual(true); + expect(router.navigateByUrl).not.toHaveBeenCalled(); + }); + }); + }); + + describe('when root endpoint has not succeeded', () => { + beforeEach(() => { + rootDataServiceStub.findRoot.and.returnValue(createFailedRemoteDataObject$()); + }); + + it('should redirect to error page', () => { + guard.canActivate({} as any, {} as any).pipe( + first() + ).subscribe((canActivate: boolean) => { + expect(canActivate).toEqual(false); + expect(router.navigateByUrl).toHaveBeenCalledWith(getPageInternalServerErrorRoute()); + }); + }); + }); }); diff --git a/src/app/core/server-check/server-check.guard.ts b/src/app/core/server-check/server-check.guard.ts index 32ff57ebb3..478a710567 100644 --- a/src/app/core/server-check/server-check.guard.ts +++ b/src/app/core/server-check/server-check.guard.ts @@ -1,26 +1,30 @@ import { Injectable } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; + import { Observable } from 'rxjs'; -import { RootDataService } from '../data/root-data.service'; import { map, tap } from 'rxjs/operators'; + +import { RootDataService } from '../data/root-data.service'; import { RemoteData } from '../data/remote-data'; import { getPageInternalServerErrorRoute } from '../../app-routing-paths'; -import { Router } from '@angular/router'; +import { getFirstCompletedRemoteData } from '../shared/operators'; @Injectable({ providedIn: 'root' }) export class ServerCheckGuard implements CanActivate { - constructor(private router: Router,private rootDataService: RootDataService) {} + constructor(private router: Router, private rootDataService: RootDataService) { + } canActivate( route: ActivatedRouteSnapshot, - state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + state: RouterStateSnapshot): Observable { return this.rootDataService.findRoot().pipe( - map( (res: RemoteData) => res.hasSucceeded ), - tap( (responsehasSucceeded: boolean) => { - if (!responsehasSucceeded) { + getFirstCompletedRemoteData(), + map((res: RemoteData) => res.hasSucceeded), + tap((hasSucceeded: boolean) => { + if (!hasSucceeded) { this.router.navigateByUrl(getPageInternalServerErrorRoute()); } }), From 81c4403ee6c6a37ab5ce99974e997446aca4edca Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 15 Nov 2021 19:02:10 +0100 Subject: [PATCH 005/102] [DSC-287] reformat app-routing.module code --- src/app/app-routing.module.ts | 363 +++++++++++++++++----------------- 1 file changed, 185 insertions(+), 178 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 7941adc35c..fcf9cd034a 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -11,10 +11,11 @@ import { FORBIDDEN_PATH, FORGOT_PASSWORD_PATH, INFO_MODULE_PATH, + LEGACY_BITSTREAM_MODULE_PATH, PROFILE_MODULE_PATH, REGISTER_PATH, + REQUEST_COPY_MODULE_PATH, WORKFLOW_ITEM_MODULE_PATH, - LEGACY_BITSTREAM_MODULE_PATH, REQUEST_COPY_MODULE_PATH, } from './app-routing-paths'; import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routing-paths'; import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths'; @@ -29,187 +30,193 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component'; import { ServerCheckGuard } from './core/server-check/server-check.guard'; - @NgModule({ imports: [ RouterModule.forRoot([{ path: '', canActivate: [AuthBlockingGuard], - children: [ - { path: '', redirectTo: '/home', pathMatch: 'full' }, - { path: 'reload/:rnd', component: ThemedPageNotFoundComponent, pathMatch: 'full', canActivate: [ServerCheckGuard, ReloadGuard] }, - { - path: 'home', - loadChildren: () => import('./home-page/home-page.module') - .then((m) => m.HomePageModule), - data: { showBreadcrumbs: false }, - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'community-list', - loadChildren: () => import('./community-list-page/community-list-page.module') - .then((m) => m.CommunityListPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'id', - loadChildren: () => import('./lookup-by-id/lookup-by-id.module') - .then((m) => m.LookupIdModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'handle', - loadChildren: () => import('./lookup-by-id/lookup-by-id.module') - .then((m) => m.LookupIdModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: REGISTER_PATH, - loadChildren: () => import('./register-page/register-page.module') - .then((m) => m.RegisterPageModule), - canActivate: [ServerCheckGuard, SiteRegisterGuard] - }, - { - path: FORGOT_PASSWORD_PATH, - loadChildren: () => import('./forgot-password/forgot-password.module') - .then((m) => m.ForgotPasswordModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: COMMUNITY_MODULE_PATH, - loadChildren: () => import('./community-page/community-page.module') - .then((m) => m.CommunityPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: COLLECTION_MODULE_PATH, - loadChildren: () => import('./collection-page/collection-page.module') - .then((m) => m.CollectionPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: ITEM_MODULE_PATH, - loadChildren: () => import('./item-page/item-page.module') - .then((m) => m.ItemPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { path: 'entities/:entity-type', - loadChildren: () => import('./item-page/item-page.module') - .then((m) => m.ItemPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: LEGACY_BITSTREAM_MODULE_PATH, - loadChildren: () => import('./bitstream-page/bitstream-page.module') - .then((m) => m.BitstreamPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: BITSTREAM_MODULE_PATH, - loadChildren: () => import('./bitstream-page/bitstream-page.module') - .then((m) => m.BitstreamPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'mydspace', - loadChildren: () => import('./my-dspace-page/my-dspace-page.module') - .then((m) => m.MyDSpacePageModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'search', - loadChildren: () => import('./search-page/search-page-routing.module') - .then((m) => m.SearchPageRoutingModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'browse', - loadChildren: () => import('./browse-by/browse-by-page.module') - .then((m) => m.BrowseByPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: ADMIN_MODULE_PATH, - loadChildren: () => import('./admin/admin.module') - .then((m) => m.AdminModule), - canActivate: [ServerCheckGuard, SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'login', - loadChildren: () => import('./login-page/login-page.module') - .then((m) => m.LoginPageModule), - canActivate: [ServerCheckGuard] - }, - { - path: 'logout', - loadChildren: () => import('./logout-page/logout-page.module') - .then((m) => m.LogoutPageModule), - canActivate: [ServerCheckGuard] - }, - { - path: 'submit', - loadChildren: () => import('./submit-page/submit-page.module') - .then((m) => m.SubmitPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'import-external', - loadChildren: () => import('./import-external-page/import-external-page.module') - .then((m) => m.ImportExternalPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'workspaceitems', - loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page.module') - .then((m) => m.WorkspaceitemsEditPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: WORKFLOW_ITEM_MODULE_PATH, - loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page.module') - .then((m) => m.WorkflowItemsEditPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: PROFILE_MODULE_PATH, - loadChildren: () => import('./profile-page/profile-page.module') - .then((m) => m.ProfilePageModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: PROCESS_MODULE_PATH, - loadChildren: () => import('./process-page/process-page.module') - .then((m) => m.ProcessPageModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: INFO_MODULE_PATH, - loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), - canActivate: [ServerCheckGuard] - }, - { - path: REQUEST_COPY_MODULE_PATH, - loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: FORBIDDEN_PATH, - component: ThemedForbiddenComponent - }, - { - path: 'statistics', - loadChildren: () => import('./statistics-page/statistics-page-routing.module') - .then((m) => m.StatisticsPageRoutingModule), - canActivate: [ServerCheckGuard] - }, - { - path: ACCESS_CONTROL_MODULE_PATH, - loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), - canActivate: [ServerCheckGuard, GroupAdministratorGuard], - }, - { path: '500', component: ThemedPageInternalServerErrorComponent }, - { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, - ]} - ],{ + children: [ + { path: '', redirectTo: '/home', pathMatch: 'full' }, + { + path: 'reload/:rnd', + component: ThemedPageNotFoundComponent, + pathMatch: 'full', + canActivate: [ServerCheckGuard, ReloadGuard] + }, + { + path: 'home', + loadChildren: () => import('./home-page/home-page.module') + .then((m) => m.HomePageModule), + data: { showBreadcrumbs: false }, + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'community-list', + loadChildren: () => import('./community-list-page/community-list-page.module') + .then((m) => m.CommunityListPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'id', + loadChildren: () => import('./lookup-by-id/lookup-by-id.module') + .then((m) => m.LookupIdModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'handle', + loadChildren: () => import('./lookup-by-id/lookup-by-id.module') + .then((m) => m.LookupIdModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: REGISTER_PATH, + loadChildren: () => import('./register-page/register-page.module') + .then((m) => m.RegisterPageModule), + canActivate: [ServerCheckGuard, SiteRegisterGuard] + }, + { + path: FORGOT_PASSWORD_PATH, + loadChildren: () => import('./forgot-password/forgot-password.module') + .then((m) => m.ForgotPasswordModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: COMMUNITY_MODULE_PATH, + loadChildren: () => import('./community-page/community-page.module') + .then((m) => m.CommunityPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: COLLECTION_MODULE_PATH, + loadChildren: () => import('./collection-page/collection-page.module') + .then((m) => m.CollectionPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: ITEM_MODULE_PATH, + loadChildren: () => import('./item-page/item-page.module') + .then((m) => m.ItemPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'entities/:entity-type', + loadChildren: () => import('./item-page/item-page.module') + .then((m) => m.ItemPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: LEGACY_BITSTREAM_MODULE_PATH, + loadChildren: () => import('./bitstream-page/bitstream-page.module') + .then((m) => m.BitstreamPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: BITSTREAM_MODULE_PATH, + loadChildren: () => import('./bitstream-page/bitstream-page.module') + .then((m) => m.BitstreamPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'mydspace', + loadChildren: () => import('./my-dspace-page/my-dspace-page.module') + .then((m) => m.MyDSpacePageModule), + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'search', + loadChildren: () => import('./search-page/search-page-routing.module') + .then((m) => m.SearchPageRoutingModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'browse', + loadChildren: () => import('./browse-by/browse-by-page.module') + .then((m) => m.BrowseByPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: ADMIN_MODULE_PATH, + loadChildren: () => import('./admin/admin.module') + .then((m) => m.AdminModule), + canActivate: [ServerCheckGuard, SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'login', + loadChildren: () => import('./login-page/login-page.module') + .then((m) => m.LoginPageModule), + canActivate: [ServerCheckGuard] + }, + { + path: 'logout', + loadChildren: () => import('./logout-page/logout-page.module') + .then((m) => m.LogoutPageModule), + canActivate: [ServerCheckGuard] + }, + { + path: 'submit', + loadChildren: () => import('./submit-page/submit-page.module') + .then((m) => m.SubmitPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'import-external', + loadChildren: () => import('./import-external-page/import-external-page.module') + .then((m) => m.ImportExternalPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'workspaceitems', + loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page.module') + .then((m) => m.WorkspaceitemsEditPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: WORKFLOW_ITEM_MODULE_PATH, + loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page.module') + .then((m) => m.WorkflowItemsEditPageModule), + canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: PROFILE_MODULE_PATH, + loadChildren: () => import('./profile-page/profile-page.module') + .then((m) => m.ProfilePageModule), + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: PROCESS_MODULE_PATH, + loadChildren: () => import('./process-page/process-page.module') + .then((m) => m.ProcessPageModule), + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: INFO_MODULE_PATH, + loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), + canActivate: [ServerCheckGuard] + }, + { + path: REQUEST_COPY_MODULE_PATH, + loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), + canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: FORBIDDEN_PATH, + component: ThemedForbiddenComponent + }, + { + path: 'statistics', + loadChildren: () => import('./statistics-page/statistics-page-routing.module') + .then((m) => m.StatisticsPageRoutingModule), + canActivate: [ServerCheckGuard] + }, + { + path: ACCESS_CONTROL_MODULE_PATH, + loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), + canActivate: [ServerCheckGuard, GroupAdministratorGuard], + }, + { path: '500', component: ThemedPageInternalServerErrorComponent }, + { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, + ] + } + ], { onSameUrlNavigation: 'reload', }) ], From 76ddce723990984fcb5fef3d6647c4a4338ba254 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 16 Nov 2021 17:38:20 +0100 Subject: [PATCH 006/102] [DSC-287] fix test --- .../server-check/server-check.guard.spec.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/app/core/server-check/server-check.guard.spec.ts b/src/app/core/server-check/server-check.guard.spec.ts index 976021dcdb..a9c03d1cd7 100644 --- a/src/app/core/server-check/server-check.guard.spec.ts +++ b/src/app/core/server-check/server-check.guard.spec.ts @@ -1,12 +1,16 @@ import { ServerCheckGuard } from './server-check.guard'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { first } from 'rxjs/operators'; +import { take } from 'rxjs/operators'; import { getPageInternalServerErrorRoute } from '../../app-routing-paths'; +import { Router } from '@angular/router'; +import { RootDataService } from '../data/root-data.service'; +import { Root } from '../data/root.model'; +import SpyObj = jasmine.SpyObj; describe('ServerCheckGuard', () => { let guard: ServerCheckGuard; - let router; - let rootDataServiceStub: any; + let router: SpyObj; + let rootDataServiceStub: SpyObj; rootDataServiceStub = jasmine.createSpyObj('RootDataService', { findRoot: jasmine.createSpy('findRoot') @@ -19,18 +23,22 @@ describe('ServerCheckGuard', () => { guard = new ServerCheckGuard(router, rootDataServiceStub); }); + afterEach(() => { + router.navigateByUrl.calls.reset(); + }); + it('should be created', () => { expect(guard).toBeTruthy(); }); describe('when root endpoint has succeeded', () => { beforeEach(() => { - rootDataServiceStub.findRoot.and.returnValue(createSuccessfulRemoteDataObject$({})); + rootDataServiceStub.findRoot.and.returnValue(createSuccessfulRemoteDataObject$({} as any)); }); it('should not redirect to error page', () => { guard.canActivate({} as any, {} as any).pipe( - first() + take(1) ).subscribe((canActivate: boolean) => { expect(canActivate).toEqual(true); expect(router.navigateByUrl).not.toHaveBeenCalled(); @@ -40,12 +48,12 @@ describe('ServerCheckGuard', () => { describe('when root endpoint has not succeeded', () => { beforeEach(() => { - rootDataServiceStub.findRoot.and.returnValue(createFailedRemoteDataObject$()); + rootDataServiceStub.findRoot.and.returnValue(createFailedRemoteDataObject$()); }); it('should redirect to error page', () => { guard.canActivate({} as any, {} as any).pipe( - first() + take(1) ).subscribe((canActivate: boolean) => { expect(canActivate).toEqual(false); expect(router.navigateByUrl).toHaveBeenCalledWith(getPageInternalServerErrorRoute()); From a323aefc2279a5c1184c07cbe0cd1180eeadf809 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 16 Nov 2021 19:13:57 +0100 Subject: [PATCH 007/102] [DSC-287] change app-routing.module.ts inn order to have ServerCheckGuard configured once --- src/app/app-routing.module.ts | 370 +++++++++++++++++----------------- 1 file changed, 186 insertions(+), 184 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index fcf9cd034a..63fa669206 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -32,190 +32,192 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; @NgModule({ imports: [ - RouterModule.forRoot([{ - path: '', canActivate: [AuthBlockingGuard], - children: [ - { path: '', redirectTo: '/home', pathMatch: 'full' }, - { - path: 'reload/:rnd', - component: ThemedPageNotFoundComponent, - pathMatch: 'full', - canActivate: [ServerCheckGuard, ReloadGuard] - }, - { - path: 'home', - loadChildren: () => import('./home-page/home-page.module') - .then((m) => m.HomePageModule), - data: { showBreadcrumbs: false }, - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'community-list', - loadChildren: () => import('./community-list-page/community-list-page.module') - .then((m) => m.CommunityListPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'id', - loadChildren: () => import('./lookup-by-id/lookup-by-id.module') - .then((m) => m.LookupIdModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'handle', - loadChildren: () => import('./lookup-by-id/lookup-by-id.module') - .then((m) => m.LookupIdModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: REGISTER_PATH, - loadChildren: () => import('./register-page/register-page.module') - .then((m) => m.RegisterPageModule), - canActivate: [ServerCheckGuard, SiteRegisterGuard] - }, - { - path: FORGOT_PASSWORD_PATH, - loadChildren: () => import('./forgot-password/forgot-password.module') - .then((m) => m.ForgotPasswordModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: COMMUNITY_MODULE_PATH, - loadChildren: () => import('./community-page/community-page.module') - .then((m) => m.CommunityPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: COLLECTION_MODULE_PATH, - loadChildren: () => import('./collection-page/collection-page.module') - .then((m) => m.CollectionPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: ITEM_MODULE_PATH, - loadChildren: () => import('./item-page/item-page.module') - .then((m) => m.ItemPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'entities/:entity-type', - loadChildren: () => import('./item-page/item-page.module') - .then((m) => m.ItemPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: LEGACY_BITSTREAM_MODULE_PATH, - loadChildren: () => import('./bitstream-page/bitstream-page.module') - .then((m) => m.BitstreamPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: BITSTREAM_MODULE_PATH, - loadChildren: () => import('./bitstream-page/bitstream-page.module') - .then((m) => m.BitstreamPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'mydspace', - loadChildren: () => import('./my-dspace-page/my-dspace-page.module') - .then((m) => m.MyDSpacePageModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'search', - loadChildren: () => import('./search-page/search-page-routing.module') - .then((m) => m.SearchPageRoutingModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'browse', - loadChildren: () => import('./browse-by/browse-by-page.module') - .then((m) => m.BrowseByPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: ADMIN_MODULE_PATH, - loadChildren: () => import('./admin/admin.module') - .then((m) => m.AdminModule), - canActivate: [ServerCheckGuard, SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'login', - loadChildren: () => import('./login-page/login-page.module') - .then((m) => m.LoginPageModule), - canActivate: [ServerCheckGuard] - }, - { - path: 'logout', - loadChildren: () => import('./logout-page/logout-page.module') - .then((m) => m.LogoutPageModule), - canActivate: [ServerCheckGuard] - }, - { - path: 'submit', - loadChildren: () => import('./submit-page/submit-page.module') - .then((m) => m.SubmitPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'import-external', - loadChildren: () => import('./import-external-page/import-external-page.module') - .then((m) => m.ImportExternalPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: 'workspaceitems', - loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page.module') - .then((m) => m.WorkspaceitemsEditPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: WORKFLOW_ITEM_MODULE_PATH, - loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page.module') - .then((m) => m.WorkflowItemsEditPageModule), - canActivate: [ServerCheckGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: PROFILE_MODULE_PATH, - loadChildren: () => import('./profile-page/profile-page.module') - .then((m) => m.ProfilePageModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: PROCESS_MODULE_PATH, - loadChildren: () => import('./process-page/process-page.module') - .then((m) => m.ProcessPageModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: INFO_MODULE_PATH, - loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), - canActivate: [ServerCheckGuard] - }, - { - path: REQUEST_COPY_MODULE_PATH, - loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), - canActivate: [ServerCheckGuard, AuthenticatedGuard, EndUserAgreementCurrentUserGuard] - }, - { - path: FORBIDDEN_PATH, - component: ThemedForbiddenComponent - }, - { - path: 'statistics', - loadChildren: () => import('./statistics-page/statistics-page-routing.module') - .then((m) => m.StatisticsPageRoutingModule), - canActivate: [ServerCheckGuard] - }, - { - path: ACCESS_CONTROL_MODULE_PATH, - loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), - canActivate: [ServerCheckGuard, GroupAdministratorGuard], - }, - { path: '500', component: ThemedPageInternalServerErrorComponent }, - { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, - ] - } + RouterModule.forRoot([ + { path: '500', component: ThemedPageInternalServerErrorComponent }, + { + path: '', canActivate: [ServerCheckGuard, AuthBlockingGuard], + children: [ + { path: '', redirectTo: '/home', pathMatch: 'full' }, + { + path: 'reload/:rnd', + component: ThemedPageNotFoundComponent, + pathMatch: 'full', + canActivate: [ReloadGuard] + }, + { + path: 'home', + loadChildren: () => import('./home-page/home-page.module') + .then((m) => m.HomePageModule), + data: { showBreadcrumbs: false }, + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'community-list', + loadChildren: () => import('./community-list-page/community-list-page.module') + .then((m) => m.CommunityListPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'id', + loadChildren: () => import('./lookup-by-id/lookup-by-id.module') + .then((m) => m.LookupIdModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'handle', + loadChildren: () => import('./lookup-by-id/lookup-by-id.module') + .then((m) => m.LookupIdModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: REGISTER_PATH, + loadChildren: () => import('./register-page/register-page.module') + .then((m) => m.RegisterPageModule), + canActivate: [SiteRegisterGuard] + }, + { + path: FORGOT_PASSWORD_PATH, + loadChildren: () => import('./forgot-password/forgot-password.module') + .then((m) => m.ForgotPasswordModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: COMMUNITY_MODULE_PATH, + loadChildren: () => import('./community-page/community-page.module') + .then((m) => m.CommunityPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: COLLECTION_MODULE_PATH, + loadChildren: () => import('./collection-page/collection-page.module') + .then((m) => m.CollectionPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: ITEM_MODULE_PATH, + loadChildren: () => import('./item-page/item-page.module') + .then((m) => m.ItemPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'entities/:entity-type', + loadChildren: () => import('./item-page/item-page.module') + .then((m) => m.ItemPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: LEGACY_BITSTREAM_MODULE_PATH, + loadChildren: () => import('./bitstream-page/bitstream-page.module') + .then((m) => m.BitstreamPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: BITSTREAM_MODULE_PATH, + loadChildren: () => import('./bitstream-page/bitstream-page.module') + .then((m) => m.BitstreamPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'mydspace', + loadChildren: () => import('./my-dspace-page/my-dspace-page.module') + .then((m) => m.MyDSpacePageModule), + canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'search', + loadChildren: () => import('./search-page/search-page-routing.module') + .then((m) => m.SearchPageRoutingModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'browse', + loadChildren: () => import('./browse-by/browse-by-page.module') + .then((m) => m.BrowseByPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: ADMIN_MODULE_PATH, + loadChildren: () => import('./admin/admin.module') + .then((m) => m.AdminModule), + canActivate: [SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: 'login', + loadChildren: () => import('./login-page/login-page.module') + .then((m) => m.LoginPageModule), + canActivate: [ServerCheckGuard] + }, + { + path: 'logout', + loadChildren: () => import('./logout-page/logout-page.module') + .then((m) => m.LogoutPageModule), + canActivate: [ServerCheckGuard] + }, + { + path: 'submit', + loadChildren: () => import('./submit-page/submit-page.module') + .then((m) => m.SubmitPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'import-external', + loadChildren: () => import('./import-external-page/import-external-page.module') + .then((m) => m.ImportExternalPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: 'workspaceitems', + loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page.module') + .then((m) => m.WorkspaceitemsEditPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: WORKFLOW_ITEM_MODULE_PATH, + loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page.module') + .then((m) => m.WorkflowItemsEditPageModule), + canActivate: [EndUserAgreementCurrentUserGuard] + }, + { + path: PROFILE_MODULE_PATH, + loadChildren: () => import('./profile-page/profile-page.module') + .then((m) => m.ProfilePageModule), + canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: PROCESS_MODULE_PATH, + loadChildren: () => import('./process-page/process-page.module') + .then((m) => m.ProcessPageModule), + canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: INFO_MODULE_PATH, + loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), + canActivate: [ServerCheckGuard] + }, + { + path: REQUEST_COPY_MODULE_PATH, + loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), + canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, + { + path: FORBIDDEN_PATH, + component: ThemedForbiddenComponent + }, + { + path: 'statistics', + loadChildren: () => import('./statistics-page/statistics-page-routing.module') + .then((m) => m.StatisticsPageRoutingModule), + canActivate: [ServerCheckGuard] + }, + { + path: ACCESS_CONTROL_MODULE_PATH, + loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), + canActivate: [GroupAdministratorGuard], + }, + + { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, + ] + } ], { onSameUrlNavigation: 'reload', }) From 92e9f79f09d4e6551abcea79c0fc4dc492f33213 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 17 Nov 2021 09:37:49 +0100 Subject: [PATCH 008/102] [DSC-287] optimize code and add typedoc --- src/app/app-routing.module.ts | 16 ++++++---------- src/app/core/server-check/server-check.guard.ts | 7 +++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 63fa669206..49e96a028a 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -11,6 +11,7 @@ import { FORBIDDEN_PATH, FORGOT_PASSWORD_PATH, INFO_MODULE_PATH, + INTERNAL_SERVER_ERROR, LEGACY_BITSTREAM_MODULE_PATH, PROFILE_MODULE_PATH, REGISTER_PATH, @@ -33,7 +34,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; @NgModule({ imports: [ RouterModule.forRoot([ - { path: '500', component: ThemedPageInternalServerErrorComponent }, + { path: INTERNAL_SERVER_ERROR, component: ThemedPageInternalServerErrorComponent }, { path: '', canActivate: [ServerCheckGuard, AuthBlockingGuard], children: [ @@ -144,14 +145,12 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; { path: 'login', loadChildren: () => import('./login-page/login-page.module') - .then((m) => m.LoginPageModule), - canActivate: [ServerCheckGuard] + .then((m) => m.LoginPageModule) }, { path: 'logout', loadChildren: () => import('./logout-page/logout-page.module') - .then((m) => m.LogoutPageModule), - canActivate: [ServerCheckGuard] + .then((m) => m.LogoutPageModule) }, { path: 'submit', @@ -191,8 +190,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; }, { path: INFO_MODULE_PATH, - loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), - canActivate: [ServerCheckGuard] + loadChildren: () => import('./info/info.module').then((m) => m.InfoModule) }, { path: REQUEST_COPY_MODULE_PATH, @@ -206,15 +204,13 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; { path: 'statistics', loadChildren: () => import('./statistics-page/statistics-page-routing.module') - .then((m) => m.StatisticsPageRoutingModule), - canActivate: [ServerCheckGuard] + .then((m) => m.StatisticsPageRoutingModule) }, { path: ACCESS_CONTROL_MODULE_PATH, loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), canActivate: [GroupAdministratorGuard], }, - { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, ] } diff --git a/src/app/core/server-check/server-check.guard.ts b/src/app/core/server-check/server-check.guard.ts index 478a710567..b7982eee9a 100644 --- a/src/app/core/server-check/server-check.guard.ts +++ b/src/app/core/server-check/server-check.guard.ts @@ -12,10 +12,17 @@ import { getFirstCompletedRemoteData } from '../shared/operators'; @Injectable({ providedIn: 'root' }) +/** + * A guard that checks if root api endpoint is reachable. + * If not redirect to 500 error page + */ export class ServerCheckGuard implements CanActivate { constructor(private router: Router, private rootDataService: RootDataService) { } + /** + * True when root api endpoint is reachable. + */ canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { From 5e17a4e958db858bc7eee8c4806ee51ce46ff00b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 30 Nov 2021 13:27:16 +0100 Subject: [PATCH 009/102] 85262: Always enable deposit button --- .../submission-form-footer.component.html | 1 - .../submission-form-footer.component.spec.ts | 9 ---- .../submission-objects.effects.spec.ts | 53 ------------------- .../objects/submission-objects.effects.ts | 4 +- 4 files changed, 1 insertion(+), 66 deletions(-) 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 4964eb56a2..203f7e2afa 100644 --- a/src/app/submission/form/footer/submission-form-footer.component.html +++ b/src/app/submission/form/footer/submission-form-footer.component.html @@ -41,7 +41,6 @@ diff --git a/src/app/submission/form/footer/submission-form-footer.component.spec.ts b/src/app/submission/form/footer/submission-form-footer.component.spec.ts index dd47dad444..dbeb4ee00d 100644 --- a/src/app/submission/form/footer/submission-form-footer.component.spec.ts +++ b/src/app/submission/form/footer/submission-form-footer.component.spec.ts @@ -201,15 +201,6 @@ describe('SubmissionFormFooterComponent Component', () => { }); }); - it('should have deposit button disabled when submission is not valid', () => { - comp.showDepositAndDiscard = observableOf(true); - compAsAny.submissionIsInvalid = observableOf(true); - fixture.detectChanges(); - const depositBtn: any = fixture.debugElement.query(By.css('.btn-success')); - - expect(depositBtn.nativeElement.disabled).toBeTruthy(); - }); - it('should not have deposit button disabled when submission is valid', () => { comp.showDepositAndDiscard = observableOf(true); compAsAny.submissionIsInvalid = observableOf(false); diff --git a/src/app/submission/objects/submission-objects.effects.spec.ts b/src/app/submission/objects/submission-objects.effects.spec.ts index 122ebd90ac..8e763fd94b 100644 --- a/src/app/submission/objects/submission-objects.effects.spec.ts +++ b/src/app/submission/objects/submission-objects.effects.spec.ts @@ -879,59 +879,6 @@ describe('SubmissionObjectEffects test suite', () => { expect(submissionObjectEffects.saveAndDeposit$).toBeObservable(expected); }); - it('should not allow to deposit when there are errors', () => { - store.nextState({ - submission: { - objects: submissionState - } - } as any); - - actions = hot('--a-', { - a: { - type: SubmissionObjectActionTypes.SAVE_AND_DEPOSIT_SUBMISSION, - payload: { - submissionId: submissionId - } - } - }); - - const response = [Object.assign({}, mockSubmissionRestResponse[0], { - sections: mockSectionsData, - errors: mockSectionsErrors - })]; - - submissionJsonPatchOperationsServiceStub.jsonPatchByResourceType.and.returnValue(observableOf(response)); - - const errorsList = parseSectionErrors(mockSectionsErrors); - const expected = cold('--b-', { - b: [ - new UpdateSectionDataAction( - submissionId, - 'traditionalpageone', - mockSectionsData.traditionalpageone as any, - errorsList.traditionalpageone || [], - errorsList.traditionalpageone || [] - ), - new UpdateSectionDataAction( - submissionId, - 'license', - mockSectionsData.license as any, - errorsList.license || [], - errorsList.license || [] - ), - new UpdateSectionDataAction( - submissionId, - 'upload', - mockSectionsData.upload as any, - errorsList.upload || [], - errorsList.upload || [] - ) - ] - }); - - expect(submissionObjectEffects.saveAndDeposit$).toBeObservable(expected); - }); - it('should catch errors and return a SAVE_SUBMISSION_FORM_ERROR', () => { actions = hot('--a-', { a: { diff --git a/src/app/submission/objects/submission-objects.effects.ts b/src/app/submission/objects/submission-objects.effects.ts index b4ba1c2480..535e662922 100644 --- a/src/app/submission/objects/submission-objects.effects.ts +++ b/src/app/submission/objects/submission-objects.effects.ts @@ -206,9 +206,7 @@ export class SubmissionObjectEffects { if (this.canDeposit(response)) { return new DepositSubmissionAction(action.payload.submissionId); } else { - this.notificationsService.warning(null, this.translate.get('submission.sections.general.sections_not_valid')); - return this.parseSaveResponse((currentState.submission as SubmissionState).objects[action.payload.submissionId], - response, action.payload.submissionId, currentState.forms); + return new SaveSubmissionFormSuccessAction(action.payload.submissionId, response); } }), catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId)))); From 99af22b6216cfd67fffffc0ab5f7f7883584ab1b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 30 Nov 2021 13:44:08 +0100 Subject: [PATCH 010/102] 85262: Re-disable deposit button during save/deposit --- .../submission/form/footer/submission-form-footer.component.html | 1 + 1 file changed, 1 insertion(+) 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 203f7e2afa..e954fab34c 100644 --- a/src/app/submission/form/footer/submission-form-footer.component.html +++ b/src/app/submission/form/footer/submission-form-footer.component.html @@ -41,6 +41,7 @@ From c8de6ccb4c42dfbb08cbc5aed6398a2f61168449 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Wed, 1 Dec 2021 18:50:42 +0100 Subject: [PATCH 011/102] [DSC-287] Fixed for yarn start error 500 loading --- .../page-internal-server-error.component.ts | 21 +------------------ src/app/root/root.component.ts | 4 ++++ 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.ts b/src/app/page-internal-server-error/page-internal-server-error.component.ts index 2c4e37d30f..b2dc70dbdf 100644 --- a/src/app/page-internal-server-error/page-internal-server-error.component.ts +++ b/src/app/page-internal-server-error/page-internal-server-error.component.ts @@ -1,6 +1,5 @@ import { ServerResponseService } from '../core/services/server-response.service'; import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; -import { AuthService } from '../core/auth/auth.service'; /** * This component representing the `PageNotFound` DSpace page. @@ -11,23 +10,5 @@ import { AuthService } from '../core/auth/auth.service'; templateUrl: './page-internal-server-error.component.html', changeDetection: ChangeDetectionStrategy.Default }) -export class PageInternalServerErrorComponent implements OnInit { - - /** - * Initialize instance variables - * - * @param {AuthService} authservice - * @param {ServerResponseService} responseService - */ - constructor(private authservice: AuthService, private responseService: ServerResponseService) { - this.responseService.setNotFound(); - } - - /** - * Remove redirect url from the state - */ - ngOnInit(): void { - this.authservice.clearRedirectUrl(); - } - +export class PageInternalServerErrorComponent { } diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index 209f17b520..b70573e42b 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -71,5 +71,9 @@ export class RootComponent implements OnInit { .pipe( map(([collapsed, mobile]) => collapsed || mobile) ); + + if (this.router.url === '/500') { + this.shouldShowRouteLoader = false; + } } } From ed2c774d86b71ca09c3e286a448650311bf97aca Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 2 Dec 2021 12:39:07 +0100 Subject: [PATCH 012/102] 85262: Allow empty patch requests --- .../json-patch-operations.service.ts | 16 ++++++---- .../submission-form-footer.component.spec.ts | 9 ++++++ .../submission-objects.effects.spec.ts | 30 +++++++++++++++++++ .../objects/submission-objects.effects.ts | 3 +- 4 files changed, 51 insertions(+), 7 deletions(-) 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..f160376837 100644 --- a/src/app/core/json-patch/json-patch-operations.service.ts +++ b/src/app/core/json-patch/json-patch-operations.service.ts @@ -40,13 +40,15 @@ export abstract class JsonPatchOperationsService * observable of response */ - protected submitJsonPatchOperations(hrefObs: Observable, resourceType: string, resourceId?: string): Observable { + protected submitJsonPatchOperations(hrefObs: Observable, resourceType: string, resourceId?: string, allowEmptyRequest = false): Observable { const requestId = this.requestService.generateRequestId(); let startTransactionTime = null; - const [patchRequest$, emptyRequest$] = partition((request: PatchRequestDefinition) => isNotEmpty(request.body))(hrefObs.pipe( + const [patchRequest$, emptyRequest$] = partition((request: PatchRequestDefinition) => allowEmptyRequest || isNotEmpty(request.body))(hrefObs.pipe( mergeMap((endpointURL: string) => { return this.store.select(jsonPatchOperationsByResourceType(resourceType)).pipe( take(1), @@ -79,11 +81,11 @@ export abstract class JsonPatchOperationsService isEmpty(request.body)), + filter((request: PatchRequestDefinition) => !allowEmptyRequest && isEmpty(request.body)), tap(() => startTransactionTime = null), map(() => null)), patchRequest$.pipe( - filter((request: PatchRequestDefinition) => isNotEmpty(request.body)), + filter((request: PatchRequestDefinition) => allowEmptyRequest || isNotEmpty(request.body)), tap(() => this.store.dispatch(new StartTransactionPatchOperationsAction(resourceType, resourceId, startTransactionTime))), tap((request: PatchRequestDefinition) => this.requestService.send(request)), mergeMap(() => { @@ -141,16 +143,18 @@ export abstract class JsonPatchOperationsService * observable of response */ - public jsonPatchByResourceType(linkPath: string, scopeId: string, resourceType: string): Observable { + public jsonPatchByResourceType(linkPath: string, scopeId: string, resourceType: string, allowEmptyRequest = false): Observable { const href$ = this.halService.getEndpoint(linkPath).pipe( filter((href: string) => isNotEmpty(href)), distinctUntilChanged(), map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId))); - return this.submitJsonPatchOperations(href$, resourceType); + return this.submitJsonPatchOperations(href$, resourceType, undefined, allowEmptyRequest); } /** diff --git a/src/app/submission/form/footer/submission-form-footer.component.spec.ts b/src/app/submission/form/footer/submission-form-footer.component.spec.ts index dbeb4ee00d..072f826deb 100644 --- a/src/app/submission/form/footer/submission-form-footer.component.spec.ts +++ b/src/app/submission/form/footer/submission-form-footer.component.spec.ts @@ -201,6 +201,15 @@ describe('SubmissionFormFooterComponent Component', () => { }); }); + it('should not have deposit button disabled when submission is not valid', () => { + comp.showDepositAndDiscard = observableOf(true); + compAsAny.submissionIsInvalid = observableOf(true); + fixture.detectChanges(); + const depositBtn: any = fixture.debugElement.query(By.css('.btn-success')); + + expect(depositBtn.nativeElement.disabled).toBeFalsy(); + }); + it('should not have deposit button disabled when submission is valid', () => { comp.showDepositAndDiscard = observableOf(true); compAsAny.submissionIsInvalid = observableOf(false); diff --git a/src/app/submission/objects/submission-objects.effects.spec.ts b/src/app/submission/objects/submission-objects.effects.spec.ts index 8e763fd94b..e760f71941 100644 --- a/src/app/submission/objects/submission-objects.effects.spec.ts +++ b/src/app/submission/objects/submission-objects.effects.spec.ts @@ -879,6 +879,36 @@ describe('SubmissionObjectEffects test suite', () => { expect(submissionObjectEffects.saveAndDeposit$).toBeObservable(expected); }); + it('should return a SAVE_SUBMISSION_FORM_SUCCESS action when there are errors', () => { + store.nextState({ + submission: { + objects: submissionState + } + } as any); + + actions = hot('--a-', { + a: { + type: SubmissionObjectActionTypes.SAVE_AND_DEPOSIT_SUBMISSION, + payload: { + submissionId: submissionId + } + } + }); + + const response = [Object.assign({}, mockSubmissionRestResponse[0], { + sections: mockSectionsData, + errors: mockSectionsErrors + })]; + + submissionJsonPatchOperationsServiceStub.jsonPatchByResourceType.and.returnValue(observableOf(response)); + + const expected = cold('--b-', { + b: new SaveSubmissionFormSuccessAction(submissionId, response as any[]) + }); + + expect(submissionObjectEffects.saveAndDeposit$).toBeObservable(expected); + }); + it('should catch errors and return a SAVE_SUBMISSION_FORM_ERROR', () => { actions = hot('--a-', { a: { diff --git a/src/app/submission/objects/submission-objects.effects.ts b/src/app/submission/objects/submission-objects.effects.ts index 535e662922..5432aa7a4c 100644 --- a/src/app/submission/objects/submission-objects.effects.ts +++ b/src/app/submission/objects/submission-objects.effects.ts @@ -201,7 +201,8 @@ export class SubmissionObjectEffects { return this.operationsService.jsonPatchByResourceType( this.submissionService.getSubmissionObjectLinkName(), action.payload.submissionId, - 'sections').pipe( + 'sections', + true).pipe( map((response: SubmissionObject[]) => { if (this.canDeposit(response)) { return new DepositSubmissionAction(action.payload.submissionId); From 768de1a1e73a585dfb50f3f1eb6d09b04dbcde06 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 3 Dec 2021 10:54:41 -0500 Subject: [PATCH 013/102] w2p-85346 Allow facets to be authority controlled and add test to verify --- src/app/shared/search/facet-value.model.ts | 6 ++++++ src/app/shared/search/search.utils.spec.ts | 10 ++++++++++ src/app/shared/search/search.utils.ts | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/src/app/shared/search/facet-value.model.ts b/src/app/shared/search/facet-value.model.ts index 969e531bd2..935190743d 100644 --- a/src/app/shared/search/facet-value.model.ts +++ b/src/app/shared/search/facet-value.model.ts @@ -24,6 +24,12 @@ export class FacetValue implements HALResource { @autoserialize count: number; + /** + * The Authority Value for this facet + */ + @autoserialize + authorityKey?: string; + /** * The {@link HALLink}s for this FacetValue */ diff --git a/src/app/shared/search/search.utils.spec.ts b/src/app/shared/search/search.utils.spec.ts index ca4b4894b1..40f1328ffe 100644 --- a/src/app/shared/search/search.utils.spec.ts +++ b/src/app/shared/search/search.utils.spec.ts @@ -12,6 +12,7 @@ describe('Search Utils', () => { let facetValueWithSearchHref: FacetValue; let facetValueWithoutSearchHref: FacetValue; let searchFilterConfig: SearchFilterConfig; + let facetValueWithSearchHrefAuthority: FacetValue; beforeEach(() => { facetValueWithSearchHref = Object.assign(new FacetValue(), { @@ -22,6 +23,11 @@ describe('Search Utils', () => { } } }); + facetValueWithSearchHrefAuthority = Object.assign(new FacetValue(), { + value: 'Value with search href', + authorityKey: 'uuid', + } + ); facetValueWithoutSearchHref = Object.assign(new FacetValue(), { value: 'Value without search href' }); @@ -34,6 +40,10 @@ describe('Search Utils', () => { expect(getFacetValueForType(facetValueWithSearchHref, searchFilterConfig)).toEqual('Value with search href,operator'); }); + it('should retrieve the correct value from the Facet', () => { + expect(getFacetValueForType(facetValueWithSearchHrefAuthority, searchFilterConfig)).toEqual('uuid,authority'); + }); + it('should return the facet value with an equals operator by default', () => { expect(getFacetValueForType(facetValueWithoutSearchHref, searchFilterConfig)).toEqual('Value without search href,equals'); }); diff --git a/src/app/shared/search/search.utils.ts b/src/app/shared/search/search.utils.ts index 38c5f568cb..2b9944562a 100644 --- a/src/app/shared/search/search.utils.ts +++ b/src/app/shared/search/search.utils.ts @@ -16,6 +16,10 @@ export function getFacetValueForType(facetValue: FacetValue, searchFilterConfig: return values[1]; } } + if (facetValue.authorityKey) { + return addOperatorToFilterValue(facetValue.authorityKey, 'authority'); + } + return addOperatorToFilterValue(facetValue.value, 'equals'); } From c1555326fa3a4d676841125060d0a803cfdd9cb1 Mon Sep 17 00:00:00 2001 From: William Welling Date: Fri, 3 Dec 2021 21:17:36 -0600 Subject: [PATCH 014/102] build app config --- config/.gitignore | 1 + config/appConfig.json | 14 + scripts/set-env.ts | 8 +- .../notification.component.spec.ts | 12 - .../notifications.service.spec.ts | 13 - src/assets/.gitignore | 1 + ...g.interface.ts => app-config.interface.ts} | 10 +- src/config/config.ts | 151 +++++++++ src/config/default-app-config.ts | 306 ++++++++++++++++++ src/environments/environment.common.ts | 4 +- src/environments/mock-environment.ts | 4 +- src/main.ts | 20 -- webpack/webpack.browser.ts | 9 +- 13 files changed, 498 insertions(+), 55 deletions(-) create mode 100644 config/.gitignore create mode 100644 config/appConfig.json create mode 100644 src/assets/.gitignore rename src/config/{global-config.interface.ts => app-config.interface.ts} (87%) create mode 100644 src/config/config.ts create mode 100644 src/config/default-app-config.ts delete mode 100644 src/main.ts diff --git a/config/.gitignore b/config/.gitignore new file mode 100644 index 0000000000..e1899191e1 --- /dev/null +++ b/config/.gitignore @@ -0,0 +1 @@ +appConfig.*.json diff --git a/config/appConfig.json b/config/appConfig.json new file mode 100644 index 0000000000..3390bbb478 --- /dev/null +++ b/config/appConfig.json @@ -0,0 +1,14 @@ +{ + "ui": { + "ssl": false, + "host": "localhost", + "port": 4000, + "nameSpace": "/" + }, + "rest": { + "ssl": true, + "host": "api7.dspace.org", + "port": 443, + "nameSpace": "/server" + } +} diff --git a/scripts/set-env.ts b/scripts/set-env.ts index b3516ae68f..fd806ee1d9 100644 --- a/scripts/set-env.ts +++ b/scripts/set-env.ts @@ -1,6 +1,6 @@ import { writeFile } from 'fs'; import { environment as commonEnv } from '../src/environments/environment.common'; -import { GlobalConfig } from '../src/config/global-config.interface'; +import { AppConfig } from '../src/config/app-config.interface'; import { ServerConfig } from '../src/config/server-config.interface'; import { hasValue } from '../src/app/shared/empty.util'; @@ -42,7 +42,7 @@ const processEnv = { process.env.DSPACE_REST_PORT, process.env.DSPACE_REST_NAMESPACE, process.env.DSPACE_REST_SSL) -} as GlobalConfig; +} as AppConfig; import(environmentFilePath) .then((file) => generateEnvironmentFile(merge.all([commonEnv, file.environment, processEnv], mergeOptions))) @@ -51,7 +51,7 @@ import(environmentFilePath) generateEnvironmentFile(merge(commonEnv, processEnv, mergeOptions)) }); -function generateEnvironmentFile(file: GlobalConfig): void { +function generateEnvironmentFile(file: AppConfig): void { file.production = production; buildBaseUrls(file); const contents = `export const environment = ` + JSON.stringify(file); @@ -86,7 +86,7 @@ function createServerConfig(host?: string, port?: string, nameSpace?: string, ss return result; } -function buildBaseUrls(config: GlobalConfig): void { +function buildBaseUrls(config: AppConfig): void { for (const key in config) { if (config.hasOwnProperty(key) && config[key].host) { config[key].baseUrl = [ diff --git a/src/app/shared/notifications/notification/notification.component.spec.ts b/src/app/shared/notifications/notification/notification.component.spec.ts index 2bded57636..1d19464578 100644 --- a/src/app/shared/notifications/notification/notification.component.spec.ts +++ b/src/app/shared/notifications/notification/notification.component.spec.ts @@ -10,8 +10,6 @@ import { NotificationsService } from '../notifications.service'; import { NotificationType } from '../models/notification-type'; import { notificationsReducer } from '../notifications.reducers'; import { NotificationOptions } from '../models/notification-options.model'; -import { INotificationBoardOptions } from '../../../../config/notifications-config.interfaces'; -import { GlobalConfig } from '../../../../config/global-config.interface'; import { Notification } from '../models/notification.model'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; @@ -33,16 +31,6 @@ describe('NotificationComponent', () => { /* tslint:disable:no-empty */ notifications: [] }); - const envConfig: GlobalConfig = { - notifications: { - rtl: false, - position: ['top', 'right'], - maxStack: 8, - timeOut: 5000, - clickToClose: true, - animate: 'scale' - } as INotificationBoardOptions, - } as any; TestBed.configureTestingModule({ imports: [ diff --git a/src/app/shared/notifications/notifications.service.spec.ts b/src/app/shared/notifications/notifications.service.spec.ts index bb0f94eede..92c74e0017 100644 --- a/src/app/shared/notifications/notifications.service.spec.ts +++ b/src/app/shared/notifications/notifications.service.spec.ts @@ -8,7 +8,6 @@ import { of as observableOf } from 'rxjs'; import { NewNotificationAction, RemoveAllNotificationsAction, RemoveNotificationAction } from './notifications.actions'; import { Notification } from './models/notification.model'; import { NotificationType } from './models/notification-type'; -import { GlobalConfig } from '../../../config/global-config.interface'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../mocks/translate-loader.mock'; import { storeModuleConfig } from '../../app.reducer'; @@ -19,18 +18,6 @@ describe('NotificationsService test', () => { select: observableOf(true) }); let service: NotificationsService; - let envConfig: GlobalConfig; - - envConfig = { - notifications: { - rtl: false, - position: ['top', 'right'], - maxStack: 8, - timeOut: 5000, - clickToClose: true, - animate: 'scale' - }, - } as any; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ diff --git a/src/assets/.gitignore b/src/assets/.gitignore new file mode 100644 index 0000000000..c25cf8c104 --- /dev/null +++ b/src/assets/.gitignore @@ -0,0 +1 @@ +appConfig.json diff --git a/src/config/global-config.interface.ts b/src/config/app-config.interface.ts similarity index 87% rename from src/config/global-config.interface.ts rename to src/config/app-config.interface.ts index d46822eb61..9a993cb415 100644 --- a/src/config/global-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -1,3 +1,4 @@ +import { InjectionToken } from '@angular/core'; import { Config } from './config.interface'; import { ServerConfig } from './server-config.interface'; import { CacheConfig } from './cache-config.interface'; @@ -14,7 +15,7 @@ import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; -export interface GlobalConfig extends Config { +interface AppConfig extends Config { ui: UIServerConfig; rest: ServerConfig; production: boolean; @@ -33,3 +34,10 @@ export interface GlobalConfig extends Config { themes: ThemeConfig[]; mediaViewer: MediaViewerConfig; } + +const APP_CONFIG = new InjectionToken('APP_CONFIG'); + +export { + AppConfig, + APP_CONFIG +}; diff --git a/src/config/config.ts b/src/config/config.ts new file mode 100644 index 0000000000..d695c141f8 --- /dev/null +++ b/src/config/config.ts @@ -0,0 +1,151 @@ +import * as colors from 'colors'; +import * as fs from 'fs'; +import { join } from 'path'; +import { AppConfig } from './app-config.interface'; +import { Config } from './config.interface'; +import { DefaultAppConfig } from './default-app-config'; +import { ServerConfig } from './server-config.interface'; + +const CONFIG_PATH = join(process.cwd(), 'config'); + +const APP_CONFIG_PATH = join(CONFIG_PATH, 'appConfig.json'); + +type Environment = 'production' | 'development' | 'test'; + +const getEnvironment = (): Environment => { + let environment: Environment = 'development'; + if (!!process.env.NODE_ENV) { + switch (process.env.NODE_ENV) { + case 'prod': + case 'production': + environment = 'production'; + break; + case 'test': + environment = 'test'; + break; + case 'dev': + case 'development': + break; + default: + console.warn(`Unknown NODE_ENV ${process.env.NODE_ENV}. Defaulting to development`); + } + } + + return environment; +}; + +const getDistConfigPath = (env: Environment) => { + // determine app config filename variations + let envVariations; + switch (env) { + case 'production': + envVariations = ['prod', 'production']; + break; + case 'test': + envVariations = ['test']; + break; + case 'development': + default: + envVariations = ['dev', 'development'] + } + + // check if any environment variations of app config exist + for (const envVariation of envVariations) { + const altDistConfigPath = join(CONFIG_PATH, `appConfig.${envVariation}.json`); + if (fs.existsSync(altDistConfigPath)) { + return altDistConfigPath; + } + } + + // return default config/appConfig.json + return APP_CONFIG_PATH; +}; + +const overrideWithConfig = (config: Config, pathToConfig: string) => { + try { + console.log(`Overriding app config with ${pathToConfig}`); + const externalConfig = fs.readFileSync(pathToConfig, 'utf8'); + Object.assign(config, JSON.parse(externalConfig)); + } catch (err) { + console.error(err); + } +}; + +const overrideWithEnvironment = (config: Config, key: string = '') => { + for (const property in config) { + const variable = `${key}${!!key ? '_' : ''}${property.toUpperCase()}`; + const innerConfig = config[property]; + if (!!innerConfig) { + if (typeof innerConfig === 'object') { + overrideWithEnvironment(innerConfig, variable); + } else { + if (!!process.env[variable]) { + console.log(`Applying environment variable ${variable} with value ${process.env[variable]}`); + config[property] = process.env[variable]; + } + } + } + } +}; + +const buildBaseUrl = (config: ServerConfig): void => { + config.baseUrl = [ + config.ssl ? 'https://' : 'http://', + config.host, + config.port && config.port !== 80 && config.port !== 443 ? `:${config.port}` : '', + config.nameSpace && config.nameSpace.startsWith('/') ? config.nameSpace : `/${config.nameSpace}` + ].join(''); +}; + +export const buildAppConfig = (destConfigPath: string): AppConfig => { + // start with default app config + const appConfig: AppConfig = new DefaultAppConfig(); + + // determine which dist app config by environment + const env = getEnvironment(); + + switch (env) { + case 'production': + console.log(`Building ${colors.red.bold(`production`)} app config`); + break; + case 'test': + console.log(`Building ${colors.blue.bold(`test`)} app config`); + break; + default: + console.log(`Building ${colors.green.bold(`development`)} app config`); + } + + // override with dist config + const distConfigPath = getDistConfigPath(env); + if (fs.existsSync(distConfigPath)) { + overrideWithConfig(appConfig, distConfigPath); + } else { + console.warn(`Unable to find dist config file at ${distConfigPath}`); + } + + // override with external config if specified by environment variable `APP_CONFIG_PATH` + const externalConfigPath = process.env.APP_CONFIG_PATH; + if (!!externalConfigPath) { + if (fs.existsSync(externalConfigPath)) { + overrideWithConfig(appConfig, externalConfigPath); + } else { + console.warn(`Unable to find external config file at ${externalConfigPath}`); + } + } + + // override with environment variables + overrideWithEnvironment(appConfig); + + // apply build defined production + appConfig.production = env === 'production'; + + // build base URLs + buildBaseUrl(appConfig.ui); + buildBaseUrl(appConfig.rest); + + fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); + + console.log(`Angular ${colors.bold('appConfig.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`); + + return appConfig; +} diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts new file mode 100644 index 0000000000..12859db977 --- /dev/null +++ b/src/config/default-app-config.ts @@ -0,0 +1,306 @@ +import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { RestRequestMethod } from '../app/core/data/rest-request-method'; +import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; +import { AppConfig } from './app-config.interface'; +import { AuthConfig } from './auth-config.interfaces'; +import { BrowseByConfig } from './browse-by-config.interface'; +import { CacheConfig } from './cache-config.interface'; +import { CollectionPageConfig } from './collection-page-config.interface'; +import { FormConfig } from './form-config.interfaces'; +import { ItemPageConfig } from './item-page-config.interface'; +import { LangConfig } from './lang-config.interface'; +import { MediaViewerConfig } from './media-viewer-config.interface'; +import { INotificationBoardOptions } from './notifications-config.interfaces'; +import { ServerConfig } from './server-config.interface'; +import { SubmissionConfig } from './submission-config.interface'; +import { ThemeConfig } from './theme.model'; +import { UIServerConfig } from './ui-server-config.interface'; +import { UniversalConfig } from './universal-config.interface'; + +export class DefaultAppConfig implements AppConfig { + production: boolean = false; + + // Angular Universal server settings. + // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. + ui: UIServerConfig = { + ssl: false, + host: 'localhost', + port: 4000, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/', + + // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). + rateLimiter: { + windowMs: 1 * 60 * 1000, // 1 minute + max: 500 // limit each IP to 500 requests per windowMs + } + }; + + // The REST API server settings. + // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. + rest: ServerConfig = { + ssl: false, + host: 'localhost', + port: 8080, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/', + }; + + // Caching settings + cache: CacheConfig = { + // NOTE: how long should objects be cached for by default + msToLive: { + default: 15 * 60 * 1000 // 15 minutes + }, + control: 'max-age=60', // revalidate browser + autoSync: { + defaultTime: 0, + maxBufferSize: 100, + timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds + } + }; + + // Authentication settings + auth: AuthConfig = { + // Authentication UI settings + ui: { + // the amount of time before the idle warning is shown + timeUntilIdle: 15 * 60 * 1000, // 15 minutes + // the amount of time the user has to react after the idle warning is shown before they are logged out. + idleGracePeriod: 5 * 60 * 1000 // 5 minutes + }, + // Authentication REST settings + rest: { + // If the rest token expires in less than this amount of time, it will be refreshed automatically. + // This is independent from the idle warning. + timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes + } + }; + + // Form settings + form: FormConfig = { + // NOTE: Map server-side validators to comparative Angular form validators + validatorMap: { + required: 'required', + regex: 'pattern' + } + }; + + // Notifications + notifications: INotificationBoardOptions = { + rtl: false, + position: ['top', 'right'], + maxStack: 8, + // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically + timeOut: 5000, // 5 second + clickToClose: true, + // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' + animate: NotificationAnimationsType.Scale + }; + + // Submission settings + submission: SubmissionConfig = { + autosave: { + // NOTE: which metadata trigger an autosave + metadata: [], + /** + * NOTE: after how many time (milliseconds) submission is saved automatically + * eg. timer: 5 * (1000 * 60); // 5 minutes + */ + timer: 0 + }, + icons: { + metadata: [ + /** + * NOTE: example of configuration + * { + * // NOTE: metadata name + * name: 'dc.author', + * // NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used + * style: 'fa-user' + * } + */ + { + name: 'dc.author', + style: 'fas fa-user' + }, + // default configuration + { + name: 'default', + style: '' + } + ], + authority: { + confidence: [ + /** + * NOTE: example of configuration + * { + * // NOTE: confidence value + * value: 'dc.author', + * // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used + * style: 'fa-user' + * } + */ + { + value: 600, + style: 'text-success' + }, + { + value: 500, + style: 'text-info' + }, + { + value: 400, + style: 'text-warning' + }, + // default configuration + { + value: 'default', + style: 'text-muted' + } + + ] + } + } + }; + + // Angular Universal settings + universal: UniversalConfig = { + preboot: true, + async: true, + time: false + }; + + // NOTE: will log all redux actions and transfers in console + debug: boolean = false; + + // Default Language in which the UI will be rendered if the user's browser language is not an active language + defaultLanguage: string = 'en'; + + // Languages. DSpace Angular holds a message catalog for each of the following languages. + // When set to active, users will be able to switch to the use of this language in the user interface. + languages: LangConfig[] = [ + { code: 'en', label: 'English', active: true }, + { code: 'cs', label: 'Čeština', active: true }, + { code: 'de', label: 'Deutsch', active: true }, + { code: 'es', label: 'Español', active: true }, + { code: 'fr', label: 'Français', active: true }, + { code: 'lv', label: 'Latviešu', active: true }, + { code: 'hu', label: 'Magyar', active: true }, + { code: 'nl', label: 'Nederlands', active: true }, + { code: 'pt-PT', label: 'Português', active: true }, + { code: 'pt-BR', label: 'Português do Brasil', active: true }, + { code: 'fi', label: 'Suomi', active: true } + ]; + + // Browse-By Pages + browseBy: BrowseByConfig = { + // Amount of years to display using jumps of one year (current year - oneYearLimit) + oneYearLimit: 10, + // Limit for years to display using jumps of five years (current year - fiveYearLimit) + fiveYearLimit: 30, + // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) + defaultLowerLimit: 1900, + // List of all the active Browse-By types + // Adding a type will activate their Browse-By page and add them to the global navigation menu, + // as well as community and collection pages + // Allowed fields and their purpose: + // id: The browse id to use for fetching info from the rest api + // type: The type of Browse-By page to display + // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') + types: [ + { + id: 'title', + type: BrowseByType.Title, + }, + { + id: 'dateissued', + type: BrowseByType.Date, + metadataField: 'dc.date.issued' + }, + { + id: 'author', + type: BrowseByType.Metadata + }, + { + id: 'subject', + type: BrowseByType.Metadata + } + ] + }; + + // Item Page Config + item: ItemPageConfig = { + edit: { + undoTimeout: 10000 // 10 seconds + } + }; + + // Collection Page Config + collection: CollectionPageConfig = { + edit: { + undoTimeout: 10000 // 10 seconds + } + }; + + // Theme Config + themes: ThemeConfig[] = [ + // Add additional themes here. In the case where multiple themes match a route, the first one + // in this list will get priority. It is advisable to always have a theme that matches + // every route as the last one + + // { + // // A theme with a handle property will match the community, collection or item with the given + // // handle, and all collections and/or items within it + // name: 'custom', + // handle: '10673/1233' + // }, + // { + // // A theme with a regex property will match the route using a regular expression. If it + // // matches the route for a community or collection it will also apply to all collections + // // and/or items within it + // name: 'custom', + // regex: 'collections\/e8043bc2.*' + // }, + // { + // // A theme with a uuid property will match the community, collection or item with the given + // // ID, and all collections and/or items within it + // name: 'custom', + // uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' + // }, + // { + // // The extends property specifies an ancestor theme (by name). Whenever a themed component is not found + // // in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. + // name: 'custom-A', + // extends: 'custom-B', + // // Any of the matching properties above can be used + // handle: '10673/34' + // }, + // { + // name: 'custom-B', + // extends: 'custom', + // handle: '10673/12' + // }, + // { + // // A theme with only a name will match every route + // name: 'custom' + // }, + // { + // // This theme will use the default bootstrap styling for DSpace components + // name: BASE_THEME_NAME + // }, + + { + // The default dspace theme + name: 'dspace' + } + ]; + + // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). + // For images, this enables a gallery viewer where you can zoom or page through images. + // For videos, this enables embedded video streaming + mediaViewer: MediaViewerConfig = { + image: false, + video: false + }; +} diff --git a/src/environments/environment.common.ts b/src/environments/environment.common.ts index b1cbd699a3..6bb2c8dc51 100644 --- a/src/environments/environment.common.ts +++ b/src/environments/environment.common.ts @@ -1,9 +1,9 @@ -import { GlobalConfig } from '../config/global-config.interface'; +import { AppConfig } from '../config/app-config.interface'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; -export const environment: GlobalConfig = { +export const environment: AppConfig = { production: true, // Angular Universal server settings. // NOTE: these must be "synced" with the 'dspace.ui.url' setting in your backend's local.cfg. diff --git a/src/environments/mock-environment.ts b/src/environments/mock-environment.ts index 824c8c8a83..02c1012c68 100644 --- a/src/environments/mock-environment.ts +++ b/src/environments/mock-environment.ts @@ -2,9 +2,9 @@ import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; -import { GlobalConfig } from '../config/global-config.interface'; +import { AppConfig } from '../config/app-config.interface'; -export const environment: Partial = { +export const environment: Partial = { rest: { ssl: true, host: 'rest.com', diff --git a/src/main.ts b/src/main.ts deleted file mode 100644 index 734cd3c5f5..0000000000 --- a/src/main.ts +++ /dev/null @@ -1,20 +0,0 @@ -import 'core-js/es/reflect'; -import 'zone.js/dist/zone'; -import 'reflect-metadata'; - -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -document.addEventListener('DOMContentLoaded', () => { - document.addEventListener('DOMContentLoaded', () => { - platformBrowserDynamic().bootstrapModule(AppModule) - .catch((err) => console.error(err)); - }); -}); diff --git a/webpack/webpack.browser.ts b/webpack/webpack.browser.ts index bc281e9b43..3c1d01dda0 100644 --- a/webpack/webpack.browser.ts +++ b/webpack/webpack.browser.ts @@ -1,8 +1,15 @@ +import { buildAppConfig } from '../src/config/config'; import { commonExports } from './webpack.common'; +import { join } from 'path'; module.exports = Object.assign({}, commonExports, { target: 'web', node: { module: 'empty' - } + }, + devServer: { + before(app, server) { + buildAppConfig(join(process.cwd(), 'src/assets/appConfig.json')); + } + } }); From 71f5b46639babc0ab551993dfcc9eda4f537c196 Mon Sep 17 00:00:00 2001 From: William Welling Date: Sat, 4 Dec 2021 10:37:56 -0600 Subject: [PATCH 015/102] use standard environments --- .github/workflows/build.yml | 2 +- .gitignore | 4 - angular.json | 50 +- config/appConfig.json | 6 - cypress/.gitignore | 2 + mock-nodemon.json | 5 - nodemon.json | 6 - package.json | 38 +- scripts/serve.ts | 13 +- scripts/set-env.ts | 116 ---- scripts/set-mock-env.ts | 11 - scripts/test-rest.ts | 8 +- server.ts | 11 +- src/app/app.component.ts | 15 +- src/app/app.module.ts | 18 +- .../community-authorizations.component.ts | 4 +- src/app/core/data/request.models.ts | 3 +- .../core/shared/hal-endpoint.service.spec.ts | 2 +- .../resource-policy-form.component.spec.ts | 4 +- src/app/shared/theme-support/theme.effects.ts | 15 +- src/config/{config.ts => config.server.ts} | 76 ++- src/config/config.util.ts | 37 ++ src/environments/environment.ci.ts | 294 ++++++++++ ...ironment.common.ts => environment.prod.ts} | 129 ++--- src/environments/environment.template.ts | 10 - ...ock-environment.ts => environment.test.ts} | 66 ++- src/environments/environment.ts | 309 ++++++++++ src/main.browser.ts | 27 +- webpack/webpack.browser.ts | 7 +- yarn.lock | 536 +----------------- 30 files changed, 963 insertions(+), 861 deletions(-) create mode 100644 cypress/.gitignore delete mode 100644 mock-nodemon.json delete mode 100644 nodemon.json delete mode 100644 scripts/set-env.ts delete mode 100644 scripts/set-mock-env.ts rename src/config/{config.ts => config.server.ts} (55%) create mode 100644 src/config/config.util.ts create mode 100644 src/environments/environment.ci.ts rename src/environments/{environment.common.ts => environment.prod.ts} (84%) delete mode 100644 src/environments/environment.template.ts rename src/environments/{mock-environment.ts => environment.test.ts} (90%) create mode 100644 src/environments/environment.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7758020724..ab537f80cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,7 +73,7 @@ jobs: run: yarn run lint - name: Run build - run: yarn run build:prod + run: yarn run build:ssr:ci - name: Run specs (unit tests) run: yarn run test:headless diff --git a/.gitignore b/.gitignore index f110ba720c..026110f222 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,6 @@ npm-debug.log /build/ -/src/environments/environment.ts -/src/environments/environment.dev.ts -/src/environments/environment.prod.ts - /coverage /dist/ diff --git a/angular.json b/angular.json index c6607fc80a..b81d166cea 100644 --- a/angular.json +++ b/angular.json @@ -68,6 +68,12 @@ }, "configurations": { "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], "optimization": true, "outputHashing": "all", "extractCss": true, @@ -88,6 +94,22 @@ "maximumError": "300kb" } ] + }, + "ci": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.ci.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true } } }, @@ -139,6 +161,16 @@ } ], "scripts": [] + }, + "configurations": { + "test": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.test.ts" + } + ] + } } }, "lint": { @@ -183,7 +215,23 @@ "configurations": { "production": { "sourceMap": false, - "optimization": true + "optimization": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ] + }, + "ci": { + "sourceMap": false, + "optimization": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.ci.ts" + } + ] } } }, diff --git a/config/appConfig.json b/config/appConfig.json index 3390bbb478..a70d8ea6ab 100644 --- a/config/appConfig.json +++ b/config/appConfig.json @@ -1,10 +1,4 @@ { - "ui": { - "ssl": false, - "host": "localhost", - "port": 4000, - "nameSpace": "/" - }, "rest": { "ssl": true, "host": "api7.dspace.org", diff --git a/cypress/.gitignore b/cypress/.gitignore new file mode 100644 index 0000000000..99bd2a6312 --- /dev/null +++ b/cypress/.gitignore @@ -0,0 +1,2 @@ +screenshots/ +videos/ diff --git a/mock-nodemon.json b/mock-nodemon.json deleted file mode 100644 index 18fc86bd9d..0000000000 --- a/mock-nodemon.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "watch": ["src/environments/mock-environment.ts"], - "ext": "ts", - "exec": "ts-node --project ./tsconfig.ts-node.json scripts/set-mock-env.ts" -} diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index 39e9d9aa5b..0000000000 --- a/nodemon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "watch": ["src/environments"], - "ext": "ts", - "ignore": ["src/environments/environment.ts", "src/environments/mock-environment.ts"], - "exec": "ts-node --project ./tsconfig.ts-node.json scripts/set-env.ts --dev" -} diff --git a/package.json b/package.json index 236845da1a..4ce674ce1c 100644 --- a/package.json +++ b/package.json @@ -3,23 +3,10 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "config:dev": "ts-node --project ./tsconfig.ts-node.json scripts/set-env.ts --dev", - "config:prod": "ts-node --project ./tsconfig.ts-node.json scripts/set-env.ts --prod", - "config:test": "ts-node --project ./tsconfig.ts-node.json scripts/set-mock-env.ts", - "config:test:watch": "nodemon --config mock-nodemon.json", - "config:dev:watch": "nodemon", - "config:check:rest": "yarn run config:prod && ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", - "config:dev:check:rest": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", - "prestart:dev": "yarn run config:dev", - "prebuild": "yarn run config:dev", - "pretest": "yarn run config:test", - "pretest:watch": "yarn run config:test", - "pretest:headless": "yarn run config:test", - "prebuild:prod": "yarn run config:prod", - "pree2e": "yarn run config:prod", + "test:rest": "ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", "start": "yarn run start:prod", "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", - "start:dev": "npm-run-all --parallel config:dev:watch serve", + "start:dev": "yarn run serve", "start:prod": "yarn run build:prod && yarn run serve:ssr", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod", "analyze": "webpack-bundle-analyzer dist/browser/stats.json", @@ -27,27 +14,25 @@ "build:stats": "ng build --stats-json", "build:prod": "yarn run build:ssr", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", - "test:watch": "npm-run-all --parallel config:test:watch test", - "test": "ng test --sourceMap=true --watch=true", - "test:headless": "ng test --watch=false --sourceMap=true --browsers=ChromeHeadless --code-coverage", + "build:ssr:ci": "ng build --configuration ci && ng run dspace-angular:server:ci", + "test": "ng test --sourceMap=true --watch=true --configuration test", + "test:headless": "ng test --sourceMap=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", "lint": "ng lint", "lint-fix": "ng lint --fix=true", "e2e": "ng e2e", "serve:ssr": "node dist/server/main", + "clean:dev:config": "rimraf src/assets/appConfig.json", "clean:coverage": "rimraf coverage", "clean:dist": "rimraf dist", "clean:doc": "rimraf doc", "clean:log": "rimraf *.log*", "clean:json": "rimraf *.records.json", - "clean:bld": "rimraf build", "clean:node": "rimraf node_modules", - "clean:prod": "yarn run clean:coverage && yarn run clean:doc && yarn run clean:dist && yarn run clean:log && yarn run clean:json && yarn run clean:bld", - "clean": "yarn run clean:prod && yarn run clean:env && yarn run clean:node", - "clean:env": "rimraf src/environments/environment.ts", - "sync-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts", + "clean:prod": "yarn run clean:dist && yarn run clean:log && yarn run clean:doc && yarn run clean:coverage && yarn run clean:json", + "clean": "yarn run clean:prod && yarn run clean:node && yarn run clean:dev:config", + "sync-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts", "build:mirador": "webpack --config webpack/webpack.mirador.config.ts", - "merge-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", - "postinstall": "ngcc", + "merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", "cypress:open": "cypress open", "cypress:run": "cypress run" }, @@ -74,7 +59,6 @@ "@angular/platform-browser-dynamic": "~11.2.14", "@angular/platform-server": "~11.2.14", "@angular/router": "~11.2.14", - "@angularclass/bootloader": "1.0.1", "@kolkov/ngx-gallery": "^1.2.3", "@ng-bootstrap/ng-bootstrap": "9.1.3", "@ng-dynamic-forms/core": "^13.0.0", @@ -178,8 +162,6 @@ "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", - "nodemon": "^2.0.2", - "npm-run-all": "^4.1.5", "optimize-css-assets-webpack-plugin": "^5.0.4", "postcss-apply": "0.11.0", "postcss-import": "^12.0.1", diff --git a/scripts/serve.ts b/scripts/serve.ts index c69f8e8a21..903374c615 100644 --- a/scripts/serve.ts +++ b/scripts/serve.ts @@ -1,11 +1,16 @@ -import { environment } from '../src/environments/environment'; - import * as child from 'child_process'; +import { environment } from '../src/environments/environment'; + +// import { AppConfig } from '../src/config/app-config.interface'; +// import { buildAppConfig } from '../src/config/config.server'; + +// const appConfig: AppConfig = buildAppConfig(); + /** - * Calls `ng serve` with the following arguments configured for the UI in the environment file: host, port, nameSpace, ssl + * Calls `ng serve` with the following arguments configured for the UI in the environment: host, port, nameSpace, ssl */ child.spawn( - `ng serve --host ${environment.ui.host} --port ${environment.ui.port} --servePath ${environment.ui.nameSpace} --ssl ${environment.ui.ssl}`, + `ng serve --host ${environment.ui.host} --port ${environment.ui.port} --serve-path ${environment.ui.nameSpace} --ssl ${environment.ui.ssl}`, { stdio:'inherit', shell: true } ); diff --git a/scripts/set-env.ts b/scripts/set-env.ts deleted file mode 100644 index fd806ee1d9..0000000000 --- a/scripts/set-env.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { writeFile } from 'fs'; -import { environment as commonEnv } from '../src/environments/environment.common'; -import { AppConfig } from '../src/config/app-config.interface'; -import { ServerConfig } from '../src/config/server-config.interface'; -import { hasValue } from '../src/app/shared/empty.util'; - -// Configure Angular `environment.ts` file path -const targetPath = './src/environments/environment.ts'; -// Load node modules -const colors = require('colors'); -require('dotenv').config(); -const merge = require('deepmerge'); -const mergeOptions = { arrayMerge: (destinationArray, sourceArray, options) => sourceArray }; -const environment = process.argv[2]; -let environmentFilePath; -let production = false; - -switch (environment) { - case '--prod': - case '--production': - production = true; - console.log(`Building ${colors.red.bold(`production`)} environment`); - environmentFilePath = '../src/environments/environment.prod.ts'; - break; - case '--test': - console.log(`Building ${colors.blue.bold(`test`)} environment`); - environmentFilePath = '../src/environments/environment.test.ts'; - break; - default: - console.log(`Building ${colors.green.bold(`development`)} environment`); - environmentFilePath = '../src/environments/environment.dev.ts'; -} - -const processEnv = { - ui: createServerConfig( - process.env.DSPACE_HOST, - process.env.DSPACE_PORT, - process.env.DSPACE_NAMESPACE, - process.env.DSPACE_SSL), - rest: createServerConfig( - process.env.DSPACE_REST_HOST, - process.env.DSPACE_REST_PORT, - process.env.DSPACE_REST_NAMESPACE, - process.env.DSPACE_REST_SSL) -} as AppConfig; - -import(environmentFilePath) - .then((file) => generateEnvironmentFile(merge.all([commonEnv, file.environment, processEnv], mergeOptions))) - .catch(() => { - console.log(colors.yellow.bold(`No specific environment file found for ` + environment)); - generateEnvironmentFile(merge(commonEnv, processEnv, mergeOptions)) - }); - -function generateEnvironmentFile(file: AppConfig): void { - file.production = production; - buildBaseUrls(file); - const contents = `export const environment = ` + JSON.stringify(file); - writeFile(targetPath, contents, (err) => { - if (err) { - throw console.error(err); - } else { - console.log(`Angular ${colors.bold('environment.ts')} file generated correctly at ${colors.bold(targetPath)} \n`); - } - }); -} - -// allow to override a few important options by environment variables -function createServerConfig(host?: string, port?: string, nameSpace?: string, ssl?: string): ServerConfig { - const result = {} as any; - if (hasValue(host)) { - result.host = host; - } - - if (hasValue(nameSpace)) { - result.nameSpace = nameSpace; - } - - if (hasValue(port)) { - result.port = Number(port); - } - - if (hasValue(ssl)) { - result.ssl = ssl.trim().match(/^(true|1|yes)$/i) ? true : false; - } - - return result; -} - -function buildBaseUrls(config: AppConfig): void { - for (const key in config) { - if (config.hasOwnProperty(key) && config[key].host) { - config[key].baseUrl = [ - getProtocol(config[key].ssl), - getHost(config[key].host), - getPort(config[key].port), - getNameSpace(config[key].nameSpace) - ].join(''); - } - } -} - -function getProtocol(ssl: boolean): string { - return ssl ? 'https://' : 'http://'; -} - -function getHost(host: string): string { - return host; -} - -function getPort(port: number): string { - return port ? (port !== 80 && port !== 443) ? ':' + port : '' : ''; -} - -function getNameSpace(nameSpace: string): string { - return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : ''; -} diff --git a/scripts/set-mock-env.ts b/scripts/set-mock-env.ts deleted file mode 100644 index 5271432896..0000000000 --- a/scripts/set-mock-env.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { copyFile } from 'fs'; - -// Configure Angular `environment.ts` file path -const sourcePath = './src/environments/mock-environment.ts'; -const targetPath = './src/environments/environment.ts'; - -// destination.txt will be created or overwritten by default. -copyFile(sourcePath, targetPath, (err) => { - if (err) throw err; - console.log(sourcePath + ' was copied to ' + targetPath); -}); diff --git a/scripts/test-rest.ts b/scripts/test-rest.ts index b12a9929c2..1584613387 100644 --- a/scripts/test-rest.ts +++ b/scripts/test-rest.ts @@ -1,13 +1,19 @@ import * as http from 'http'; import * as https from 'https'; + import { environment } from '../src/environments/environment'; +// import { AppConfig } from '../src/config/app-config.interface'; +// import { buildAppConfig } from '../src/config/config.server'; + +// const appConfig: AppConfig = buildAppConfig(); + /** * Script to test the connection with the configured REST API (in the 'rest' settings of your environment.*.ts) * * This script is useful to test for any Node.js connection issues with your REST API. * - * Usage (see package.json): yarn test:rest-api + * Usage (see package.json): yarn test:rest */ // Get root URL of configured REST API diff --git a/server.ts b/server.ts index c00bdb5ef5..9a48a91ffb 100644 --- a/server.ts +++ b/server.ts @@ -40,6 +40,9 @@ 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'; + /* * Set path for the browser application's dist folder */ @@ -51,6 +54,8 @@ const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : ' const cookieParser = require('cookie-parser'); +// const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/appConfig.json')); + // The Express app is exported so that it can be used by serverless Functions. export function app() { @@ -100,7 +105,11 @@ export function app() { provide: RESPONSE, useValue: (options as any).req.res, }, - ], + // { + // provide: APP_CONFIG, + // useValue: appConfig + // } + ] })(_, (options as any), callback) ); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6f06a84144..bf096c210f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,5 @@ import { distinctUntilChanged, filter, switchMap, take, withLatestFrom } from 'rxjs/operators'; +import { DOCUMENT, isPlatformBrowser } from '@angular/common'; import { AfterViewInit, ChangeDetectionStrategy, @@ -19,6 +20,7 @@ import { import { BehaviorSubject, Observable, of } from 'rxjs'; import { select, Store } from '@ngrx/store'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; @@ -39,13 +41,11 @@ import { LocaleService } from './core/locale/locale.service'; import { hasValue, isNotEmpty } from './shared/empty.util'; import { KlaroService } from './shared/cookies/klaro.service'; import { GoogleAnalyticsService } from './statistics/google-analytics.service'; -import { DOCUMENT, isPlatformBrowser } from '@angular/common'; import { ThemeService } from './shared/theme-support/theme.service'; import { BASE_THEME_NAME } from './shared/theme-support/theme.constants'; -import { DEFAULT_THEME_CONFIG } from './shared/theme-support/theme.effects'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { getDefaultThemeConfig } from '../config/config.util'; @Component({ selector: 'ds-app', @@ -116,10 +116,13 @@ export class AppComponent implements OnInit, AfterViewInit { } if (hasValue(themeName)) { this.setThemeCss(themeName); - } else if (hasValue(DEFAULT_THEME_CONFIG)) { - this.setThemeCss(DEFAULT_THEME_CONFIG.name); } else { - this.setThemeCss(BASE_THEME_NAME); + const defaultThemeConfig = getDefaultThemeConfig(); + if (hasValue(defaultThemeConfig)) { + this.setThemeCss(defaultThemeConfig.name); + } else { + this.setThemeCss(BASE_THEME_NAME); + } } }); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e2cb10691b..a1c5addae8 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,8 @@ import { APP_BASE_HREF, CommonModule } from '@angular/common'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { AbstractControl } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { EffectsModule } from '@ngrx/effects'; @@ -37,7 +39,6 @@ import { NotificationsBoardComponent } from './shared/notifications/notification import { SharedModule } from './shared/shared.module'; import { BreadcrumbsComponent } from './breadcrumbs/breadcrumbs.component'; import { environment } from '../environments/environment'; -import { BrowserModule } from '@angular/platform-browser'; import { ForbiddenComponent } from './forbidden/forbidden.component'; import { AuthInterceptor } from './core/auth/auth.interceptor'; import { LocaleInterceptor } from './core/locale/locale.interceptor'; @@ -56,7 +57,8 @@ import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { UUIDService } from './core/shared/uuid.service'; import { CookieService } from './core/services/cookie.service'; -import { AbstractControl } from '@angular/forms'; + +// import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; export function getBase() { return environment.ui.nameSpace; @@ -100,11 +102,13 @@ IMPORTS.push( const PROVIDERS = [ { provide: APP_BASE_HREF, - useFactory: (getBase) + useFactory: getBase, + // deps: [APP_CONFIG] }, { provide: USER_PROVIDED_META_REDUCERS, useFactory: getMetaReducers, + // deps: [APP_CONFIG] }, { provide: RouterStateSerializer, @@ -117,7 +121,7 @@ const PROVIDERS = [ useFactory: (store: Store,) => { return () => store.dispatch(new CheckAuthenticationTokenAction()); }, - deps: [ Store ], + deps: [Store], multi: true }, // register AuthInterceptor as HttpInterceptor @@ -146,7 +150,7 @@ const PROVIDERS = [ }, // insert the unique id of the user that is using the application utilizing cookies { - provide: APP_INITIALIZER, + provide: APP_INITIALIZER, useFactory: (cookieService: CookieService, uuidService: UUIDService) => { const correlationId = cookieService.get('CORRELATION-ID'); @@ -156,8 +160,8 @@ const PROVIDERS = [ } return () => true; }, - multi: true, - deps: [ CookieService, UUIDService ] + multi: true, + deps: [CookieService, UUIDService] }, { provide: DYNAMIC_ERROR_MESSAGES_MATCHER, diff --git a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts index 8b241af667..7a9f224311 100644 --- a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts +++ b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; -import { RemoteData } from 'src/app/core/data/remote-data'; -import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; +import { RemoteData } from '../../../core/data/remote-data'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; @Component({ selector: 'ds-community-authorizations', diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index 667e4a0434..a29c99d326 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -20,7 +20,7 @@ export enum IdentifierType { } export abstract class RestRequest { - public responseMsToLive = environment.cache.msToLive.default; + public responseMsToLive; public isMultipart = false; constructor( @@ -30,6 +30,7 @@ export abstract class RestRequest { public body?: any, public options?: HttpOptions, ) { + this.responseMsToLive = environment.cache.msToLive.default; } getResponseParser(): GenericConstructor { diff --git a/src/app/core/shared/hal-endpoint.service.spec.ts b/src/app/core/shared/hal-endpoint.service.spec.ts index 8b71954cbe..b29b8f662e 100644 --- a/src/app/core/shared/hal-endpoint.service.spec.ts +++ b/src/app/core/shared/hal-endpoint.service.spec.ts @@ -89,7 +89,7 @@ describe('HALEndpointService', () => { describe('getRootEndpointMap', () => { it('should send a new EndpointMapRequest', () => { (service as any).getRootEndpointMap(); - const expected = new EndpointMapRequest(requestService.generateRequestId(), environment.rest.baseUrl + 'api'); + const expected = new EndpointMapRequest(requestService.generateRequestId(), `${environment.rest.baseUrl}/api`); expect(requestService.send).toHaveBeenCalledWith(expected, true); }); 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 7f66eb052c..5cc6397118 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 @@ -32,11 +32,11 @@ import { RESOURCE_POLICY } from '../../../core/resource-policy/models/resource-p import { EPersonMock } from '../../testing/eperson.mock'; import { isNotEmptyOperator } from '../../empty.util'; import { ActivatedRoute, Router } from '@angular/router'; -import { RemoteData } from 'src/app/core/data/remote-data'; +import { RemoteData } from '../../../core/data/remote-data'; import { RouterMock } from '../../mocks/router.mock'; import { Store } from '@ngrx/store'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; -import { PaginationService } from 'src/app/core/pagination/pagination.service'; +import { PaginationService } from '../../../core/pagination/pagination.service'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { StoreMock } from '../../testing/store.mock'; diff --git a/src/app/shared/theme-support/theme.effects.ts b/src/app/shared/theme-support/theme.effects.ts index e120257728..df74818fa8 100644 --- a/src/app/shared/theme-support/theme.effects.ts +++ b/src/app/shared/theme-support/theme.effects.ts @@ -2,15 +2,9 @@ import { Injectable } from '@angular/core'; import { createEffect, Actions, ofType, ROOT_EFFECTS_INIT } from '@ngrx/effects'; import { map } from 'rxjs/operators'; import { SetThemeAction } from './theme.actions'; -import { environment } from '../../../environments/environment'; -import { hasValue, hasNoValue } from '../empty.util'; +import { hasValue } from '../empty.util'; import { BASE_THEME_NAME } from './theme.constants'; - -export const DEFAULT_THEME_CONFIG = environment.themes.find((themeConfig: any) => - hasNoValue(themeConfig.regex) && - hasNoValue(themeConfig.handle) && - hasNoValue(themeConfig.uuid) -); +import { getDefaultThemeConfig } from '../../../config/config.util'; @Injectable() export class ThemeEffects { @@ -21,8 +15,9 @@ export class ThemeEffects { this.actions$.pipe( ofType(ROOT_EFFECTS_INIT), map(() => { - if (hasValue(DEFAULT_THEME_CONFIG)) { - return new SetThemeAction(DEFAULT_THEME_CONFIG.name); + const defaultThemeConfig = getDefaultThemeConfig(); + if (hasValue(defaultThemeConfig)) { + return new SetThemeAction(defaultThemeConfig.name); } else { return new SetThemeAction(BASE_THEME_NAME); } diff --git a/src/config/config.ts b/src/config/config.server.ts similarity index 55% rename from src/config/config.ts rename to src/config/config.server.ts index d695c141f8..355ba028ab 100644 --- a/src/config/config.ts +++ b/src/config/config.server.ts @@ -1,10 +1,15 @@ import * as colors from 'colors'; import * as fs from 'fs'; import { join } from 'path'; + +import { environment } from '../environments/environment'; + import { AppConfig } from './app-config.interface'; import { Config } from './config.interface'; import { DefaultAppConfig } from './default-app-config'; import { ServerConfig } from './server-config.interface'; +import { extendConfig, extendEnvironmentWithAppConfig } from './config.util'; +import { isNotEmpty } from '../app/shared/empty.util'; const CONFIG_PATH = join(process.cwd(), 'config'); @@ -12,9 +17,17 @@ const APP_CONFIG_PATH = join(CONFIG_PATH, 'appConfig.json'); type Environment = 'production' | 'development' | 'test'; +const getBooleanFromString = (variable: string): boolean => { + return variable === 'true' || variable === '1'; +}; + +const getNumberFromString = (variable: string): number => { + return Number(variable); +}; + const getEnvironment = (): Environment => { let environment: Environment = 'development'; - if (!!process.env.NODE_ENV) { + if (isNotEmpty(process.env.NODE_ENV)) { switch (process.env.NODE_ENV) { case 'prod': case 'production': @@ -34,7 +47,10 @@ const getEnvironment = (): Environment => { return environment; }; -const getDistConfigPath = (env: Environment) => { +const getLocalConfigPath = (env: Environment) => { + // default to config/appConfig.json + let localConfigPath = APP_CONFIG_PATH; + // determine app config filename variations let envVariations; switch (env) { @@ -53,19 +69,18 @@ const getDistConfigPath = (env: Environment) => { for (const envVariation of envVariations) { const altDistConfigPath = join(CONFIG_PATH, `appConfig.${envVariation}.json`); if (fs.existsSync(altDistConfigPath)) { - return altDistConfigPath; + localConfigPath = altDistConfigPath; } } - // return default config/appConfig.json - return APP_CONFIG_PATH; + return localConfigPath; }; const overrideWithConfig = (config: Config, pathToConfig: string) => { try { console.log(`Overriding app config with ${pathToConfig}`); const externalConfig = fs.readFileSync(pathToConfig, 'utf8'); - Object.assign(config, JSON.parse(externalConfig)); + extendConfig(config, JSON.parse(externalConfig)); } catch (err) { console.error(err); } @@ -73,15 +88,27 @@ const overrideWithConfig = (config: Config, pathToConfig: string) => { const overrideWithEnvironment = (config: Config, key: string = '') => { for (const property in config) { - const variable = `${key}${!!key ? '_' : ''}${property.toUpperCase()}`; + const variable = `${key}${isNotEmpty(key) ? '_' : ''}${property.toUpperCase()}`; const innerConfig = config[property]; - if (!!innerConfig) { + if (isNotEmpty(innerConfig)) { if (typeof innerConfig === 'object') { overrideWithEnvironment(innerConfig, variable); } else { - if (!!process.env[variable]) { + if (isNotEmpty(process.env[variable])) { console.log(`Applying environment variable ${variable} with value ${process.env[variable]}`); - config[property] = process.env[variable]; + switch (typeof innerConfig) { + case 'number': + config[property] = getNumberFromString(process.env[variable]); + break; + case 'boolean': + config[property] = getBooleanFromString(process.env[variable]); + break; + case 'string': + config[property] = process.env[variable]; + default: + console.warn(`Unsupported environment variable type ${typeof innerConfig} ${variable}`); + } + } } } @@ -97,7 +124,7 @@ const buildBaseUrl = (config: ServerConfig): void => { ].join(''); }; -export const buildAppConfig = (destConfigPath: string): AppConfig => { +export const buildAppConfig = (destConfigPath?: string): AppConfig => { // start with default app config const appConfig: AppConfig = new DefaultAppConfig(); @@ -116,7 +143,7 @@ export const buildAppConfig = (destConfigPath: string): AppConfig => { } // override with dist config - const distConfigPath = getDistConfigPath(env); + const distConfigPath = getLocalConfigPath(env); if (fs.existsSync(distConfigPath)) { overrideWithConfig(appConfig, distConfigPath); } else { @@ -125,7 +152,7 @@ export const buildAppConfig = (destConfigPath: string): AppConfig => { // override with external config if specified by environment variable `APP_CONFIG_PATH` const externalConfigPath = process.env.APP_CONFIG_PATH; - if (!!externalConfigPath) { + if (isNotEmpty(externalConfigPath)) { if (fs.existsSync(externalConfigPath)) { overrideWithConfig(appConfig, externalConfigPath); } else { @@ -136,6 +163,18 @@ export const buildAppConfig = (destConfigPath: string): AppConfig => { // override with environment variables overrideWithEnvironment(appConfig); + // apply existing non convention UI environment variables + appConfig.ui.host = isNotEmpty(process.env.DSPACE_HOST) ? process.env.DSPACE_HOST : appConfig.ui.host; + appConfig.ui.port = isNotEmpty(process.env.DSPACE_PORT) ? getNumberFromString(process.env.DSPACE_PORT) : appConfig.ui.port; + appConfig.ui.nameSpace = isNotEmpty(process.env.DSPACE_NAMESPACE) ? process.env.DSPACE_NAMESPACE : appConfig.ui.nameSpace; + appConfig.ui.ssl = isNotEmpty(process.env.DSPACE_SSL) ? getBooleanFromString(process.env.DSPACE_SSL) : appConfig.ui.ssl; + + // apply existing non convention REST environment variables + appConfig.rest.host = isNotEmpty(process.env.DSPACE_REST_HOST) ? process.env.DSPACE_REST_HOST : appConfig.rest.host; + appConfig.rest.port = isNotEmpty(process.env.DSPACE_REST_PORT) ? getNumberFromString(process.env.DSPACE_REST_PORT) : appConfig.rest.port; + appConfig.rest.nameSpace = isNotEmpty(process.env.DSPACE_REST_NAMESPACE) ? process.env.DSPACE_REST_NAMESPACE : appConfig.rest.nameSpace; + appConfig.rest.ssl = isNotEmpty(process.env.DSPACE_REST_SSL) ? getBooleanFromString(process.env.DSPACE_REST_SSL) : appConfig.rest.ssl; + // apply build defined production appConfig.production = env === 'production'; @@ -143,9 +182,14 @@ export const buildAppConfig = (destConfigPath: string): AppConfig => { buildBaseUrl(appConfig.ui); buildBaseUrl(appConfig.rest); - fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); + // extend environment with app config for server side use + extendEnvironmentWithAppConfig(environment, appConfig); - console.log(`Angular ${colors.bold('appConfig.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`); + if (isNotEmpty(destConfigPath)) { + fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); + + console.log(`Angular ${colors.bold('appConfig.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`); + } return appConfig; -} +}; diff --git a/src/config/config.util.ts b/src/config/config.util.ts new file mode 100644 index 0000000000..b576e99eba --- /dev/null +++ b/src/config/config.util.ts @@ -0,0 +1,37 @@ +import * as merge from 'deepmerge'; + +import { environment } from '../environments/environment'; + +import { hasNoValue } from '../app/shared/empty.util'; + +import { AppConfig } from './app-config.interface'; +import { ThemeConfig } from './theme.model'; + +const extendEnvironmentWithAppConfig = (env: any, appConfig: AppConfig): void => { + extendConfig(env, appConfig); + console.log(`Environment extended with app config`); +}; + +const extendConfig = (config: any, appConfig: AppConfig): void => { + const mergeOptions = { + arrayMerge: (destinationArray, sourceArray, options) => sourceArray + }; + Object.assign(config, merge.all([ + config, + appConfig + ], mergeOptions)); +}; + +const getDefaultThemeConfig = (): ThemeConfig => { + return environment.themes.find((themeConfig: any) => + hasNoValue(themeConfig.regex) && + hasNoValue(themeConfig.handle) && + hasNoValue(themeConfig.uuid) + ); +}; + +export { + extendEnvironmentWithAppConfig, + extendConfig, + getDefaultThemeConfig +}; diff --git a/src/environments/environment.ci.ts b/src/environments/environment.ci.ts new file mode 100644 index 0000000000..bb12eb14c1 --- /dev/null +++ b/src/environments/environment.ci.ts @@ -0,0 +1,294 @@ +import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { RestRequestMethod } from '../app/core/data/rest-request-method'; +import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; +import { AppConfig } from '../config/app-config.interface'; + +export const environment: AppConfig = { + production: true, + + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false + }, + + // Angular Universal server settings. + // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. + ui: { + ssl: false, + host: 'localhost', + port: 4000, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/', + baseUrl: 'http://localhost:4000/', + + // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). + rateLimiter: { + windowMs: 1 * 60 * 1000, // 1 minute + max: 500 // limit each IP to 500 requests per windowMs + } + }, + + // The REST API server settings. + // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. + rest: { + ssl: false, + host: 'localhost', + port: 8080, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/server', + baseUrl: 'http://localhost:8080/server' + }, + + // Caching settings + cache: { + // NOTE: how long should objects be cached for by default + msToLive: { + default: 15 * 60 * 1000 // 15 minutes + }, + control: 'max-age=60', // revalidate browser + autoSync: { + defaultTime: 0, + maxBufferSize: 100, + timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds + } + }, + + // Authentication settings + auth: { + // Authentication UI settings + ui: { + // the amount of time before the idle warning is shown + timeUntilIdle: 15 * 60 * 1000, // 15 minutes + // the amount of time the user has to react after the idle warning is shown before they are logged out. + idleGracePeriod: 5 * 60 * 1000 // 5 minutes + }, + // Authentication REST settings + rest: { + // If the rest token expires in less than this amount of time, it will be refreshed automatically. + // This is independent from the idle warning. + timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes + } + }, + + // Form settings + form: { + // NOTE: Map server-side validators to comparative Angular form validators + validatorMap: { + required: 'required', + regex: 'pattern' + } + }, + + // Notifications + notifications: { + rtl: false, + position: ['top', 'right'], + maxStack: 8, + // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically + timeOut: 5000, // 5 second + clickToClose: true, + // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' + animate: NotificationAnimationsType.Scale + }, + + // Submission settings + submission: { + autosave: { + // NOTE: which metadata trigger an autosave + metadata: [], + /** + * NOTE: after how many time (milliseconds) submission is saved automatically + * eg. timer: 5 * (1000 * 60); // 5 minutes + */ + timer: 0 + }, + icons: { + metadata: [ + /** + * NOTE: example of configuration + * { + * // NOTE: metadata name + * name: 'dc.author', + * // NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used + * style: 'fa-user' + * } + */ + { + name: 'dc.author', + style: 'fas fa-user' + }, + // default configuration + { + name: 'default', + style: '' + } + ], + authority: { + confidence: [ + /** + * NOTE: example of configuration + * { + * // NOTE: confidence value + * value: 'dc.author', + * // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used + * style: 'fa-user' + * } + */ + { + value: 600, + style: 'text-success' + }, + { + value: 500, + style: 'text-info' + }, + { + value: 400, + style: 'text-warning' + }, + // default configuration + { + value: 'default', + style: 'text-muted' + } + + ] + } + } + }, + + // NOTE: will log all redux actions and transfers in console + debug: false, + + // Default Language in which the UI will be rendered if the user's browser language is not an active language + defaultLanguage: 'en', + + // Languages. DSpace Angular holds a message catalog for each of the following languages. + // When set to active, users will be able to switch to the use of this language in the user interface. + languages: [ + { code: 'en', label: 'English', active: true }, + { code: 'cs', label: 'Čeština', active: true }, + { code: 'de', label: 'Deutsch', active: true }, + { code: 'es', label: 'Español', active: true }, + { code: 'fr', label: 'Français', active: true }, + { code: 'lv', label: 'Latviešu', active: true }, + { code: 'hu', label: 'Magyar', active: true }, + { code: 'nl', label: 'Nederlands', active: true }, + { code: 'pt-PT', label: 'Português', active: true }, + { code: 'pt-BR', label: 'Português do Brasil', active: true }, + { code: 'fi', label: 'Suomi', active: true } + ], + + // Browse-By Pages + browseBy: { + // Amount of years to display using jumps of one year (current year - oneYearLimit) + oneYearLimit: 10, + // Limit for years to display using jumps of five years (current year - fiveYearLimit) + fiveYearLimit: 30, + // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) + defaultLowerLimit: 1900, + // List of all the active Browse-By types + // Adding a type will activate their Browse-By page and add them to the global navigation menu, + // as well as community and collection pages + // Allowed fields and their purpose: + // id: The browse id to use for fetching info from the rest api + // type: The type of Browse-By page to display + // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') + types: [ + { + id: 'title', + type: BrowseByType.Title, + }, + { + id: 'dateissued', + type: BrowseByType.Date, + metadataField: 'dc.date.issued' + }, + { + id: 'author', + type: BrowseByType.Metadata + }, + { + id: 'subject', + type: BrowseByType.Metadata + } + ] + }, + + // Item Page Config + item: { + edit: { + undoTimeout: 10000 // 10 seconds + } + }, + + // Collection Page Config + collection: { + edit: { + undoTimeout: 10000 // 10 seconds + } + }, + + // Theme Config + themes: [ + // Add additional themes here. In the case where multiple themes match a route, the first one + // in this list will get priority. It is advisable to always have a theme that matches + // every route as the last one + + // { + // // A theme with a handle property will match the community, collection or item with the given + // // handle, and all collections and/or items within it + // name: 'custom', + // handle: '10673/1233' + // }, + // { + // // A theme with a regex property will match the route using a regular expression. If it + // // matches the route for a community or collection it will also apply to all collections + // // and/or items within it + // name: 'custom', + // regex: 'collections\/e8043bc2.*' + // }, + // { + // // A theme with a uuid property will match the community, collection or item with the given + // // ID, and all collections and/or items within it + // name: 'custom', + // uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' + // }, + // { + // // The extends property specifies an ancestor theme (by name). Whenever a themed component is not found + // // in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. + // name: 'custom-A', + // extends: 'custom-B', + // // Any of the matching properties above can be used + // handle: '10673/34' + // }, + // { + // name: 'custom-B', + // extends: 'custom', + // handle: '10673/12' + // }, + // { + // // A theme with only a name will match every route + // name: 'custom' + // }, + // { + // // This theme will use the default bootstrap styling for DSpace components + // name: BASE_THEME_NAME + // }, + + { + // The default dspace theme + name: 'dspace' + } + ], + + // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). + // For images, this enables a gallery viewer where you can zoom or page through images. + // For videos, this enables embedded video streaming + mediaViewer: { + image: false, + video: false + } +}; diff --git a/src/environments/environment.common.ts b/src/environments/environment.prod.ts similarity index 84% rename from src/environments/environment.common.ts rename to src/environments/environment.prod.ts index 6bb2c8dc51..63a9e01a2e 100644 --- a/src/environments/environment.common.ts +++ b/src/environments/environment.prod.ts @@ -1,47 +1,60 @@ -import { AppConfig } from '../config/app-config.interface'; -import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; +import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; +import { AppConfig } from '../config/app-config.interface'; export const environment: AppConfig = { production: true, + + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false + }, + // Angular Universal server settings. - // NOTE: these must be "synced" with the 'dspace.ui.url' setting in your backend's local.cfg. + // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. ui: { ssl: false, host: 'localhost', port: 4000, // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript nameSpace: '/', - // The rateLimiter settings limit each IP to a "max" of 500 requests per "windowMs" (1 minute). + baseUrl: 'http://localhost:4000/', + + // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). rateLimiter: { - windowMs: 1 * 60 * 1000, // 1 minute + windowMs: 1 * 60 * 1000, // 1 minute max: 500 // limit each IP to 500 requests per windowMs } }, + // The REST API server settings. - // NOTE: these must be "synced" with the 'dspace.server.url' setting in your backend's local.cfg. + // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. rest: { ssl: true, host: 'api7.dspace.org', port: 443, // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript nameSpace: '/server', + baseUrl: 'https://api7.dspace.org/server' }, + // Caching settings cache: { // NOTE: how long should objects be cached for by default msToLive: { - default: 15 * 60 * 1000, // 15 minutes + default: 15 * 60 * 1000 // 15 minutes }, - // msToLive: 1000, // 15 minutes control: 'max-age=60', // revalidate browser autoSync: { defaultTime: 0, maxBufferSize: 100, - timePerMethod: {[RestRequestMethod.PATCH]: 3} as any // time in seconds + timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds } }, + // Authentication settings auth: { // Authentication UI settings @@ -49,15 +62,16 @@ export const environment: AppConfig = { // the amount of time before the idle warning is shown timeUntilIdle: 15 * 60 * 1000, // 15 minutes // the amount of time the user has to react after the idle warning is shown before they are logged out. - idleGracePeriod: 5 * 60 * 1000, // 5 minutes + idleGracePeriod: 5 * 60 * 1000 // 5 minutes }, // Authentication REST settings rest: { // If the rest token expires in less than this amount of time, it will be refreshed automatically. // This is independent from the idle warning. - timeLeftBeforeTokenRefresh: 2 * 60 * 1000, // 2 minutes - }, + timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes + } }, + // Form settings form: { // NOTE: Map server-side validators to comparative Angular form validators @@ -66,6 +80,7 @@ export const environment: AppConfig = { regex: 'pattern' } }, + // Notifications notifications: { rtl: false, @@ -77,6 +92,7 @@ export const environment: AppConfig = { // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' animate: NotificationAnimationsType.Scale }, + // Submission settings submission: { autosave: { @@ -136,69 +152,35 @@ export const environment: AppConfig = { { value: 'default', style: 'text-muted' - }, + } ] } } }, - // Angular Universal settings - universal: { - preboot: true, - async: true, - time: false - }, + // NOTE: will log all redux actions and transfers in console debug: false, + // Default Language in which the UI will be rendered if the user's browser language is not an active language defaultLanguage: 'en', + // Languages. DSpace Angular holds a message catalog for each of the following languages. // When set to active, users will be able to switch to the use of this language in the user interface. - languages: [{ - code: 'en', - label: 'English', - active: true, - }, { - code: 'cs', - label: 'Čeština', - active: true, - }, { - code: 'de', - label: 'Deutsch', - active: true, - }, { - code: 'es', - label: 'Español', - active: true, - }, { - code: 'fr', - label: 'Français', - active: true, - }, { - code: 'lv', - label: 'Latviešu', - active: true, - }, { - code: 'hu', - label: 'Magyar', - active: true, - }, { - code: 'nl', - label: 'Nederlands', - active: true, - }, { - code: 'pt-PT', - label: 'Português', - active: true, - },{ - code: 'pt-BR', - label: 'Português do Brasil', - active: true, - },{ - code: 'fi', - label: 'Suomi', - active: true, - }], + languages: [ + { code: 'en', label: 'English', active: true }, + { code: 'cs', label: 'Čeština', active: true }, + { code: 'de', label: 'Deutsch', active: true }, + { code: 'es', label: 'Español', active: true }, + { code: 'fr', label: 'Français', active: true }, + { code: 'lv', label: 'Latviešu', active: true }, + { code: 'hu', label: 'Magyar', active: true }, + { code: 'nl', label: 'Nederlands', active: true }, + { code: 'pt-PT', label: 'Português', active: true }, + { code: 'pt-BR', label: 'Português do Brasil', active: true }, + { code: 'fi', label: 'Suomi', active: true } + ], + // Browse-By Pages browseBy: { // Amount of years to display using jumps of one year (current year - oneYearLimit) @@ -234,16 +216,22 @@ export const environment: AppConfig = { } ] }, + + // Item Page Config item: { edit: { undoTimeout: 10000 // 10 seconds } }, + + // Collection Page Config collection: { edit: { undoTimeout: 10000 // 10 seconds } }, + + // Theme Config themes: [ // Add additional themes here. In the case where multiple themes match a route, the first one // in this list will get priority. It is advisable to always have a theme that matches @@ -274,12 +262,12 @@ export const environment: AppConfig = { // name: 'custom-A', // extends: 'custom-B', // // Any of the matching properties above can be used - // handle: '10673/34', + // handle: '10673/34' // }, // { // name: 'custom-B', // extends: 'custom', - // handle: '10673/12', + // handle: '10673/12' // }, // { // // A theme with only a name will match every route @@ -293,13 +281,14 @@ export const environment: AppConfig = { { // The default dspace theme name: 'dspace' - }, + } ], - // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with "image" or "video"). + + // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). // For images, this enables a gallery viewer where you can zoom or page through images. // For videos, this enables embedded video streaming mediaViewer: { image: false, - video: false, - }, + video: false + } }; diff --git a/src/environments/environment.template.ts b/src/environments/environment.template.ts deleted file mode 100644 index 6f6510e1ab..0000000000 --- a/src/environments/environment.template.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const environment = { - /** - * TODO add the sections from environment.common.ts you want to override here - * e.g. - * rest: { - * host: 'rest.api', - * nameSpace: '/rest', - * } - */ -}; diff --git a/src/environments/mock-environment.ts b/src/environments/environment.test.ts similarity index 90% rename from src/environments/mock-environment.ts rename to src/environments/environment.test.ts index 02c1012c68..394a2e5dac 100644 --- a/src/environments/mock-environment.ts +++ b/src/environments/environment.test.ts @@ -1,46 +1,66 @@ -// This configuration is only used for unit tests, end-to-end tests use environment.prod.ts +// This configuration is only used for unit tests, end-to-end tests use environment.e2e.ts import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { AppConfig } from '../config/app-config.interface'; -export const environment: Partial = { - rest: { - ssl: true, - host: 'rest.com', - port: 443, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/api', - baseUrl: 'https://rest.api/' +export const environment: AppConfig = { + production: false, + + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false }, + + // Angular Universal server settings. ui: { ssl: false, host: 'dspace.com', port: 80, // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript nameSpace: '/angular-dspace', - rateLimiter: undefined + baseUrl: 'http://dspace.com/angular-dspace', + // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). + rateLimiter: { + windowMs: 1 * 60 * 1000, // 1 minute + max: 500 // limit each IP to 500 requests per windowMs + } }, - // Caching settings + + // The REST API server settings. + rest: { + ssl: true, + host: 'rest.com', + port: 443, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/api', + baseUrl: 'https://rest.com/api' + }, + + // Caching settings cache: { // NOTE: how long should objects be cached for by default msToLive: { default: 15 * 60 * 1000, // 15 minutes }, // msToLive: 1000, // 15 minutes - control: 'max-age=60', // revalidate browser + control: 'max-age=60', autoSync: { defaultTime: 0, maxBufferSize: 100, - timePerMethod: {[RestRequestMethod.PATCH]: 3} as any // time in seconds + timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds } }, + // Authentication settings auth: { // Authentication UI settings ui: { // the amount of time before the idle warning is shown - timeUntilIdle: 20000, // 20 sec + timeUntilIdle: 20000, + // the amount of time the user has to react after the idle warning is shown before they are logged out. idleGracePeriod: 20000, // 20 sec }, @@ -51,6 +71,7 @@ export const environment: Partial = { timeLeftBeforeTokenRefresh: 20000, // 20 sec }, }, + // Form settings form: { // NOTE: Map server-side validators to comparative Angular form validators @@ -59,17 +80,19 @@ export const environment: Partial = { regex: 'pattern' } }, + // Notifications notifications: { rtl: false, position: ['top', 'right'], maxStack: 8, // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically - timeOut: 5000, // 5 second + timeOut: 5000, clickToClose: true, // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' animate: NotificationAnimationsType.Scale }, + // Submission settings submission: { autosave: { @@ -115,21 +138,17 @@ export const environment: Partial = { value: 'default', style: 'text-muted' }, - ] } } }, - // Angular Universal settings - universal: { - preboot: true, - async: true, - time: false - }, + // NOTE: will log all redux actions and transfers in console debug: false, + // Default Language in which the UI will be rendered if the user's browser language is not an active language defaultLanguage: 'en', + // Languages. DSpace Angular holds a message catalog for each of the following languages. // When set to active, users will be able to switch to the use of this language in the user interface. languages: [{ @@ -161,6 +180,7 @@ export const environment: Partial = { label: 'Latviešu', active: true, }], + // Browse-By Pages browseBy: { // Amount of years to display using jumps of one year (current year - oneYearLimit) @@ -234,5 +254,5 @@ export const environment: Partial = { mediaViewer: { image: true, video: true - }, + } }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000000..ba7b262ac4 --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,309 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --configuration production` replaces `environment.ts` with `environment.prod.ts`. +// `ng build --configuration ci` replaces `environment.ts` with `environment.ci.ts`. +// `ng test --configuration test` replaces `environment.ts` with `environment.test.ts`. +// The list of file replacements can be found in `angular.json`. + +import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { RestRequestMethod } from '../app/core/data/rest-request-method'; +import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; +import { AppConfig } from '../config/app-config.interface'; + +export const environment: AppConfig = { + production: false, + + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false + }, + + // Angular Universal server settings. + // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. + ui: { + ssl: false, + host: 'localhost', + port: 4000, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/', + baseUrl: 'http://localhost:4000/', + + // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). + rateLimiter: { + windowMs: 1 * 60 * 1000, // 1 minute + max: 500 // limit each IP to 500 requests per windowMs + } + }, + + // The REST API server settings. + // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. + rest: { + ssl: true, + host: 'api7.dspace.org', + port: 443, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/server', + baseUrl: 'https://api7.dspace.org/server' + }, + + // Caching settings + cache: { + // NOTE: how long should objects be cached for by default + msToLive: { + default: 15 * 60 * 1000 // 15 minutes + }, + control: 'max-age=60', // revalidate browser + autoSync: { + defaultTime: 0, + maxBufferSize: 100, + timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds + } + }, + + // Authentication settings + auth: { + // Authentication UI settings + ui: { + // the amount of time before the idle warning is shown + timeUntilIdle: 15 * 60 * 1000, // 15 minutes + // the amount of time the user has to react after the idle warning is shown before they are logged out. + idleGracePeriod: 5 * 60 * 1000 // 5 minutes + }, + // Authentication REST settings + rest: { + // If the rest token expires in less than this amount of time, it will be refreshed automatically. + // This is independent from the idle warning. + timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes + } + }, + + // Form settings + form: { + // NOTE: Map server-side validators to comparative Angular form validators + validatorMap: { + required: 'required', + regex: 'pattern' + } + }, + + // Notifications + notifications: { + rtl: false, + position: ['top', 'right'], + maxStack: 8, + // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically + timeOut: 5000, // 5 second + clickToClose: true, + // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' + animate: NotificationAnimationsType.Scale + }, + + // Submission settings + submission: { + autosave: { + // NOTE: which metadata trigger an autosave + metadata: [], + /** + * NOTE: after how many time (milliseconds) submission is saved automatically + * eg. timer: 5 * (1000 * 60); // 5 minutes + */ + timer: 0 + }, + icons: { + metadata: [ + /** + * NOTE: example of configuration + * { + * // NOTE: metadata name + * name: 'dc.author', + * // NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used + * style: 'fa-user' + * } + */ + { + name: 'dc.author', + style: 'fas fa-user' + }, + // default configuration + { + name: 'default', + style: '' + } + ], + authority: { + confidence: [ + /** + * NOTE: example of configuration + * { + * // NOTE: confidence value + * value: 'dc.author', + * // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used + * style: 'fa-user' + * } + */ + { + value: 600, + style: 'text-success' + }, + { + value: 500, + style: 'text-info' + }, + { + value: 400, + style: 'text-warning' + }, + // default configuration + { + value: 'default', + style: 'text-muted' + } + + ] + } + } + }, + + // NOTE: will log all redux actions and transfers in console + debug: false, + + // Default Language in which the UI will be rendered if the user's browser language is not an active language + defaultLanguage: 'en', + + // Languages. DSpace Angular holds a message catalog for each of the following languages. + // When set to active, users will be able to switch to the use of this language in the user interface. + languages: [ + { code: 'en', label: 'English', active: true }, + { code: 'cs', label: 'Čeština', active: true }, + { code: 'de', label: 'Deutsch', active: true }, + { code: 'es', label: 'Español', active: true }, + { code: 'fr', label: 'Français', active: true }, + { code: 'lv', label: 'Latviešu', active: true }, + { code: 'hu', label: 'Magyar', active: true }, + { code: 'nl', label: 'Nederlands', active: true }, + { code: 'pt-PT', label: 'Português', active: true }, + { code: 'pt-BR', label: 'Português do Brasil', active: true }, + { code: 'fi', label: 'Suomi', active: true } + ], + + // Browse-By Pages + browseBy: { + // Amount of years to display using jumps of one year (current year - oneYearLimit) + oneYearLimit: 10, + // Limit for years to display using jumps of five years (current year - fiveYearLimit) + fiveYearLimit: 30, + // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) + defaultLowerLimit: 1900, + // List of all the active Browse-By types + // Adding a type will activate their Browse-By page and add them to the global navigation menu, + // as well as community and collection pages + // Allowed fields and their purpose: + // id: The browse id to use for fetching info from the rest api + // type: The type of Browse-By page to display + // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') + types: [ + { + id: 'title', + type: BrowseByType.Title, + }, + { + id: 'dateissued', + type: BrowseByType.Date, + metadataField: 'dc.date.issued' + }, + { + id: 'author', + type: BrowseByType.Metadata + }, + { + id: 'subject', + type: BrowseByType.Metadata + } + ] + }, + + // Item Page Config + item: { + edit: { + undoTimeout: 10000 // 10 seconds + } + }, + + // Collection Page Config + collection: { + edit: { + undoTimeout: 10000 // 10 seconds + } + }, + + // Theme Config + themes: [ + // Add additional themes here. In the case where multiple themes match a route, the first one + // in this list will get priority. It is advisable to always have a theme that matches + // every route as the last one + + // { + // // A theme with a handle property will match the community, collection or item with the given + // // handle, and all collections and/or items within it + // name: 'custom', + // handle: '10673/1233' + // }, + // { + // // A theme with a regex property will match the route using a regular expression. If it + // // matches the route for a community or collection it will also apply to all collections + // // and/or items within it + // name: 'custom', + // regex: 'collections\/e8043bc2.*' + // }, + // { + // // A theme with a uuid property will match the community, collection or item with the given + // // ID, and all collections and/or items within it + // name: 'custom', + // uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' + // }, + // { + // // The extends property specifies an ancestor theme (by name). Whenever a themed component is not found + // // in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. + // name: 'custom-A', + // extends: 'custom-B', + // // Any of the matching properties above can be used + // handle: '10673/34' + // }, + // { + // name: 'custom-B', + // extends: 'custom', + // handle: '10673/12' + // }, + // { + // // A theme with only a name will match every route + // name: 'custom' + // }, + // { + // // This theme will use the default bootstrap styling for DSpace components + // name: BASE_THEME_NAME + // }, + + { + // The default dspace theme + name: 'dspace' + } + ], + + // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). + // For images, this enables a gallery viewer where you can zoom or page through images. + // For videos, this enables embedded video streaming + mediaViewer: { + image: false, + video: false + } +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/src/main.browser.ts b/src/main.browser.ts index d6c3fd6535..5a17c3e7cc 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -1,22 +1,25 @@ import 'zone.js/dist/zone'; import 'reflect-metadata'; import 'core-js/es/reflect'; + import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { bootloader } from '@angularclass/bootloader'; - import { load as loadWebFont } from 'webfontloader'; + import { hasValue } from './app/shared/empty.util'; import { BrowserAppModule } from './modules/app/browser-app.module'; import { environment } from './environments/environment'; +// import { AppConfig, APP_CONFIG } from './config/app-config.interface'; +// import { extendEnvironmentWithAppConfig } from './config/config.util'; + if (environment.production) { enableProdMode(); } -export function main() { +const main = () => { // Load fonts async // https://github.com/typekit/webfontloader#configuration loadWebFont({ @@ -25,13 +28,23 @@ export function main() { } }); - return platformBrowserDynamic().bootstrapModule(BrowserAppModule, {preserveWhitespaces:true}); -} + return platformBrowserDynamic() + .bootstrapModule(BrowserAppModule, { + preserveWhitespaces: true + }); +}; // support async tag or hmr if (hasValue(environment.universal) && environment.universal.preboot === false) { - bootloader(main); + main(); } else { - document.addEventListener('DOMContentLoaded', () => bootloader(main)); + document.addEventListener('DOMContentLoaded', main); } + +// fetch('assets/appConfig.json') +// .then((response) => response.json()) +// .then((appConfig: AppConfig) => { +// // extend environment with app config for client side use +// extendEnvironmentWithAppConfig(environment, appConfig); +// }); diff --git a/webpack/webpack.browser.ts b/webpack/webpack.browser.ts index 3c1d01dda0..187262071e 100644 --- a/webpack/webpack.browser.ts +++ b/webpack/webpack.browser.ts @@ -1,6 +1,7 @@ -import { buildAppConfig } from '../src/config/config'; +// import { join } from 'path'; + +// import { buildAppConfig } from '../src/config/config.server'; import { commonExports } from './webpack.common'; -import { join } from 'path'; module.exports = Object.assign({}, commonExports, { target: 'web', @@ -9,7 +10,7 @@ module.exports = Object.assign({}, commonExports, { }, devServer: { before(app, server) { - buildAppConfig(join(process.cwd(), 'src/assets/appConfig.json')); + // buildAppConfig(join(process.cwd(), 'src/assets/appConfig.json')); } } }); diff --git a/yarn.lock b/yarn.lock index fdb758af01..e8e9b67c4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -427,11 +427,6 @@ dependencies: tslib "^2.0.0" -"@angularclass/bootloader@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@angularclass/bootloader/-/bootloader-1.0.1.tgz#75de7cf3901b445900a419c2aeca44181d465060" - integrity sha1-dd5885AbRFkApBnCrspEGB1GUGA= - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.8.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" @@ -1941,18 +1936,6 @@ semver "7.3.4" semver-intersect "1.4.0" -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2630,13 +2613,6 @@ angulartics2@^10.0.0: dependencies: tslib "^2.0.0" -ansi-align@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== - dependencies: - string-width "^4.1.0" - ansi-colors@4.1.1, ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -3201,20 +3177,6 @@ bootstrap@4.3.1: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.3.1.tgz#280ca8f610504d99d7b6b4bfc4b68cec601704ac" integrity sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag== -boxen@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" - integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.2" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3582,19 +3544,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - cachedir@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" @@ -3774,11 +3723,6 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - ci-info@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" @@ -3834,11 +3778,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -3918,13 +3857,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -4153,18 +4085,6 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== - dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" - connect-history-api-fallback@^1, connect-history-api-fallback@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" @@ -4470,7 +4390,7 @@ critters@0.0.7: parse5-htmlparser2-tree-adapter "^6.0.1" pretty-bytes "^5.3.0" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -4512,11 +4432,6 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - css-blank-pseudo@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" @@ -5032,7 +4947,7 @@ debug@4.3.1: dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6, debug@^3.2.7: +debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -5068,13 +4983,6 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -5087,11 +4995,6 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-freeze@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" @@ -5129,11 +5032,6 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -5451,11 +5349,6 @@ dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -5769,11 +5662,6 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -6492,7 +6380,7 @@ get-stdin@^8.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== -get-stream@^4.0.0, get-stream@^4.1.0: +get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== @@ -6636,23 +6524,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" @@ -6772,11 +6643,6 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== - has@^1.0.0, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -6827,11 +6693,6 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - hosted-git-info@^3.0.6: version "3.0.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" @@ -6953,7 +6814,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: +http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -7168,11 +7029,6 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -ignore-by-default@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" - integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= - ignore-walk@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" @@ -7249,11 +7105,6 @@ import-from@^3.0.0: dependencies: resolve-from "^5.0.0" -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -7323,7 +7174,7 @@ ini@2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -ini@^1.3.4, ini@~1.3.0: +ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7497,13 +7348,6 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - is-ci@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" @@ -7632,7 +7476,7 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= -is-installed-globally@^0.4.0, is-installed-globally@~0.4.0: +is-installed-globally@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== @@ -7662,11 +7506,6 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== -is-npm@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" - integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== - is-number-like@^1.0.3: version "1.0.8" resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3" @@ -7805,7 +7644,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -7844,11 +7683,6 @@ is-wsl@^2.1.1, is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== - isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -8077,11 +7911,6 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -8358,13 +8187,6 @@ karma@^5.2.3: ua-parser-js "0.7.22" yargs "^15.3.1" -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -8417,13 +8239,6 @@ last-call-webpack-plugin@^3.0.0: lodash "^4.17.5" webpack-sources "^1.1.0" -latest-version@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" - lazy-ass@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" @@ -8553,16 +8368,6 @@ listr2@^3.8.3: through "^2.3.8" wrap-ansi "^7.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -8749,16 +8554,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -8904,11 +8699,6 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" -memorystream@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" - integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -9008,11 +8798,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - mini-css-extract-plugin@0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.0.tgz#a0e6bfcad22a9c73f6c882a3c7557a98e2d3d27d" @@ -9457,22 +9242,6 @@ node-releases@^2.0.1: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -nodemon@^2.0.2: - version "2.0.15" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.15.tgz#504516ce3b43d9dc9a955ccd9ec57550a31a8d4e" - integrity sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA== - dependencies: - chokidar "^3.5.2" - debug "^3.2.7" - ignore-by-default "^1.0.1" - minimatch "^3.0.4" - pstree.remy "^1.1.8" - semver "^5.7.1" - supports-color "^5.5.0" - touch "^3.1.0" - undefsafe "^2.0.5" - update-notifier "^5.1.0" - nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -9480,23 +9249,6 @@ nopt@^5.0.0: dependencies: abbrev "1" -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= - dependencies: - abbrev "1" - -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -9529,7 +9281,7 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -normalize-url@^4.1.0, normalize-url@^4.5.0: +normalize-url@^4.5.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== @@ -9624,21 +9376,6 @@ npm-registry-fetch@^9.0.0: minizlib "^2.0.0" npm-package-arg "^8.0.0" -npm-run-all@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" - integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== - dependencies: - ansi-styles "^3.2.1" - chalk "^2.4.1" - cross-spawn "^6.0.5" - memorystream "^0.3.1" - minimatch "^3.0.4" - pidtree "^0.3.0" - read-pkg "^3.0.0" - shell-quote "^1.6.1" - string.prototype.padend "^3.0.0" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -9937,11 +9674,6 @@ ospath@^1.2.2: resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - p-event@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" @@ -10028,16 +9760,6 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== - dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - pacote@11.2.4: version "11.2.4" resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.2.4.tgz#dc7ca740a573ed86a3bf863511d22c1d413ec82f" @@ -10236,13 +9958,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -10294,21 +10009,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -pidtree@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" - integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== - pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -11362,11 +11067,6 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - pretty-bytes@^5.3.0, pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -11487,11 +11187,6 @@ psl@^1.1.28, psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== -pstree.remy@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" - integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== - public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" @@ -11544,13 +11239,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pupa@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== - dependencies: - escape-goat "^2.0.0" - q@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" @@ -11680,16 +11368,6 @@ raw-loader@4.0.2: loader-utils "^2.0.0" schema-utils "^3.0.0" -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - re-reselect@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-4.0.0.tgz#9ddec4c72c4d952f68caa5aa4b76a9ed38b75cac" @@ -11926,15 +11604,6 @@ read-package-json-fast@^2.0.1: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" @@ -12076,20 +11745,6 @@ regexpu-core@^4.7.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== - dependencies: - rc "^1.2.8" - -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - regjsgen@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" @@ -12260,7 +11915,7 @@ resolve@1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" -resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.8.1, resolve@^1.9.0: +resolve@^1.1.7, resolve@^1.3.2, resolve@^1.8.1, resolve@^1.9.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -12276,13 +11931,6 @@ resp-modifier@6.0.2: debug "^2.2.0" minimatch "^3.0.2" -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -12641,13 +12289,6 @@ selfsigned@^1.10.11, selfsigned@^1.10.7, selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== - dependencies: - semver "^6.3.0" - semver-dsl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/semver-dsl/-/semver-dsl-1.0.1.tgz#d3678de5555e8a61f629eed025366ae5f27340a0" @@ -12662,11 +12303,6 @@ semver-intersect@1.4.0: dependencies: semver "^5.0.0" -"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" @@ -12684,7 +12320,12 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^5.0.0, semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -12875,11 +12516,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.6.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -13221,32 +12857,6 @@ sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== - spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -13450,7 +13060,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13468,15 +13078,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.padend@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz#997a6de12c92c7cb34dc8a201a6c53d9bd88a5f1" - integrity sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -13542,11 +13143,6 @@ strip-ansi@^7.0.0: dependencies: ansi-regex "^6.0.1" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -13557,11 +13153,6 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - style-loader@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" @@ -13634,7 +13225,7 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: +supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -13938,11 +13529,6 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -13988,13 +13574,6 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== -touch@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" - integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== - dependencies: - nopt "~1.0.10" - tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -14143,11 +13722,6 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -14171,13 +13745,6 @@ type@^2.5.0: resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -14242,11 +13809,6 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" -undefsafe@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" - integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== - unfetch@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" @@ -14309,13 +13871,6 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - universal-analytics@0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" @@ -14363,26 +13918,6 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-notifier@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" - integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== - dependencies: - boxen "^5.0.0" - chalk "^4.1.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.4.0" - is-npm "^5.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.1.0" - pupa "^2.1.1" - semver "^7.3.4" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -14395,13 +13930,6 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - url-parse@^1.4.3, url-parse@^1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" @@ -14490,14 +14018,6 @@ uuid@^3.0.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - validate-npm-package-name@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" @@ -15045,13 +14565,6 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2 || 3 || 4" -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" @@ -15108,16 +14621,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - ws@^6.2.1: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" @@ -15140,11 +14643,6 @@ ws@~7.4.2: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== - xhr2@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.2.1.tgz#4e73adc4f9cfec9cbd2157f73efdce3a5f108a93" From 33488ccf40052ee57a3d24ff438c818cfe7dd8ae Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 6 Dec 2021 16:54:55 -0600 Subject: [PATCH 016/102] extend environment and use injected app config --- .github/workflows/build.yml | 2 +- angular.json | 26 - nodemon.json | 6 + package.json | 16 +- scripts/serve.ts | 14 +- scripts/test-rest.ts | 16 +- server.ts | 31 +- src/app/app.component.spec.ts | 3 + src/app/app.component.ts | 13 +- src/app/app.module.ts | 24 +- .../item-types/shared/item.component.ts | 3 +- src/app/root/root.component.ts | 3 +- src/config/app-config.interface.ts | 6 +- src/config/config.server.ts | 7 +- src/config/default-app-config.ts | 6 +- src/environments/environment.ci.ts | 294 ------- src/environments/environment.prod.ts | 284 +------ src/environments/environment.test.ts | 2 +- src/environments/environment.ts | 285 +------ src/main.browser.ts | 40 +- src/modules/app/browser-app.module.ts | 35 +- src/modules/app/server-app.module.ts | 28 +- tslint.json | 4 - webpack/webpack.browser.ts | 6 +- yarn.lock | 791 +++++++++++++----- 25 files changed, 742 insertions(+), 1203 deletions(-) create mode 100644 nodemon.json delete mode 100644 src/environments/environment.ci.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab537f80cb..7758020724 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,7 +73,7 @@ jobs: run: yarn run lint - name: Run build - run: yarn run build:ssr:ci + run: yarn run build:prod - name: Run specs (unit tests) run: yarn run test:headless diff --git a/angular.json b/angular.json index b81d166cea..b9b8cb41cc 100644 --- a/angular.json +++ b/angular.json @@ -94,22 +94,6 @@ "maximumError": "300kb" } ] - }, - "ci": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.ci.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "extractCss": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true } } }, @@ -222,16 +206,6 @@ "with": "src/environments/environment.prod.ts" } ] - }, - "ci": { - "sourceMap": false, - "optimization": true, - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.ci.ts" - } - ] } } }, diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000000..dec8d9724c --- /dev/null +++ b/nodemon.json @@ -0,0 +1,6 @@ +{ + "watch": [ + "config" + ], + "ext": "json" +} diff --git a/package.json b/package.json index 4ce674ce1c..e0b07b1040 100644 --- a/package.json +++ b/package.json @@ -3,24 +3,25 @@ "version": "0.0.0", "scripts": { "ng": "ng", + "config:watch": "nodemon", "test:rest": "ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", "start": "yarn run start:prod", - "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", - "start:dev": "yarn run serve", + "start:dev": "nodemon --exec \"yarn run serve\"", "start:prod": "yarn run build:prod && yarn run serve:ssr", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod", + "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", + "serve:ssr": "node dist/server/main", "analyze": "webpack-bundle-analyzer dist/browser/stats.json", "build": "ng build", "build:stats": "ng build --stats-json", "build:prod": "yarn run build:ssr", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", - "build:ssr:ci": "ng build --configuration ci && ng run dspace-angular:server:ci", - "test": "ng test --sourceMap=true --watch=true --configuration test", + "test": "ng test --sourceMap=true --watch=false --configuration test", + "test:watch": "nodemon --exec \"ng test --sourceMap=true --watch=true --configuration test\"", "test:headless": "ng test --sourceMap=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", "lint": "ng lint", "lint-fix": "ng lint --fix=true", "e2e": "ng e2e", - "serve:ssr": "node dist/server/main", "clean:dev:config": "rimraf src/assets/appConfig.json", "clean:coverage": "rimraf coverage", "clean:dist": "rimraf dist", @@ -86,8 +87,8 @@ "file-saver": "^2.0.5", "filesize": "^6.1.0", "font-awesome": "4.7.0", - "https": "1.0.0", "http-proxy-middleware": "^1.0.5", + "https": "1.0.0", "js-cookie": "2.2.1", "json5": "^2.1.3", "jsonschema": "1.4.0", @@ -162,6 +163,7 @@ "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", + "nodemon": "^2.0.15", "optimize-css-assets-webpack-plugin": "^5.0.4", "postcss-apply": "0.11.0", "postcss-import": "^12.0.1", @@ -186,4 +188,4 @@ "webpack-cli": "^4.2.0", "webpack-dev-server": "^4.5.0" } -} \ No newline at end of file +} diff --git a/scripts/serve.ts b/scripts/serve.ts index 903374c615..bf5506b8bd 100644 --- a/scripts/serve.ts +++ b/scripts/serve.ts @@ -1,16 +1,14 @@ import * as child from 'child_process'; -import { environment } from '../src/environments/environment'; +import { AppConfig } from '../src/config/app-config.interface'; +import { buildAppConfig } from '../src/config/config.server'; -// import { AppConfig } from '../src/config/app-config.interface'; -// import { buildAppConfig } from '../src/config/config.server'; - -// const appConfig: AppConfig = buildAppConfig(); +const appConfig: AppConfig = buildAppConfig(); /** - * Calls `ng serve` with the following arguments configured for the UI in the environment: host, port, nameSpace, ssl + * Calls `ng serve` with the following arguments configured for the UI in the app config: host, port, nameSpace, ssl */ child.spawn( - `ng serve --host ${environment.ui.host} --port ${environment.ui.port} --serve-path ${environment.ui.nameSpace} --ssl ${environment.ui.ssl}`, - { stdio:'inherit', shell: true } + `ng serve --host ${appConfig.ui.host} --port ${appConfig.ui.port} --serve-path ${appConfig.ui.nameSpace} --ssl ${appConfig.ui.ssl}`, + { stdio: 'inherit', shell: true } ); diff --git a/scripts/test-rest.ts b/scripts/test-rest.ts index 1584613387..00455d69b5 100644 --- a/scripts/test-rest.ts +++ b/scripts/test-rest.ts @@ -1,15 +1,13 @@ import * as http from 'http'; import * as https from 'https'; -import { environment } from '../src/environments/environment'; - -// import { AppConfig } from '../src/config/app-config.interface'; -// import { buildAppConfig } from '../src/config/config.server'; +import { AppConfig } from '../src/config/app-config.interface'; +import { buildAppConfig } from '../src/config/config.server'; -// const appConfig: AppConfig = buildAppConfig(); +const appConfig: AppConfig = buildAppConfig(); /** - * Script to test the connection with the configured REST API (in the 'rest' settings of your environment.*.ts) + * Script to test the connection with the configured REST API (in the 'rest' settings of your appConfig.*.json) * * This script is useful to test for any Node.js connection issues with your REST API. * @@ -17,11 +15,11 @@ import { environment } from '../src/environments/environment'; */ // Get root URL of configured REST API -const restUrl = environment.rest.baseUrl + '/api'; +const restUrl = appConfig.rest.baseUrl + '/api'; console.log(`...Testing connection to REST API at ${restUrl}...\n`); // If SSL enabled, test via HTTPS, else via HTTP -if (environment.rest.ssl) { +if (appConfig.rest.ssl) { const req = https.request(restUrl, (res) => { console.log(`RESPONSE: ${res.statusCode} ${res.statusMessage} \n`); res.on('data', (data) => { @@ -61,7 +59,7 @@ function checkJSONResponse(responseData: any): any { console.log(`\t"dspaceVersion" = ${parsedData.dspaceVersion}`); console.log(`\t"dspaceUI" = ${parsedData.dspaceUI}`); console.log(`\t"dspaceServer" = ${parsedData.dspaceServer}`); - console.log(`\t"dspaceServer" property matches UI's "rest" config? ${(parsedData.dspaceServer === environment.rest.baseUrl)}`); + console.log(`\t"dspaceServer" property matches UI's "rest" config? ${(parsedData.dspaceServer === appConfig.rest.baseUrl)}`); // Check for "authn" and "sites" in "_links" section as they should always exist (even if no data)! const linksFound: string[] = Object.keys(parsedData._links); console.log(`\tDoes "/api" endpoint have HAL links ("_links" section)? ${linksFound.includes('authn') && linksFound.includes('sites')}`); diff --git a/server.ts b/server.ts index 9a48a91ffb..70d01e7710 100644 --- a/server.ts +++ b/server.ts @@ -19,29 +19,33 @@ import 'zone.js/dist/zone-node'; import 'reflect-metadata'; import 'rxjs'; -import * as fs from 'fs'; import * as pem from 'pem'; import * as https from 'https'; import * as morgan from 'morgan'; import * as express from 'express'; import * as bodyParser from 'body-parser'; import * as compression from 'compression'; + +import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; +import { APP_BASE_HREF } from '@angular/common'; import { enableProdMode } from '@angular/core'; -import { existsSync } from 'fs'; + import { ngExpressEngine } from '@nguniversal/express-engine'; 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 { APP_BASE_HREF } from '@angular/common'; + 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 { buildAppConfig } from './src/config/config.server'; +import { AppConfig, APP_CONFIG } from './src/config/app-config.interface'; +import { extendEnvironmentWithAppConfig } from './src/config/config.util'; /* * Set path for the browser application's dist folder @@ -54,7 +58,10 @@ const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : ' const cookieParser = require('cookie-parser'); -// const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/appConfig.json')); +const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/appConfig.json')); + +// extend environment with app config for server +extendEnvironmentWithAppConfig(environment, appConfig); // The Express app is exported so that it can be used by serverless Functions. export function app() { @@ -105,10 +112,10 @@ export function app() { provide: RESPONSE, useValue: (options as any).req.res, }, - // { - // provide: APP_CONFIG, - // useValue: appConfig - // } + { + provide: APP_CONFIG, + useValue: environment + } ] })(_, (options as any), callback) ); @@ -246,14 +253,14 @@ function start() { if (environment.ui.ssl) { let serviceKey; try { - serviceKey = fs.readFileSync('./config/ssl/key.pem'); + serviceKey = readFileSync('./config/ssl/key.pem'); } catch (e) { console.warn('Service key not found at ./config/ssl/key.pem'); } let certificate; try { - certificate = fs.readFileSync('./config/ssl/cert.pem'); + certificate = readFileSync('./config/ssl/cert.pem'); } catch (e) { console.warn('Certificate not found at ./config/ssl/key.pem'); } diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 3f2dc45ce7..d33bade8c2 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -36,6 +36,8 @@ import { GoogleAnalyticsService } from './statistics/google-analytics.service'; import { ThemeService } from './shared/theme-support/theme.service'; import { getMockThemeService } from './shared/mocks/theme-service.mock'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; +import { APP_CONFIG } from '../config/app-config.interface'; +import { environment } from '../environments/environment'; let comp: AppComponent; let fixture: ComponentFixture; @@ -83,6 +85,7 @@ describe('App component', () => { { provide: LocaleService, useValue: getMockLocaleService() }, { provide: ThemeService, useValue: getMockThemeService() }, { provide: BreadcrumbsService, useValue: breadcrumbsServiceSpy }, + { provide: APP_CONFIG, useValue: environment }, provideMockStore({ initialState }), AppComponent, RouteService diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bf096c210f..2a7a885794 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -18,6 +18,7 @@ import { Router, } from '@angular/router'; +import { isEqual } from 'lodash'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { select, Store } from '@ngrx/store'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -46,6 +47,7 @@ import { BASE_THEME_NAME } from './shared/theme-support/theme.constants'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { getDefaultThemeConfig } from '../config/config.util'; +import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; @Component({ selector: 'ds-app', @@ -59,7 +61,7 @@ export class AppComponent implements OnInit, AfterViewInit { collapsedSidebarWidth: Observable; totalSidebarWidth: Observable; theme: Observable = of({} as any); - notificationOptions = environment.notifications; + notificationOptions; models; /** @@ -88,6 +90,7 @@ export class AppComponent implements OnInit, AfterViewInit { @Inject(NativeWindowService) private _window: NativeWindowRef, @Inject(DOCUMENT) private document: any, @Inject(PLATFORM_ID) private platformId: any, + @Inject(APP_CONFIG) private appConfig: AppConfig, private themeService: ThemeService, private translate: TranslateService, private store: Store, @@ -106,6 +109,14 @@ export class AppComponent implements OnInit, AfterViewInit { @Optional() private googleAnalyticsService: GoogleAnalyticsService, ) { + console.log(this.appConfig); + + if (!isEqual(environment, this.appConfig)) { + throw new Error('environment does not match app config!'); + } + + this.notificationOptions = environment.notifications; + /* Use models object so all decorators are actually called */ this.models = models; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a1c5addae8..98b2d9fe92 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -58,14 +58,18 @@ import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { UUIDService } from './core/shared/uuid.service'; import { CookieService } from './core/services/cookie.service'; -// import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; +import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; -export function getBase() { - return environment.ui.nameSpace; +export function getConfig() { + return environment; } -export function getMetaReducers(): MetaReducer[] { - return environment.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers; +export function getBase(appConfig: AppConfig) { + return appConfig.ui.nameSpace; +} + +export function getMetaReducers(appConfig: AppConfig): MetaReducer[] { + return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers; } /** @@ -100,15 +104,19 @@ IMPORTS.push( ); const PROVIDERS = [ + { + provide: APP_CONFIG, + useFactory: getConfig + }, { provide: APP_BASE_HREF, useFactory: getBase, - // deps: [APP_CONFIG] + deps: [APP_CONFIG] }, { provide: USER_PROVIDED_META_REDUCERS, useFactory: getMetaReducers, - // deps: [APP_CONFIG] + deps: [APP_CONFIG] }, { provide: RouterStateSerializer, @@ -199,7 +207,7 @@ const EXPORTS = [ @NgModule({ imports: [ - BrowserModule.withServerTransition({ appId: 'serverApp' }), + BrowserModule.withServerTransition({ appId: 'dspace-angular' }), ...IMPORTS ], providers: [ diff --git a/src/app/item-page/simple/item-types/shared/item.component.ts b/src/app/item-page/simple/item-types/shared/item.component.ts index fa7fba8724..71b0b5b678 100644 --- a/src/app/item-page/simple/item-types/shared/item.component.ts +++ b/src/app/item-page/simple/item-types/shared/item.component.ts @@ -36,9 +36,10 @@ export class ItemComponent implements OnInit { */ iiifQuery$: Observable; - mediaViewer = environment.mediaViewer; + mediaViewer; constructor(protected routeService: RouteService) { + this.mediaViewer = environment.mediaViewer; } ngOnInit(): void { diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index 209f17b520..6ba859ef23 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -32,7 +32,7 @@ export class RootComponent implements OnInit { collapsedSidebarWidth: Observable; totalSidebarWidth: Observable; theme: Observable = of({} as any); - notificationOptions = environment.notifications; + notificationOptions; models; /** @@ -58,6 +58,7 @@ export class RootComponent implements OnInit { private menuService: MenuService, private windowService: HostWindowService ) { + this.notificationOptions = environment.notifications; } ngOnInit() { diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 9a993cb415..4c15e6dbc1 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -14,6 +14,7 @@ import { ThemeConfig } from './theme.model'; import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; +import { makeStateKey } from '@angular/platform-browser'; interface AppConfig extends Config { ui: UIServerConfig; @@ -37,7 +38,10 @@ interface AppConfig extends Config { const APP_CONFIG = new InjectionToken('APP_CONFIG'); +const APP_CONFIG_STATE = makeStateKey('APP_CONFIG_STATE'); + export { AppConfig, - APP_CONFIG + APP_CONFIG, + APP_CONFIG_STATE }; diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 355ba028ab..51a8c8119f 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -2,13 +2,11 @@ import * as colors from 'colors'; import * as fs from 'fs'; import { join } from 'path'; -import { environment } from '../environments/environment'; - import { AppConfig } from './app-config.interface'; import { Config } from './config.interface'; import { DefaultAppConfig } from './default-app-config'; import { ServerConfig } from './server-config.interface'; -import { extendConfig, extendEnvironmentWithAppConfig } from './config.util'; +import { extendConfig } from './config.util'; import { isNotEmpty } from '../app/shared/empty.util'; const CONFIG_PATH = join(process.cwd(), 'config'); @@ -182,9 +180,6 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => { buildBaseUrl(appConfig.ui); buildBaseUrl(appConfig.rest); - // extend environment with app config for server side use - extendEnvironmentWithAppConfig(environment, appConfig); - if (isNotEmpty(destConfigPath)) { fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 12859db977..bceea29b96 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -18,7 +18,7 @@ import { UIServerConfig } from './ui-server-config.interface'; import { UniversalConfig } from './universal-config.interface'; export class DefaultAppConfig implements AppConfig { - production: boolean = false; + production = false; // Angular Universal server settings. // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. @@ -172,10 +172,10 @@ export class DefaultAppConfig implements AppConfig { }; // NOTE: will log all redux actions and transfers in console - debug: boolean = false; + debug = false; // Default Language in which the UI will be rendered if the user's browser language is not an active language - defaultLanguage: string = 'en'; + defaultLanguage = 'en'; // Languages. DSpace Angular holds a message catalog for each of the following languages. // When set to active, users will be able to switch to the use of this language in the user interface. diff --git a/src/environments/environment.ci.ts b/src/environments/environment.ci.ts deleted file mode 100644 index bb12eb14c1..0000000000 --- a/src/environments/environment.ci.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; -import { RestRequestMethod } from '../app/core/data/rest-request-method'; -import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; -import { AppConfig } from '../config/app-config.interface'; - -export const environment: AppConfig = { - production: true, - - // Angular Universal settings - universal: { - preboot: true, - async: true, - time: false - }, - - // Angular Universal server settings. - // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. - ui: { - ssl: false, - host: 'localhost', - port: 4000, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/', - baseUrl: 'http://localhost:4000/', - - // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). - rateLimiter: { - windowMs: 1 * 60 * 1000, // 1 minute - max: 500 // limit each IP to 500 requests per windowMs - } - }, - - // The REST API server settings. - // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. - rest: { - ssl: false, - host: 'localhost', - port: 8080, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/server', - baseUrl: 'http://localhost:8080/server' - }, - - // Caching settings - cache: { - // NOTE: how long should objects be cached for by default - msToLive: { - default: 15 * 60 * 1000 // 15 minutes - }, - control: 'max-age=60', // revalidate browser - autoSync: { - defaultTime: 0, - maxBufferSize: 100, - timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds - } - }, - - // Authentication settings - auth: { - // Authentication UI settings - ui: { - // the amount of time before the idle warning is shown - timeUntilIdle: 15 * 60 * 1000, // 15 minutes - // the amount of time the user has to react after the idle warning is shown before they are logged out. - idleGracePeriod: 5 * 60 * 1000 // 5 minutes - }, - // Authentication REST settings - rest: { - // If the rest token expires in less than this amount of time, it will be refreshed automatically. - // This is independent from the idle warning. - timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes - } - }, - - // Form settings - form: { - // NOTE: Map server-side validators to comparative Angular form validators - validatorMap: { - required: 'required', - regex: 'pattern' - } - }, - - // Notifications - notifications: { - rtl: false, - position: ['top', 'right'], - maxStack: 8, - // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically - timeOut: 5000, // 5 second - clickToClose: true, - // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' - animate: NotificationAnimationsType.Scale - }, - - // Submission settings - submission: { - autosave: { - // NOTE: which metadata trigger an autosave - metadata: [], - /** - * NOTE: after how many time (milliseconds) submission is saved automatically - * eg. timer: 5 * (1000 * 60); // 5 minutes - */ - timer: 0 - }, - icons: { - metadata: [ - /** - * NOTE: example of configuration - * { - * // NOTE: metadata name - * name: 'dc.author', - * // NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used - * style: 'fa-user' - * } - */ - { - name: 'dc.author', - style: 'fas fa-user' - }, - // default configuration - { - name: 'default', - style: '' - } - ], - authority: { - confidence: [ - /** - * NOTE: example of configuration - * { - * // NOTE: confidence value - * value: 'dc.author', - * // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used - * style: 'fa-user' - * } - */ - { - value: 600, - style: 'text-success' - }, - { - value: 500, - style: 'text-info' - }, - { - value: 400, - style: 'text-warning' - }, - // default configuration - { - value: 'default', - style: 'text-muted' - } - - ] - } - } - }, - - // NOTE: will log all redux actions and transfers in console - debug: false, - - // Default Language in which the UI will be rendered if the user's browser language is not an active language - defaultLanguage: 'en', - - // Languages. DSpace Angular holds a message catalog for each of the following languages. - // When set to active, users will be able to switch to the use of this language in the user interface. - languages: [ - { code: 'en', label: 'English', active: true }, - { code: 'cs', label: 'Čeština', active: true }, - { code: 'de', label: 'Deutsch', active: true }, - { code: 'es', label: 'Español', active: true }, - { code: 'fr', label: 'Français', active: true }, - { code: 'lv', label: 'Latviešu', active: true }, - { code: 'hu', label: 'Magyar', active: true }, - { code: 'nl', label: 'Nederlands', active: true }, - { code: 'pt-PT', label: 'Português', active: true }, - { code: 'pt-BR', label: 'Português do Brasil', active: true }, - { code: 'fi', label: 'Suomi', active: true } - ], - - // Browse-By Pages - browseBy: { - // Amount of years to display using jumps of one year (current year - oneYearLimit) - oneYearLimit: 10, - // Limit for years to display using jumps of five years (current year - fiveYearLimit) - fiveYearLimit: 30, - // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) - defaultLowerLimit: 1900, - // List of all the active Browse-By types - // Adding a type will activate their Browse-By page and add them to the global navigation menu, - // as well as community and collection pages - // Allowed fields and their purpose: - // id: The browse id to use for fetching info from the rest api - // type: The type of Browse-By page to display - // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') - types: [ - { - id: 'title', - type: BrowseByType.Title, - }, - { - id: 'dateissued', - type: BrowseByType.Date, - metadataField: 'dc.date.issued' - }, - { - id: 'author', - type: BrowseByType.Metadata - }, - { - id: 'subject', - type: BrowseByType.Metadata - } - ] - }, - - // Item Page Config - item: { - edit: { - undoTimeout: 10000 // 10 seconds - } - }, - - // Collection Page Config - collection: { - edit: { - undoTimeout: 10000 // 10 seconds - } - }, - - // Theme Config - themes: [ - // Add additional themes here. In the case where multiple themes match a route, the first one - // in this list will get priority. It is advisable to always have a theme that matches - // every route as the last one - - // { - // // A theme with a handle property will match the community, collection or item with the given - // // handle, and all collections and/or items within it - // name: 'custom', - // handle: '10673/1233' - // }, - // { - // // A theme with a regex property will match the route using a regular expression. If it - // // matches the route for a community or collection it will also apply to all collections - // // and/or items within it - // name: 'custom', - // regex: 'collections\/e8043bc2.*' - // }, - // { - // // A theme with a uuid property will match the community, collection or item with the given - // // ID, and all collections and/or items within it - // name: 'custom', - // uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' - // }, - // { - // // The extends property specifies an ancestor theme (by name). Whenever a themed component is not found - // // in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. - // name: 'custom-A', - // extends: 'custom-B', - // // Any of the matching properties above can be used - // handle: '10673/34' - // }, - // { - // name: 'custom-B', - // extends: 'custom', - // handle: '10673/12' - // }, - // { - // // A theme with only a name will match every route - // name: 'custom' - // }, - // { - // // This theme will use the default bootstrap styling for DSpace components - // name: BASE_THEME_NAME - // }, - - { - // The default dspace theme - name: 'dspace' - } - ], - - // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). - // For images, this enables a gallery viewer where you can zoom or page through images. - // For videos, this enables embedded video streaming - mediaViewer: { - image: false, - video: false - } -}; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 63a9e01a2e..85c0f8fddc 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,9 +1,6 @@ -import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; -import { RestRequestMethod } from '../app/core/data/rest-request-method'; -import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { AppConfig } from '../config/app-config.interface'; -export const environment: AppConfig = { +export const environment: Partial = { production: true, // Angular Universal settings @@ -11,284 +8,5 @@ export const environment: AppConfig = { preboot: true, async: true, time: false - }, - - // Angular Universal server settings. - // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. - ui: { - ssl: false, - host: 'localhost', - port: 4000, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/', - baseUrl: 'http://localhost:4000/', - - // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). - rateLimiter: { - windowMs: 1 * 60 * 1000, // 1 minute - max: 500 // limit each IP to 500 requests per windowMs - } - }, - - // The REST API server settings. - // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. - rest: { - ssl: true, - host: 'api7.dspace.org', - port: 443, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/server', - baseUrl: 'https://api7.dspace.org/server' - }, - - // Caching settings - cache: { - // NOTE: how long should objects be cached for by default - msToLive: { - default: 15 * 60 * 1000 // 15 minutes - }, - control: 'max-age=60', // revalidate browser - autoSync: { - defaultTime: 0, - maxBufferSize: 100, - timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds - } - }, - - // Authentication settings - auth: { - // Authentication UI settings - ui: { - // the amount of time before the idle warning is shown - timeUntilIdle: 15 * 60 * 1000, // 15 minutes - // the amount of time the user has to react after the idle warning is shown before they are logged out. - idleGracePeriod: 5 * 60 * 1000 // 5 minutes - }, - // Authentication REST settings - rest: { - // If the rest token expires in less than this amount of time, it will be refreshed automatically. - // This is independent from the idle warning. - timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes - } - }, - - // Form settings - form: { - // NOTE: Map server-side validators to comparative Angular form validators - validatorMap: { - required: 'required', - regex: 'pattern' - } - }, - - // Notifications - notifications: { - rtl: false, - position: ['top', 'right'], - maxStack: 8, - // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically - timeOut: 5000, // 5 second - clickToClose: true, - // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' - animate: NotificationAnimationsType.Scale - }, - - // Submission settings - submission: { - autosave: { - // NOTE: which metadata trigger an autosave - metadata: [], - /** - * NOTE: after how many time (milliseconds) submission is saved automatically - * eg. timer: 5 * (1000 * 60); // 5 minutes - */ - timer: 0 - }, - icons: { - metadata: [ - /** - * NOTE: example of configuration - * { - * // NOTE: metadata name - * name: 'dc.author', - * // NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used - * style: 'fa-user' - * } - */ - { - name: 'dc.author', - style: 'fas fa-user' - }, - // default configuration - { - name: 'default', - style: '' - } - ], - authority: { - confidence: [ - /** - * NOTE: example of configuration - * { - * // NOTE: confidence value - * value: 'dc.author', - * // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used - * style: 'fa-user' - * } - */ - { - value: 600, - style: 'text-success' - }, - { - value: 500, - style: 'text-info' - }, - { - value: 400, - style: 'text-warning' - }, - // default configuration - { - value: 'default', - style: 'text-muted' - } - - ] - } - } - }, - - // NOTE: will log all redux actions and transfers in console - debug: false, - - // Default Language in which the UI will be rendered if the user's browser language is not an active language - defaultLanguage: 'en', - - // Languages. DSpace Angular holds a message catalog for each of the following languages. - // When set to active, users will be able to switch to the use of this language in the user interface. - languages: [ - { code: 'en', label: 'English', active: true }, - { code: 'cs', label: 'Čeština', active: true }, - { code: 'de', label: 'Deutsch', active: true }, - { code: 'es', label: 'Español', active: true }, - { code: 'fr', label: 'Français', active: true }, - { code: 'lv', label: 'Latviešu', active: true }, - { code: 'hu', label: 'Magyar', active: true }, - { code: 'nl', label: 'Nederlands', active: true }, - { code: 'pt-PT', label: 'Português', active: true }, - { code: 'pt-BR', label: 'Português do Brasil', active: true }, - { code: 'fi', label: 'Suomi', active: true } - ], - - // Browse-By Pages - browseBy: { - // Amount of years to display using jumps of one year (current year - oneYearLimit) - oneYearLimit: 10, - // Limit for years to display using jumps of five years (current year - fiveYearLimit) - fiveYearLimit: 30, - // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) - defaultLowerLimit: 1900, - // List of all the active Browse-By types - // Adding a type will activate their Browse-By page and add them to the global navigation menu, - // as well as community and collection pages - // Allowed fields and their purpose: - // id: The browse id to use for fetching info from the rest api - // type: The type of Browse-By page to display - // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') - types: [ - { - id: 'title', - type: BrowseByType.Title, - }, - { - id: 'dateissued', - type: BrowseByType.Date, - metadataField: 'dc.date.issued' - }, - { - id: 'author', - type: BrowseByType.Metadata - }, - { - id: 'subject', - type: BrowseByType.Metadata - } - ] - }, - - // Item Page Config - item: { - edit: { - undoTimeout: 10000 // 10 seconds - } - }, - - // Collection Page Config - collection: { - edit: { - undoTimeout: 10000 // 10 seconds - } - }, - - // Theme Config - themes: [ - // Add additional themes here. In the case where multiple themes match a route, the first one - // in this list will get priority. It is advisable to always have a theme that matches - // every route as the last one - - // { - // // A theme with a handle property will match the community, collection or item with the given - // // handle, and all collections and/or items within it - // name: 'custom', - // handle: '10673/1233' - // }, - // { - // // A theme with a regex property will match the route using a regular expression. If it - // // matches the route for a community or collection it will also apply to all collections - // // and/or items within it - // name: 'custom', - // regex: 'collections\/e8043bc2.*' - // }, - // { - // // A theme with a uuid property will match the community, collection or item with the given - // // ID, and all collections and/or items within it - // name: 'custom', - // uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' - // }, - // { - // // The extends property specifies an ancestor theme (by name). Whenever a themed component is not found - // // in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. - // name: 'custom-A', - // extends: 'custom-B', - // // Any of the matching properties above can be used - // handle: '10673/34' - // }, - // { - // name: 'custom-B', - // extends: 'custom', - // handle: '10673/12' - // }, - // { - // // A theme with only a name will match every route - // name: 'custom' - // }, - // { - // // This theme will use the default bootstrap styling for DSpace components - // name: BASE_THEME_NAME - // }, - - { - // The default dspace theme - name: 'dspace' - } - ], - - // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). - // For images, this enables a gallery viewer where you can zoom or page through images. - // For videos, this enables embedded video streaming - mediaViewer: { - image: false, - video: false } }; diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 394a2e5dac..0af2fc8546 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -1,4 +1,4 @@ -// This configuration is only used for unit tests, end-to-end tests use environment.e2e.ts +// This configuration is only used for unit tests, end-to-end tests use environment.prod.ts import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index ba7b262ac4..29b67704eb 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,15 +1,11 @@ // This file can be replaced during build by using the `fileReplacements` array. // `ng build --configuration production` replaces `environment.ts` with `environment.prod.ts`. -// `ng build --configuration ci` replaces `environment.ts` with `environment.ci.ts`. // `ng test --configuration test` replaces `environment.ts` with `environment.test.ts`. // The list of file replacements can be found in `angular.json`. -import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; -import { RestRequestMethod } from '../app/core/data/rest-request-method'; -import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { AppConfig } from '../config/app-config.interface'; -export const environment: AppConfig = { +export const environment: Partial = { production: false, // Angular Universal settings @@ -17,285 +13,6 @@ export const environment: AppConfig = { preboot: true, async: true, time: false - }, - - // Angular Universal server settings. - // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. - ui: { - ssl: false, - host: 'localhost', - port: 4000, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/', - baseUrl: 'http://localhost:4000/', - - // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). - rateLimiter: { - windowMs: 1 * 60 * 1000, // 1 minute - max: 500 // limit each IP to 500 requests per windowMs - } - }, - - // The REST API server settings. - // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. - rest: { - ssl: true, - host: 'api7.dspace.org', - port: 443, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/server', - baseUrl: 'https://api7.dspace.org/server' - }, - - // Caching settings - cache: { - // NOTE: how long should objects be cached for by default - msToLive: { - default: 15 * 60 * 1000 // 15 minutes - }, - control: 'max-age=60', // revalidate browser - autoSync: { - defaultTime: 0, - maxBufferSize: 100, - timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds - } - }, - - // Authentication settings - auth: { - // Authentication UI settings - ui: { - // the amount of time before the idle warning is shown - timeUntilIdle: 15 * 60 * 1000, // 15 minutes - // the amount of time the user has to react after the idle warning is shown before they are logged out. - idleGracePeriod: 5 * 60 * 1000 // 5 minutes - }, - // Authentication REST settings - rest: { - // If the rest token expires in less than this amount of time, it will be refreshed automatically. - // This is independent from the idle warning. - timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes - } - }, - - // Form settings - form: { - // NOTE: Map server-side validators to comparative Angular form validators - validatorMap: { - required: 'required', - regex: 'pattern' - } - }, - - // Notifications - notifications: { - rtl: false, - position: ['top', 'right'], - maxStack: 8, - // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically - timeOut: 5000, // 5 second - clickToClose: true, - // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' - animate: NotificationAnimationsType.Scale - }, - - // Submission settings - submission: { - autosave: { - // NOTE: which metadata trigger an autosave - metadata: [], - /** - * NOTE: after how many time (milliseconds) submission is saved automatically - * eg. timer: 5 * (1000 * 60); // 5 minutes - */ - timer: 0 - }, - icons: { - metadata: [ - /** - * NOTE: example of configuration - * { - * // NOTE: metadata name - * name: 'dc.author', - * // NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used - * style: 'fa-user' - * } - */ - { - name: 'dc.author', - style: 'fas fa-user' - }, - // default configuration - { - name: 'default', - style: '' - } - ], - authority: { - confidence: [ - /** - * NOTE: example of configuration - * { - * // NOTE: confidence value - * value: 'dc.author', - * // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used - * style: 'fa-user' - * } - */ - { - value: 600, - style: 'text-success' - }, - { - value: 500, - style: 'text-info' - }, - { - value: 400, - style: 'text-warning' - }, - // default configuration - { - value: 'default', - style: 'text-muted' - } - - ] - } - } - }, - - // NOTE: will log all redux actions and transfers in console - debug: false, - - // Default Language in which the UI will be rendered if the user's browser language is not an active language - defaultLanguage: 'en', - - // Languages. DSpace Angular holds a message catalog for each of the following languages. - // When set to active, users will be able to switch to the use of this language in the user interface. - languages: [ - { code: 'en', label: 'English', active: true }, - { code: 'cs', label: 'Čeština', active: true }, - { code: 'de', label: 'Deutsch', active: true }, - { code: 'es', label: 'Español', active: true }, - { code: 'fr', label: 'Français', active: true }, - { code: 'lv', label: 'Latviešu', active: true }, - { code: 'hu', label: 'Magyar', active: true }, - { code: 'nl', label: 'Nederlands', active: true }, - { code: 'pt-PT', label: 'Português', active: true }, - { code: 'pt-BR', label: 'Português do Brasil', active: true }, - { code: 'fi', label: 'Suomi', active: true } - ], - - // Browse-By Pages - browseBy: { - // Amount of years to display using jumps of one year (current year - oneYearLimit) - oneYearLimit: 10, - // Limit for years to display using jumps of five years (current year - fiveYearLimit) - fiveYearLimit: 30, - // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) - defaultLowerLimit: 1900, - // List of all the active Browse-By types - // Adding a type will activate their Browse-By page and add them to the global navigation menu, - // as well as community and collection pages - // Allowed fields and their purpose: - // id: The browse id to use for fetching info from the rest api - // type: The type of Browse-By page to display - // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') - types: [ - { - id: 'title', - type: BrowseByType.Title, - }, - { - id: 'dateissued', - type: BrowseByType.Date, - metadataField: 'dc.date.issued' - }, - { - id: 'author', - type: BrowseByType.Metadata - }, - { - id: 'subject', - type: BrowseByType.Metadata - } - ] - }, - - // Item Page Config - item: { - edit: { - undoTimeout: 10000 // 10 seconds - } - }, - - // Collection Page Config - collection: { - edit: { - undoTimeout: 10000 // 10 seconds - } - }, - - // Theme Config - themes: [ - // Add additional themes here. In the case where multiple themes match a route, the first one - // in this list will get priority. It is advisable to always have a theme that matches - // every route as the last one - - // { - // // A theme with a handle property will match the community, collection or item with the given - // // handle, and all collections and/or items within it - // name: 'custom', - // handle: '10673/1233' - // }, - // { - // // A theme with a regex property will match the route using a regular expression. If it - // // matches the route for a community or collection it will also apply to all collections - // // and/or items within it - // name: 'custom', - // regex: 'collections\/e8043bc2.*' - // }, - // { - // // A theme with a uuid property will match the community, collection or item with the given - // // ID, and all collections and/or items within it - // name: 'custom', - // uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' - // }, - // { - // // The extends property specifies an ancestor theme (by name). Whenever a themed component is not found - // // in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. - // name: 'custom-A', - // extends: 'custom-B', - // // Any of the matching properties above can be used - // handle: '10673/34' - // }, - // { - // name: 'custom-B', - // extends: 'custom', - // handle: '10673/12' - // }, - // { - // // A theme with only a name will match every route - // name: 'custom' - // }, - // { - // // This theme will use the default bootstrap styling for DSpace components - // name: BASE_THEME_NAME - // }, - - { - // The default dspace theme - name: 'dspace' - } - ], - - // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). - // For images, this enables a gallery viewer where you can zoom or page through images. - // For videos, this enables embedded video streaming - mediaViewer: { - image: false, - video: false } }; diff --git a/src/main.browser.ts b/src/main.browser.ts index 5a17c3e7cc..c72ca0cf31 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -11,13 +11,13 @@ import { hasValue } from './app/shared/empty.util'; import { BrowserAppModule } from './modules/app/browser-app.module'; import { environment } from './environments/environment'; +import { AppConfig } from './config/app-config.interface'; +import { extendEnvironmentWithAppConfig } from './config/config.util'; -// import { AppConfig, APP_CONFIG } from './config/app-config.interface'; -// import { extendEnvironmentWithAppConfig } from './config/config.util'; - -if (environment.production) { - enableProdMode(); -} +const bootstrap = () => platformBrowserDynamic() + .bootstrapModule(BrowserAppModule, { + preserveWhitespaces: true + }); const main = () => { // Load fonts async @@ -28,10 +28,22 @@ const main = () => { } }); - return platformBrowserDynamic() - .bootstrapModule(BrowserAppModule, { - preserveWhitespaces: true - }); + if (environment.production) { + enableProdMode(); + + return bootstrap(); + } else { + + return fetch('assets/appConfig.json') + .then((response) => response.json()) + .then((appConfig: AppConfig) => { + + // extend environment with app config for browser when not prerendered + extendEnvironmentWithAppConfig(environment, appConfig); + + return bootstrap(); + }); + } }; // support async tag or hmr @@ -40,11 +52,3 @@ if (hasValue(environment.universal) && environment.universal.preboot === false) } else { document.addEventListener('DOMContentLoaded', main); } - - -// fetch('assets/appConfig.json') -// .then((response) => response.json()) -// .then((appConfig: AppConfig) => { -// // extend environment with app config for client side use -// extendEnvironmentWithAppConfig(environment, appConfig); -// }); diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index efdaf12a1b..86497d4599 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -1,7 +1,8 @@ import { HttpClient, HttpClientModule } from '@angular/common/http'; -import { NgModule } from '@angular/core'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; import { BrowserModule, makeStateKey, TransferState } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterModule, NoPreloading } from '@angular/router'; import { REQUEST } from '@nguniversal/express-engine/tokens'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -30,9 +31,13 @@ import { } from '../../app/core/services/browser-hard-redirect.service'; import { LocaleService } from '../../app/core/locale/locale.service'; import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.service'; -import { RouterModule, NoPreloading } from '@angular/router'; import { AuthRequestService } from '../../app/core/auth/auth-request.service'; import { BrowserAuthRequestService } from '../../app/core/auth/browser-auth-request.service'; +import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface'; +import { DefaultAppConfig } from '../../config/default-app-config'; +import { extendEnvironmentWithAppConfig } from '../../config/config.util'; + +import { environment } from '../../environments/environment'; export const REQ_KEY = makeStateKey('req'); @@ -54,12 +59,12 @@ export function getRequest(transferState: TransferState): any { // forRoot ensures the providers are only created once IdlePreloadModule.forRoot(), RouterModule.forRoot([], { - // enableTracing: true, - useHash: false, - scrollPositionRestoration: 'enabled', - anchorScrolling: 'enabled', - preloadingStrategy: NoPreloading -}), + // enableTracing: true, + useHash: false, + scrollPositionRestoration: 'enabled', + anchorScrolling: 'enabled', + preloadingStrategy: NoPreloading + }), StatisticsModule.forRoot(), Angulartics2RouterlessModule.forRoot(), BrowserAnimationsModule, @@ -74,6 +79,20 @@ export function getRequest(transferState: TransferState): any { AppModule ], providers: [ + { + provide: APP_INITIALIZER, + useFactory: (transferState: TransferState) => { + if (transferState.hasKey(APP_CONFIG_STATE)) { + const appConfig = transferState.get(APP_CONFIG_STATE, new DefaultAppConfig()); + // extend environment with app config for browser + extendEnvironmentWithAppConfig(environment, appConfig); + } + + return () => true; + }, + deps: [TransferState], + multi: true + }, { provide: REQUEST, useFactory: getRequest, diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index dad3a60d5c..8c49554de9 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -1,38 +1,40 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { BrowserModule, TransferState } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ServerModule } from '@angular/platform-server'; +import { RouterModule } from '@angular/router'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { Angulartics2 } from 'angulartics2'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; + import { AppComponent } from '../../app/app.component'; import { AppModule } from '../../app/app.module'; import { DSpaceServerTransferStateModule } from '../transfer-state/dspace-server-transfer-state.module'; import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service'; - import { TranslateJson5UniversalLoader } from '../../ngx-translate-loaders/translate-json5-universal.loader'; import { CookieService } from '../../app/core/services/cookie.service'; import { ServerCookieService } from '../../app/core/services/server-cookie.service'; import { AuthService } from '../../app/core/auth/auth.service'; import { ServerAuthService } from '../../app/core/auth/server-auth.service'; - -import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import { AngularticsProviderMock } from '../../app/shared/mocks/angulartics-provider.service.mock'; import { SubmissionService } from '../../app/submission/submission.service'; import { ServerSubmissionService } from '../../app/submission/server-submission.service'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { ServerLocaleService } from '../../app/core/locale/server-locale.service'; import { LocaleService } from '../../app/core/locale/locale.service'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { ForwardClientIpInterceptor } from '../../app/core/forward-client-ip/forward-client-ip.interceptor'; import { HardRedirectService } from '../../app/core/services/hard-redirect.service'; import { ServerHardRedirectService } from '../../app/core/services/server-hard-redirect.service'; -import { Angulartics2 } from 'angulartics2'; import { Angulartics2Mock } from '../../app/shared/mocks/angulartics2.service.mock'; -import { RouterModule } from '@angular/router'; import { AuthRequestService } from '../../app/core/auth/auth-request.service'; import { ServerAuthRequestService } from '../../app/core/auth/server-auth-request.service'; +import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface'; + +import { environment } from '../../environments/environment'; export function createTranslateLoader() { return new TranslateJson5UniversalLoader('dist/server/assets/i18n/', '.json5'); @@ -60,6 +62,16 @@ export function createTranslateLoader() { AppModule ], providers: [ + // Initialize app config and extend environment + { + provide: APP_INITIALIZER, + useFactory: (transferState: TransferState) => { + transferState.set(APP_CONFIG_STATE, environment as AppConfig); + return () => true; + }, + deps: [TransferState], + multi: true + }, { provide: Angulartics2, useClass: Angulartics2Mock diff --git a/tslint.json b/tslint.json index 36a32ed795..ef85c50002 100644 --- a/tslint.json +++ b/tslint.json @@ -170,9 +170,5 @@ "use-life-cycle-interface": false, "no-outputs-metadata-property": true, "use-pipe-transform-interface": true - // "rxjs-collapse-imports": true, - // "rxjs-pipeable-operators-only": true, - // "rxjs-no-static-observable-methods": true, - // "rxjs-proper-imports": true } } diff --git a/webpack/webpack.browser.ts b/webpack/webpack.browser.ts index 187262071e..134eecb7ed 100644 --- a/webpack/webpack.browser.ts +++ b/webpack/webpack.browser.ts @@ -1,6 +1,6 @@ -// import { join } from 'path'; +import { join } from 'path'; -// import { buildAppConfig } from '../src/config/config.server'; +import { buildAppConfig } from '../src/config/config.server'; import { commonExports } from './webpack.common'; module.exports = Object.assign({}, commonExports, { @@ -10,7 +10,7 @@ module.exports = Object.assign({}, commonExports, { }, devServer: { before(app, server) { - // buildAppConfig(join(process.cwd(), 'src/assets/appConfig.json')); + buildAppConfig(join(process.cwd(), 'src/assets/appConfig.json')); } } }); diff --git a/yarn.lock b/yarn.lock index e8e9b67c4c..db63c66040 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1452,9 +1452,9 @@ integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== "@cypress/request@^2.88.6": - version "2.88.7" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.7.tgz#386d960ab845a96953723348088525d5a75aaac4" - integrity sha512-FTULIP2rnDJvZDT9t6B4nSfYR40ue19tVmv3wUcY05R9/FPCoMl1nAPJkzWzBCo7ltVn5ThQTbxiMoGBN7k0ig== + version "2.88.10" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce" + integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -1463,8 +1463,7 @@ extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" + http-signature "~1.3.6" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" @@ -1502,9 +1501,9 @@ integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== "@discoveryjs/json-ext@^0.5.0": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz#9283c9ce5b289a3c4f61c12757469e59377f81f3" - integrity sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA== + version "0.5.6" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f" + integrity sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA== "@edsilv/http-status-codes@^1.0.3": version "1.0.3" @@ -1527,9 +1526,9 @@ integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== "@iiif/vocabulary@^1.0.20": - version "1.0.21" - resolved "https://registry.yarnpkg.com/@iiif/vocabulary/-/vocabulary-1.0.21.tgz#5808f62da11b64ca42a895025844be088df9f48a" - integrity sha512-XUD8RYeBiEzv8rdpC9tNBaQ0FMZuGWIkPsmcFhnqXW+5WjZmRLhgqycuoGwIjxt9nPsDgW1IxuiWduNgbLl9UA== + version "1.0.22" + resolved "https://registry.yarnpkg.com/@iiif/vocabulary/-/vocabulary-1.0.22.tgz#743d6cb1914e090137c56a4e44decbe7f8b21bdc" + integrity sha512-KOIZgRpDERMK4YGeSwPBNjpJETapMWp8pvEvSGUQkuTlqkiNLirZH65CIcEENZEL5A6mxrYLJX6okDYq7W1+RA== "@istanbuljs/schema@^0.1.2": version "0.1.3" @@ -1936,6 +1935,18 @@ semver "7.3.4" semver-intersect "1.4.0" +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -1972,9 +1983,9 @@ integrity sha512-M6x29Vk4681dght4IMnPIcF1SNmeEm0c4uatlTFhp+++H1oDK1THEIzuCC2WeCBVhX+gU0NndsseDS3zaCtlcQ== "@types/express-serve-static-core@^4.17.18": - version "4.17.25" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.25.tgz#e42f7046adc65ece2eb6059b77aecfbe9e9f82e0" - integrity sha512-OUJIVfRMFijZukGGwTpKNFprqCCXk5WjNGvUgB/CxxBR40QWSjsNK86+yvGKlCOGc7sbwfHLaXhkG+NsytwBaQ== + version "4.17.26" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz#5d9a8eeecb9d5f9d7fc1d85f541512a84638ae88" + integrity sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2066,14 +2077,14 @@ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/node@*": - version "16.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" - integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== + version "16.11.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.12.tgz#ac7fb693ac587ee182c3780c26eb65546a1a3c10" + integrity sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw== "@types/node@^14.14.31", "@types/node@^14.14.9": - version "14.17.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.33.tgz#011ee28e38dc7aee1be032ceadf6332a0ab15b12" - integrity sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g== + version "14.18.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.0.tgz#98df2397f6936bfbff4f089e40e06fa5dd88d32a" + integrity sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ== "@types/parse-json@^4.0.0": version "4.0.0" @@ -2123,9 +2134,9 @@ "@types/react" "*" "@types/react@*": - version "17.0.35" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.35.tgz#217164cf830267d56cd1aec09dcf25a541eedd4c" - integrity sha512-r3C8/TJuri/SLZiiwwxQoLAoavaczARfT9up9b4Jr65+ErAUX3MIkU0oMOQnrpfgHme8zIqZLX7O5nnjm5Wayw== + version "17.0.37" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959" + integrity sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2457,9 +2468,9 @@ acorn@^7.1.1: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.0.4, acorn@^8.2.4: - version "8.5.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" - integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== + version "8.6.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" + integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== adjust-sourcemap-loader@3.0.0: version "3.0.0" @@ -2543,9 +2554,9 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== ajv-keywords@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.0.0.tgz#d01b3b21715b2f63d02aa511b82fc6eb3b30083c" - integrity sha512-ULd1QMjRoH6JDNUQIfDLrlE+OgZlFaxyYCjzt58uNuUQtKXt8/U+vK/8Ql0gyn/C5mqZzUWtKMqr/4YquvTrWA== + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: fast-deep-equal "^3.1.3" @@ -2580,9 +2591,9 @@ ajv@8.6.2: uri-js "^4.2.2" ajv@^8.0.0, ajv@^8.8.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.1.tgz#e73dd88eeb4b10bbcd82bee136e6fbe801664d18" - integrity sha512-6CiMNDrzv0ZR916u2T+iRunnD60uWmNn8SkdB44/6stVORUg0aAkWO7PkOhpCmjmW8f2I/G/xnowD66fxGyQJg== + version "8.8.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb" + integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2613,6 +2624,13 @@ angulartics2@^10.0.0: dependencies: tslib "^2.0.0" +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-colors@4.1.1, ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -3177,6 +3195,20 @@ bootstrap@4.3.1: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.3.1.tgz#280ca8f610504d99d7b6b4bfc4b68cec601704ac" integrity sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag== +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3337,7 +3369,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.0, browserslist@^4.16.1, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.17.6, browserslist@^4.6.4, browserslist@^4.9.1: +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.0, browserslist@^4.16.1, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.18.1, browserslist@^4.6.4, browserslist@^4.9.1: version "4.18.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" integrity sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ== @@ -3544,6 +3576,19 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + cachedir@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" @@ -3610,9 +3655,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001165, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001280: - version "1.0.30001282" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz#38c781ee0a90ccfe1fe7fefd00e43f5ffdcb96fd" - integrity sha512-YhF/hG6nqBEllymSIjLtR2iWDDnChvhnVJqp+vloyt2tEHFG1yBR+ac2B/rOw0qOK0m0lEXU2dv4E/sMk5P9Kg== + version "1.0.30001285" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001285.tgz#fe1e52229187e11d6670590790d669b9e03315b7" + integrity sha512-KAOkuUtcQ901MtmvxfKD+ODHH9YVDYnBt+TGYSz2KIfnq22CiArbUxXPN9067gNbgMlnNYRSwho8OPXZPALB9Q== canonical-path@1.0.0: version "1.0.0" @@ -3723,10 +3768,15 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" - integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -3778,6 +3828,11 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -3857,6 +3912,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -3939,9 +4001,9 @@ color-name@^1.0.0, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312" - integrity sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA== + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -4085,6 +4147,18 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + connect-history-api-fallback@^1, connect-history-api-fallback@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" @@ -4262,11 +4336,11 @@ copy-webpack-plugin@^6.4.1: webpack-sources "^1.4.3" core-js-compat@^3.6.2, core-js-compat@^3.8.0: - version "3.19.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.1.tgz#fe598f1a9bf37310d77c3813968e9f7c7bb99476" - integrity sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g== + version "3.19.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.3.tgz#de75e5821c5ce924a0a1e7b7d5c2cb973ff388aa" + integrity sha512-59tYzuWgEEVU9r+SRgceIGXSSUn47JknoiXW6Oq7RW8QHjXWz3/vp8pa7dbtuVu40sewz3OP3JmQEcDdztrLhA== dependencies: - browserslist "^4.17.6" + browserslist "^4.18.1" semver "7.0.0" core-js@3.6.4: @@ -4285,9 +4359,9 @@ core-js@^2.4.0: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.7.0: - version "3.19.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.1.tgz#f6f173cae23e73a7d88fa23b6e9da329276c6641" - integrity sha512-Tnc7E9iKd/b/ff7GFbhwPVzJzPztGrChB8X8GLqoYGdEOG8IpLnK1xPyo3ZoO3HsK6TodJS58VGPOxA+hLHQMg== + version "3.19.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.3.tgz#6df8142a996337503019ff3235a7022d7cdf4559" + integrity sha512-LeLBMgEGSsG7giquSzvgBrTS7V5UL6ks3eQlUSbN8dJStlLFiRzUm5iqsRyzUB8carhfKjkJ2vzKqE6z1Vga9g== core-util-is@1.0.2: version "1.0.2" @@ -4432,6 +4506,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + css-blank-pseudo@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" @@ -4688,9 +4767,9 @@ cssnano-preset-default@^4.0.7, cssnano-preset-default@^4.0.8: postcss-unique-selectors "^4.0.1" cssnano-preset-default@^5.0.1: - version "5.1.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.7.tgz#68c3ad1ec6a810482ec7d06b2d70fc34b6b0d70c" - integrity sha512-bWDjtTY+BOqrqBtsSQIbN0RLGD2Yr2CnecpP0ydHNafh9ZUEre8c8VYTaH9FEbyOt0eIfEUAYYk5zj92ioO8LA== + version "5.1.8" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.8.tgz#7525feb1b72f7b06e57f55064cbdae341d79dea2" + integrity sha512-zWMlP0+AMPBVE852SqTrP0DnhTcTA2C1wAF92TKZ3Va+aUVqLIhkqKlnJIXXdqXD7RN+S1ujuWmNpvrJBiM/vg== dependencies: css-declaration-sorter "^6.0.3" cssnano-utils "^2.0.1" @@ -4717,7 +4796,7 @@ cssnano-preset-default@^5.0.1: postcss-normalize-url "^5.0.3" postcss-normalize-whitespace "^5.0.1" postcss-ordered-values "^5.0.2" - postcss-reduce-initial "^5.0.1" + postcss-reduce-initial "^5.0.2" postcss-reduce-transforms "^5.0.1" postcss-svgo "^5.0.3" postcss-unique-selectors "^5.0.2" @@ -4803,9 +4882,9 @@ cssstyle@^2.3.0: cssom "~0.3.6" csstype@^2.5.2: - version "2.6.18" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.18.tgz#980a8b53085f34af313410af064f2bd241784218" - integrity sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ== + version "2.6.19" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.19.tgz#feeb5aae89020bb389e1f63669a5ed490e391caa" + integrity sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ== csstype@^3.0.2: version "3.0.10" @@ -4933,10 +5012,10 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@4.3.2, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" @@ -4947,7 +5026,14 @@ debug@4.3.1: dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + +debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -4983,6 +5069,13 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -4995,6 +5088,11 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-freeze@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" @@ -5018,7 +5116,7 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" -default-gateway@^6.0.0: +default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== @@ -5032,6 +5130,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -5296,9 +5399,9 @@ domhandler@^3.0.0: domelementtype "^2.0.1" domhandler@^4.0.0, domhandler@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" - integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== + version "4.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" + integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== dependencies: domelementtype "^2.2.0" @@ -5308,9 +5411,9 @@ domino@^2.1.2: integrity sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ== dompurify@^2.0.11: - version "2.3.3" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c" - integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg== + version "2.3.4" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.4.tgz#1cf5cf0105ccb4debdf6db162525bd41e6ddacc6" + integrity sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ== domutils@^1.7.0: version "1.7.0" @@ -5349,6 +5452,11 @@ dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -5392,9 +5500,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.896: - version "1.3.901" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.901.tgz#ce2c3157d61bce9f42f1e83225c17358ae9f4918" - integrity sha512-ToJdV2vzwT2jeAsw8zIggTFllJ4Kxvwghk39AhJEHHlIxor10wsFI3wo69p8nFc0s/ATWBqugPv/k3nW4Y9Mww== + version "1.4.12" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.12.tgz#5f73d1278c6205fc41d7a0ebd75563046b77c5d8" + integrity sha512-zjfhG9Us/hIy8AlQ5OzfbR/C4aBv1Dg/ak4GX35CELYlJ4tDAtoEcQivXvyBdqdNQ+R6PhlgQqV8UNPJmhkJog== element-resize-detector@^1.2.1: version "1.2.3" @@ -5662,6 +5770,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -6175,9 +6288,9 @@ forever-agent@~0.6.1: integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= fork-ts-checker-webpack-plugin@^6.0.3: - version "6.4.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.4.0.tgz#057e477cf1d8b013b2ed2669437f818680289c4c" - integrity sha512-3I3wFkc4DbzaUDPWEi96wdYGu4EKtxBafhZYm0o4mX51d9bphAY4P3mBl8K5mFXFJqVzHfmdbm9kLGnm7vwwBg== + version "6.5.0" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz#0282b335fa495a97e167f69018f566ea7d2a2b5e" + integrity sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw== dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" @@ -6380,7 +6493,7 @@ get-stdin@^8.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== -get-stream@^4.0.0: +get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== @@ -6524,6 +6637,23 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" @@ -6643,6 +6773,11 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + has@^1.0.0, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -6814,7 +6949,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.1.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -6868,9 +7003,9 @@ http-errors@~1.7.2: toidentifier "1.0.0" http-parser-js@>=0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" - integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== + version "0.5.5" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.5.tgz#d7c30d5d3c90d865b4a2e870181f9d6f22ac7ac5" + integrity sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA== http-proxy-agent@^4.0.1: version "4.0.1" @@ -6931,6 +7066,15 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http-signature@~1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.14.1" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -7029,6 +7173,11 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + ignore-walk@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" @@ -7105,6 +7254,11 @@ import-from@^3.0.0: dependencies: resolve-from "^5.0.0" +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -7174,7 +7328,7 @@ ini@2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -ini@^1.3.4: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7206,16 +7360,6 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -internal-ip@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-6.2.0.tgz#d5541e79716e406b74ac6b07b856ef18dc1621c1" - integrity sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg== - dependencies: - default-gateway "^6.0.0" - ipaddr.js "^1.9.1" - is-ip "^3.1.0" - p-event "^4.2.0" - internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -7247,17 +7391,12 @@ ip-regex@^2.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -ip-regex@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" - integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== - ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.9.1, ipaddr.js@^1.9.0, ipaddr.js@^1.9.1: +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== @@ -7348,6 +7487,13 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-ci@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" @@ -7476,7 +7622,7 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= -is-installed-globally@~0.4.0: +is-installed-globally@^0.4.0, is-installed-globally@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== @@ -7489,13 +7635,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-ip@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" - integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== - dependencies: - ip-regex "^4.0.0" - is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -7506,6 +7645,11 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + is-number-like@^1.0.3: version "1.0.8" resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3" @@ -7644,7 +7788,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typedarray@~1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -7683,6 +7827,11 @@ is-wsl@^2.1.1, is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -7769,9 +7918,9 @@ istanbul-lib-source-maps@^3.0.6: source-map "^0.6.1" istanbul-reports@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.5.tgz#a2580107e71279ea6d661ddede929ffc6d693384" - integrity sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ== + version "3.1.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.1.tgz#7085857f17d2441053c6ce5c3b8fdf6882289397" + integrity sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -7911,6 +8060,11 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -7931,10 +8085,10 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stringify-safe@~5.0.1: version "5.0.1" @@ -8011,13 +8165,23 @@ jsonschema@1.4.0: integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: assert-plus "1.0.0" extsprintf "1.3.0" - json-schema "0.2.3" + json-schema "0.4.0" + verror "1.10.0" + +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" verror "1.10.0" jss-plugin-camel-case@^10.5.1: @@ -8187,6 +8351,13 @@ karma@^5.2.3: ua-parser-js "0.7.22" yargs "^15.3.1" +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -8239,6 +8410,13 @@ last-call-webpack-plugin@^3.0.0: lodash "^4.17.5" webpack-sources "^1.1.0" +latest-version@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + lazy-ass@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" @@ -8350,20 +8528,20 @@ limiter@^1.0.5: integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== listr2@^3.8.3: - version "3.13.4" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.13.4.tgz#34101fc0184545597e00d1e7915ccfbfb17332e6" - integrity sha512-lZ1Rut1DSIRwbxQbI8qaUBfOWJ1jEYRgltIM97j6kKOCI2pHVWMyxZvkU/JKmRBWcIYgDS2PK+yDgVqm7u3crw== + version "3.13.5" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.13.5.tgz#105a813f2eb2329c4aae27373a281d610ee4985f" + integrity sha512-3n8heFQDSk+NcwBn3CgxEibZGaRzx+pC64n3YjpMD1qguV4nWus3Al+Oo3KooqFKTQEJ1v7MmnbnyyNspgx3NA== dependencies: cli-truncate "^2.1.0" - clone "^2.1.2" colorette "^2.0.16" log-update "^4.0.0" p-map "^4.0.0" + rfdc "^1.3.0" rxjs "^7.4.0" through "^2.3.8" wrap-ansi "^7.0.0" @@ -8536,9 +8714,9 @@ log4js@^6.2.1: streamroller "^2.2.4" loglevel@^1.6.8: - version "1.7.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" - integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" + integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" @@ -8554,6 +8732,16 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -8672,9 +8860,9 @@ media-typer@0.3.0: integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= memfs@^3.1.2, memfs@^3.2.2: - version "3.3.0" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.3.0.tgz#4da2d1fc40a04b170a56622c7164c6be2c4cbef2" - integrity sha512-BEE62uMfKOavX3iG7GYX43QJ+hAeeWnwIAuJ/R6q96jaMtiLzhsxHJC8B1L7fK7Pt/vXDRwb3SG/yBpNGDPqzg== + version "3.4.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.0.tgz#8bc12062b973be6b295d4340595736a656f0a257" + integrity sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA== dependencies: fs-monkey "1.0.3" @@ -8788,7 +8976,7 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.3.1, mime@^2.4.4, mime@^2.4.5: +mime@^2.4.4, mime@^2.4.5: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -8798,6 +8986,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + mini-css-extract-plugin@0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.0.tgz#a0e6bfcad22a9c73f6c882a3c7557a98e2d3d27d" @@ -9029,6 +9222,11 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +mrmime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b" + integrity sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -9242,6 +9440,22 @@ node-releases@^2.0.1: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +nodemon@^2.0.15: + version "2.0.15" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.15.tgz#504516ce3b43d9dc9a955ccd9ec57550a31a8d4e" + integrity sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.8" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + update-notifier "^5.1.0" + nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -9249,6 +9463,13 @@ nopt@^5.0.0: dependencies: abbrev "1" +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -9281,7 +9502,7 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -normalize-url@^4.5.0: +normalize-url@^4.1.0, normalize-url@^4.5.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== @@ -9449,9 +9670,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.11.0, object-inspect@^1.9.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== + version "1.11.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.1.tgz#d4bd7d7de54b9a75599f59a00bd698c1f1c6549b" + integrity sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA== object-is@^1.0.1: version "1.1.5" @@ -9674,12 +9895,10 @@ ospath@^1.2.2: resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= -p-event@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" - integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== - dependencies: - p-timeout "^3.1.0" +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== p-finally@^1.0.0: version "1.0.0" @@ -9748,18 +9967,21 @@ p-retry@^4.5.0: "@types/retry" "^0.12.0" retry "^0.13.1" -p-timeout@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" - integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== - dependencies: - p-finally "^1.0.0" - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + pacote@11.2.4: version "11.2.4" resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.2.4.tgz#dc7ca740a573ed86a3bf863511d22c1d413ec82f" @@ -10857,12 +11079,12 @@ postcss-reduce-initial@^4.0.3: has "^1.0.0" postcss "^7.0.0" -postcss-reduce-initial@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz#9d6369865b0f6f6f6b165a0ef5dc1a4856c7e946" - integrity sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw== +postcss-reduce-initial@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz#fa424ce8aa88a89bc0b6d0f94871b24abe94c048" + integrity sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw== dependencies: - browserslist "^4.16.0" + browserslist "^4.16.6" caniuse-api "^3.0.0" postcss-reduce-transforms@^4.0.2: @@ -10991,9 +11213,9 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3: integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: version "2.0.1" @@ -11049,13 +11271,13 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2 source-map "^0.6.1" postcss@^8.1.4, postcss@^8.3.7: - version "8.3.11" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858" - integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA== + version "8.4.4" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.4.tgz#d53d4ec6a75fd62557a66bb41978bf47ff0c2869" + integrity sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q== dependencies: nanoid "^3.1.30" picocolors "^1.0.0" - source-map-js "^0.6.2" + source-map-js "^1.0.1" prelude-ls@~1.1.2: version "1.1.2" @@ -11067,6 +11289,11 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + pretty-bytes@^5.3.0, pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -11187,6 +11414,11 @@ psl@^1.1.28, psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" @@ -11239,6 +11471,13 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + q@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" @@ -11368,6 +11607,16 @@ raw-loader@4.0.2: loader-utils "^2.0.0" schema-utils "^3.0.0" +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + re-reselect@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-4.0.0.tgz#9ddec4c72c4d952f68caa5aa4b76a9ed38b75cac" @@ -11476,9 +11725,9 @@ react-full-screen@^0.2.4: fscreen "^1.0.1" react-i18next@^11.7.0: - version "11.14.2" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.14.2.tgz#2ff28f6a0ddf06eaf79435ed6c70c441d709cf34" - integrity sha512-fmDhwNA0zDmSEL3BBT5qwNMvxrKu25oXDDAZyHprfB0AHZmWXfBmRLf8MX8i1iBd2I2C2vsA2D9wxYBIwzooEQ== + version "11.14.3" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.14.3.tgz#b44b5c4d1aadac5211be011827a2830be60f2522" + integrity sha512-Hf2aanbKgYxPjG8ZdKr+PBz9sY6sxXuZWizxCYyJD2YzvJ0W9JTQcddVEjDaKyBoCyd3+5HTerdhc9ehFugc6g== dependencies: "@babel/runtime" "^7.14.5" html-parse-stringify "^3.0.1" @@ -11662,9 +11911,9 @@ redux-saga@^1.1.3: "@redux-saga/core" "^1.1.3" redux-thunk@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.0.tgz#ac89e1d6b9bdb9ee49ce69a69071be41bbd82d67" - integrity sha512-/y6ZKQNU/0u8Bm7ROLq9Pt/7lU93cT0IucYMrubo89ENjxPa7i8pqLKu6V4X7/TvYovQ6x01unTeyeZ9lgXiTA== + version "2.4.1" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714" + integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q== redux@^4.0.0, redux@^4.0.4, redux@^4.0.5: version "4.1.2" @@ -11745,6 +11994,20 @@ regexpu-core@^4.7.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + regjsgen@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" @@ -11842,9 +12105,9 @@ requires-port@^1.0.0: integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= reselect@^4.0.0: - version "4.1.4" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.4.tgz#66df0aff41b6ee0f51e2cc17cfaf2c1995916f32" - integrity sha512-i1LgXw8DKSU5qz1EV0ZIKz4yIUHJ7L3bODh+Da6HmVSm9vdL/hG7IpbgzQ3k2XSirzf8/eI7OMEs81gb1VV2fQ== + version "4.1.5" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6" + integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ== resolve-cwd@^2.0.0: version "2.0.0" @@ -11931,6 +12194,13 @@ resp-modifier@6.0.2: debug "^2.2.0" minimatch "^3.0.2" +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -11977,7 +12247,7 @@ rework@1.0.1: convert-source-map "^0.3.3" css "^2.0.0" -rfdc@^1.1.4: +rfdc@^1.1.4, rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== @@ -12029,9 +12299,9 @@ rollup@2.38.4: fsevents "~2.3.1" rtl-css-js@^1.13.1: - version "1.14.5" - resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.5.tgz#fd92685acd024e688dda899a28c5fb9270b79974" - integrity sha512-+ng7LWVvPjQUdgDVviR6vKi2X4JiBtlw5rdY0UM5/Cj39c2/KDUsY/VxEzGE25m4KR5g0dvuKfrDq7DaoDooIA== + version "1.15.0" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.15.0.tgz#680ed816e570a9ebccba9e1cd0f202c6a8bb2dc0" + integrity sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew== dependencies: "@babel/runtime" "^7.1.2" @@ -12241,7 +12511,7 @@ schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6 ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.0: +schema-utils@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== @@ -12289,6 +12559,13 @@ selfsigned@^1.10.11, selfsigned@^1.10.7, selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + semver-dsl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/semver-dsl/-/semver-dsl-1.0.1.tgz#d3678de5555e8a61f629eed025366ae5f27340a0" @@ -12320,12 +12597,12 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" -semver@^5.0.0, semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +semver@^5.0.0, semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -12526,9 +12803,9 @@ side-channel@^1.0.4: object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.5" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" - integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== simple-swizzle@^0.2.2: version "0.2.2" @@ -12538,12 +12815,12 @@ simple-swizzle@^0.2.2: is-arrayish "^0.3.1" sirv@^1.0.7: - version "1.0.18" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.18.tgz#105fab52fb656ce8a2bebbf36b11052005952899" - integrity sha512-f2AOPogZmXgJ9Ma2M22ZEhc1dNtRIzcEkiflMFeVTRq+OViOZMvH1IPMVOwrKaxpSaHioBJiDR0SluRqGa7atA== + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== dependencies: "@polka/url" "^1.0.0-next.20" - mime "^2.3.1" + mrmime "^1.0.0" totalist "^1.0.0" sisteransi@^1.0.5: @@ -12707,12 +12984,12 @@ sockjs@0.3.20: websocket-driver "0.6.5" sockjs@^0.3.21: - version "0.3.21" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417" - integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw== + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== dependencies: faye-websocket "^0.11.3" - uuid "^3.4.0" + uuid "^8.3.2" websocket-driver "^0.7.4" socks-proxy-agent@^5.0.0: @@ -12749,10 +13026,10 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== +source-map-js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" + integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== source-map-loader@1.0.2: version "1.0.2" @@ -12804,10 +13081,10 @@ source-map-support@0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.17, source-map-support@^0.5.5, source-map-support@~0.5.12, source-map-support@~0.5.20: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== +source-map-support@^0.5.17, source-map-support@^0.5.5, source-map-support@~0.5.12, source-map-support@~0.5.19, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -12819,14 +13096,6 @@ source-map-support@~0.4.0: dependencies: source-map "^0.5.6" -source-map-support@~0.5.19: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-url@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" @@ -12911,7 +13180,7 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -sshpk@^1.7.0: +sshpk@^1.14.1, sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== @@ -13060,7 +13329,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13153,6 +13422,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + style-loader@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" @@ -13225,7 +13499,7 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -13529,6 +13803,11 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -13574,6 +13853,13 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -13722,6 +14008,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -13745,6 +14036,13 @@ type@^2.5.0: resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -13809,6 +14107,11 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + unfetch@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" @@ -13871,6 +14174,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + universal-analytics@0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" @@ -13918,6 +14228,26 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -13930,6 +14260,13 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + url-parse@^1.4.3, url-parse@^1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" @@ -14295,9 +14632,9 @@ webpack-dev-server@3.11.2: yargs "^13.3.2" webpack-dev-server@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.5.0.tgz#614b5112cfa4730a4801bb4ddebb3be5b0d70497" - integrity sha512-Ss4WptsUjYa+3hPI4iYZYEc8FrtnfkaPrm5WTjk9ux5kiCS718836srs0ppKMHRaCHP5mQ6g4JZGcfDdGbCjpQ== + version "4.6.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.6.0.tgz#e8648601c440172d9b6f248d28db98bed335315a" + integrity sha512-oojcBIKvx3Ya7qs1/AVWHDgmP1Xml8rGsEBnSobxU/UJSX1xP1GPM3MwsAnDzvqcVmVki8tV7lbcsjEjk0PtYg== dependencies: ansi-html-community "^0.0.8" bonjour "^3.5.0" @@ -14305,17 +14642,17 @@ webpack-dev-server@^4.5.0: colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^1.6.0" + default-gateway "^6.0.3" del "^6.0.0" express "^4.17.1" graceful-fs "^4.2.6" html-entities "^2.3.2" http-proxy-middleware "^2.0.0" - internal-ip "^6.2.0" ipaddr.js "^2.0.1" open "^8.0.9" p-retry "^4.5.0" portfinder "^1.0.28" - schema-utils "^3.1.0" + schema-utils "^4.0.0" selfsigned "^1.10.11" serve-index "^1.9.1" sockjs "^0.3.21" @@ -14565,6 +14902,13 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" @@ -14621,6 +14965,16 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + ws@^6.2.1: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" @@ -14629,20 +14983,25 @@ ws@^6.2.1: async-limiter "~1.0.0" ws@^7.3.1, ws@^7.4.6: - version "7.5.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== + version "7.5.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" + integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== ws@^8.1.0: - version "8.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" - integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + version "8.3.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d" + integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw== ws@~7.4.2: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + xhr2@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.2.1.tgz#4e73adc4f9cfec9cbd2157f73efdce3a5f108a93" From 46bb3e109c9bd2abcc965fdf90f0a210cf0dcb25 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 7 Dec 2021 11:38:48 -0600 Subject: [PATCH 017/102] cleanup and readme updates --- README.md | 155 +++++++++++++++++++++++++----------- docs/Configuration.md | 74 ++++++++++++----- src/config/config.server.ts | 4 +- src/config/config.util.ts | 6 +- 4 files changed, 165 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 69b6132478..41df8d0223 100644 --- a/README.md +++ b/README.md @@ -102,18 +102,18 @@ Installing ### Configuring -Default configuration file is located in `src/environments/` folder. +Default configuration file is located in `config/` folder. -To change the default configuration values, create local files that override the parameters you need to change. You can use `environment.template.ts` as a starting point. +To change the default configuration values, create local files that override the parameters you need to change. You can use `appConfig.json` as a starting point. -- Create a new `environment.dev.ts` file in `src/environments/` for a `development` environment; -- Create a new `environment.prod.ts` file in `src/environments/` for a `production` environment; +- Create a new `appConfig.(dev or development).json` file in `config/` for a `development` environment; +- Create a new `appConfig.(prod or production).ts` file in `config/` for a `production` environment; -The server settings can also be overwritten using an environment file. +The settings can also be overwritten using an environment file or environment variables. This file should be called `.env` and be placed in the project root. -The following settings can be overwritten in this file: +The following non-convention settings: ```bash DSPACE_HOST # The host name of the angular application @@ -127,23 +127,44 @@ DSPACE_REST_NAMESPACE # The namespace of the REST application DSPACE_REST_SSL # Whether the angular REST uses SSL [true/false] ``` +All other settings can be set using the following convention for naming the environment variables: + +1. replace all `.` with `_` +2. convert all characters to upper case + +e.g. + +``` +cache.msToLive.default => CACHE_MSTOLIVE_DEFAULT +auth.ui.timeUntilIdle => AUTH_UI_TIMEUNTILIDLE +``` + The same settings can also be overwritten by setting system environment variables instead, E.g.: ```bash export DSPACE_HOST=api7.dspace.org ``` -The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides **`environment.(prod, dev or test).ts`** overrides **`environment.common.ts`** +The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `APP_CONFIG_PATH` overrides **`appConfig.(prod or dev).json`** -These configuration sources are collected **at build time**, and written to `src/environments/environment.ts`. At runtime the configuration is fixed, and neither `.env` nor the process' environment will be consulted. +These configuration sources are collected **at run time**, and written to `dist/browser/assets/appConfig.json` for production and `src/app/assets/appConfig.json` for development. + +The configuration file can be externalized by using environment variable `APP_CONFIG_PATH`. #### Using environment variables in code To use environment variables in a UI component, use: ```typescript -import { environment } from '../environment.ts'; +import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; +... +constructor(@Inject(APP_CONFIG) private appConfig: AppConfig) {} +... ``` -This file is generated by the script located in `scripts/set-env.ts`. This script will run automatically before every build, or can be manually triggered using the appropriate `config` script in `package.json` +or + +```typescript +import { environment } from '../environment.ts'; +``` Running the app @@ -230,7 +251,7 @@ 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, 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 `environment.prod.ts` or `environment.common.ts`. You may override this using env variables, see [Configuring](#configuring). +Before you can run e2e tests, 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 `appConfig.prod.json` or `appConfig.json`. You may override this using env variables, see [Configuring](#configuring). 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. @@ -309,49 +330,87 @@ File Structure ``` dspace-angular -├── README.md * This document -├── app.yaml * Application manifest file -├── config * Folder for configuration files -│   ├── environment.default.js * Default configuration files -│   └── environment.test.js * Test configuration files -├── docs * Folder for documentation +├── config * +│ └── appConfig.json * Default app config ├── cypress * Folder for Cypress (https://cypress.io/) / e2e tests -│   ├── integration * Folder for e2e/integration test files -│   ├── fixtures * Folder for any fixtures needed by e2e tests -│   ├── plugins * Folder for Cypress plugins (if any) -│   ├── support * Folder for global e2e test actions/commands (run for all tests) -│   └── tsconfig.json * TypeScript configuration file for e2e tests +│ ├── downloads * +│ ├── fixtures * Folder for e2e/integration test files +│ ├── integration * Folder for any fixtures needed by e2e tests +│ ├── plugins * Folder for Cypress plugins (if any) +│ ├── support * Folder for global e2e test actions/commands (run for all tests) +│ └── tsconfig.json * TypeScript configuration file for e2e tests +├── docker * +│ ├── cli.assetstore.yml * +│ ├── cli.ingest.yml * +│ ├── cli.yml * +│ ├── db.entities.yml * +│ ├── docker-compose-ci.yml * +│ ├── docker-compose-rest.yml * +│ ├── docker-compose.yml * +│ ├── environment.dev.ts * +│ ├── local.cfg * +│ └── README.md * +├── docs * Folder for documentation +│ └── Configuration.md * Configuration documentation +├── scripts * +│ ├── merge-i18n-files.ts * +│ ├── serve.ts * +│ ├── sync-i18n-files.ts * +│ ├── test-rest.ts * +│ └── webpack.js * +├── src * The source of the application +│ ├── app * The source code of the application, subdivided by module/page. +│ ├── assets * Folder for static resources +│ │ ├── fonts * Folder for fonts +│ │ ├── i18n * Folder for i18n translations +│ │ └── images * Folder for images +│ ├── backend * Folder containing a mock of the REST API, hosted by the express server +│ ├── config * +│ ├── environments * +│ │ ├── environment.prod.ts * Production configuration files +│ │ ├── environment.test.ts * Test configuration files +│ │ └── environment.ts * Default (development) configuration files +│ ├── mirador-viewer * +│ ├── modules * +│ ├── ngx-translate-loaders * +│ ├── styles * Folder containing global styles +│ ├── themes * Folder containing available themes +│ │ ├── custom * Template folder for creating a custom theme +│ │ └── dspace * Default 'dspace' theme +│ ├── index.csr.html * The index file for client side rendering fallback +│ ├── index.html * The index file +│ ├── main.browser.ts * The bootstrap file for the client +│ ├── main.server.ts * The express (http://expressjs.com/) config and bootstrap file for the server +│ ├── polyfills.ts * +│ ├── robots.txt * The robots.txt file +│ ├── test.ts * +│ └── typings.d.ts * +├── webpack * +│ ├── helpers.ts * Webpack helpers +│ ├── webpack.browser.ts * Webpack (https://webpack.github.io/) config for browser build +│ ├── webpack.common.ts * Webpack (https://webpack.github.io/) common build config +│ ├── webpack.mirador.config.ts * Webpack (https://webpack.github.io/) config for mirador config build +│ ├── webpack.prod.ts * Webpack (https://webpack.github.io/) config for prod build +│ └── webpack.test.ts * Webpack (https://webpack.github.io/) config for test build +├── angular.json * Angular CLI (https://angular.io/cli) configuration +├── cypress.json * Cypress Test (https://www.cypress.io/) configuration +├── Dockerfile * ├── karma.conf.js * Karma configuration file for Unit Test +├── LICENSE * +├── LICENSES_THIRD_PARTY * ├── nodemon.json * Nodemon (https://nodemon.io/) configuration ├── package.json * This file describes the npm package for this project, its dependencies, scripts, etc. -├── postcss.config.js * PostCSS (http://postcss.org/) configuration file -├── src * The source of the application -│   ├── app * The source code of the application, subdivided by module/page. -│   ├── assets * Folder for static resources -│   │   ├── fonts * Folder for fonts -│   │   ├── i18n * Folder for i18n translations -│   | └── en.json5 * i18n translations for English -│   │   └── images * Folder for images -│   ├── backend * Folder containing a mock of the REST API, hosted by the express server -│   ├── config * -│   ├── index.csr.html * The index file for client side rendering fallback -│   ├── index.html * The index file -│   ├── main.browser.ts * The bootstrap file for the client -│   ├── main.server.ts * The express (http://expressjs.com/) config and bootstrap file for the server -│   ├── robots.txt * The robots.txt file -│   ├── modules * -│   ├── styles * Folder containing global styles -│   └── themes * Folder containing available themes -│      ├── custom * Template folder for creating a custom theme -│      └── dspace * Default 'dspace' theme -├── tsconfig.json * TypeScript config +├── postcss.config.js * PostCSS (http://postcss.org/) configuration +├── README.md * This document +├── SECURITY.md * +├── server.ts * Angular Universal Node.js Express server +├── tsconfig.app.json * TypeScript config for browser (app) +├── tsconfig.json * TypeScript common config +├── tsconfig.server.json * TypeScript config for server +├── tsconfig.spec.json * TypeScript config for tests +├── tsconfig.ts-node.json * TypeScript config for using ts-node directly ├── tslint.json * TSLint (https://palantir.github.io/tslint/) configuration ├── typedoc.json * TYPEDOC configuration -├── webpack * Webpack (https://webpack.github.io/) config directory -│   ├── webpack.browser.ts * Webpack (https://webpack.github.io/) config for client build -│   ├── webpack.common.ts * -│   ├── webpack.prod.ts * Webpack (https://webpack.github.io/) config for production build -│   └── webpack.test.ts * Webpack (https://webpack.github.io/) config for test build └── yarn.lock * Yarn lockfile (https://yarnpkg.com/en/docs/yarn-lock) ``` diff --git a/docs/Configuration.md b/docs/Configuration.md index f4fff1166c..82aaa42c40 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1,26 +1,32 @@ # Configuration -Default configuration file is located in `src/environments/` folder. All configuration options should be listed in the default configuration file `src/environments/environment.common.ts`. Please do not change this file directly! To change the default configuration values, create local files that override the parameters you need to change. You can use `environment.template.ts` as a starting point. +Default configuration file is located in `config/` folder. All configuration options should be listed in the default configuration file `src/config/default-app-config.ts`. Please do not change this file directly! To change the default configuration values, create local files that override the parameters you need to change. You can use `appConfig.json` as a starting point. -- Create a new `environment.dev.ts` file in `src/environments/` for `development` environment; -- Create a new `environment.prod.ts` file in `src/environments/` for `production` environment; +- Create a new `appConfig.(dev or development).json` file in `config/` for `development` environment; +- Create a new `appConfig.(prod or production).json` file in `config/` for `production` environment; -Some few configuration options can be overridden by setting environment variables. These and the variable names are listed below. +Alternatively, create a desired app config file at an external location and set the path as environment variable `APP_CONFIG_PATH`. + +e.g. +``` +APP_CONFIG_PATH=/usr/local/dspace/config/appConfig.json +``` + +Configuration options can be overridden by setting environment variables. ## Nodejs server When you start dspace-angular on node, it spins up an http server on which it listens for incoming connections. You can define the ip address and port the server should bind itsself to, and if ssl should be enabled not. By default it listens on `localhost:4000`. If you want it to listen on all your network connections, configure it to bind itself to `0.0.0.0`. To change this configuration, change the options `ui.host`, `ui.port` and `ui.ssl` in the appropriate configuration file (see above): ``` -export const environment = { - // Angular UI settings. - ui: { - ssl: false, - host: 'localhost', - port: 4000, - nameSpace: '/' +{ + "ui": { + "ssl": false, + "host": "localhost", + "port": 4000, + "nameSpace": "/" } -}; +} ``` Alternately you can set the following environment variables. If any of these are set, it will override all configuration files: @@ -30,21 +36,26 @@ Alternately you can set the following environment variables. If any of these are DSPACE_PORT=4000 DSPACE_NAMESPACE=/ ``` +or +``` + UI_SSL=true + UI_HOST=localhost + UI_PORT=4000 + UI_NAMESPACE=/ +``` ## DSpace's REST endpoint dspace-angular connects to your DSpace installation by using its REST endpoint. To do so, you have to define the ip address, port and if ssl should be enabled. You can do this in a configuration file (see above) by adding the following options: ``` -export const environment = { - // The REST API server settings. - rest: { - ssl: true, - host: 'api7.dspace.org', - port: 443, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/server' +{ + "rest": { + "ssl": true, + "host": "api7.dspace.org", + "port": 443, + "nameSpace": "/server" } -}; +} ``` Alternately you can set the following environment variables. If any of these are set, it will override all configuration files: @@ -54,6 +65,27 @@ Alternately you can set the following environment variables. If any of these are DSPACE_REST_PORT=443 DSPACE_REST_NAMESPACE=/server ``` +or +``` + REST_SSL=true + REST_HOST=api7.dspace.org + REST_PORT=443 + REST_NAMESPACE=/server +``` + +## Environment variable naming convention + +Settings can be set using the following convention for naming the environment variables: + +1. replace all `.` with `_` +2. convert all characters to upper case + +e.g. + +``` +cache.msToLive.default => CACHE_MSTOLIVE_DEFAULT +auth.ui.timeUntilIdle => AUTH_UI_TIMEUNTILIDLE +``` ## Supporting analytics services other than Google Analytics This project makes use of [Angulartics](https://angulartics.github.io/angulartics2/) to track usage events and send them to Google Analytics. diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 51a8c8119f..da33f553a3 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -6,7 +6,7 @@ import { AppConfig } from './app-config.interface'; import { Config } from './config.interface'; import { DefaultAppConfig } from './default-app-config'; import { ServerConfig } from './server-config.interface'; -import { extendConfig } from './config.util'; +import { mergeConfig } from './config.util'; import { isNotEmpty } from '../app/shared/empty.util'; const CONFIG_PATH = join(process.cwd(), 'config'); @@ -78,7 +78,7 @@ const overrideWithConfig = (config: Config, pathToConfig: string) => { try { console.log(`Overriding app config with ${pathToConfig}`); const externalConfig = fs.readFileSync(pathToConfig, 'utf8'); - extendConfig(config, JSON.parse(externalConfig)); + mergeConfig(config, JSON.parse(externalConfig)); } catch (err) { console.error(err); } diff --git a/src/config/config.util.ts b/src/config/config.util.ts index b576e99eba..9f2d7d349e 100644 --- a/src/config/config.util.ts +++ b/src/config/config.util.ts @@ -8,11 +8,11 @@ import { AppConfig } from './app-config.interface'; import { ThemeConfig } from './theme.model'; const extendEnvironmentWithAppConfig = (env: any, appConfig: AppConfig): void => { - extendConfig(env, appConfig); + mergeConfig(env, appConfig); console.log(`Environment extended with app config`); }; -const extendConfig = (config: any, appConfig: AppConfig): void => { +const mergeConfig = (config: any, appConfig: AppConfig): void => { const mergeOptions = { arrayMerge: (destinationArray, sourceArray, options) => sourceArray }; @@ -32,6 +32,6 @@ const getDefaultThemeConfig = (): ThemeConfig => { export { extendEnvironmentWithAppConfig, - extendConfig, + mergeConfig, getDefaultThemeConfig }; From 18dd2ad8841476d0e960ae5e08b641508d4325be Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 7 Dec 2021 14:45:46 -0600 Subject: [PATCH 018/102] add simple config util test --- src/config/config.util.spec.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/config/config.util.spec.ts diff --git a/src/config/config.util.spec.ts b/src/config/config.util.spec.ts new file mode 100644 index 0000000000..cb2fdcdba2 --- /dev/null +++ b/src/config/config.util.spec.ts @@ -0,0 +1,17 @@ +import { environment } from '../environments/environment.prod'; +import { extendEnvironmentWithAppConfig } from './config.util'; +import { DefaultAppConfig } from './default-app-config'; + +describe('Config Util', () => { + describe('extendEnvironmentWithAppConfig', () => { + it('should extend prod environment with app config', () => { + const appConfig = new DefaultAppConfig(); + const originalMsToLive = appConfig.cache.msToLive.default; + expect(originalMsToLive).toEqual(15 * 60 * 1000); // 15 minute + const msToLive = 1 * 60 * 1000; // 1 minute + appConfig.cache.msToLive.default = msToLive; + extendEnvironmentWithAppConfig(environment, appConfig); + expect(environment.cache.msToLive.default).toEqual(msToLive); + }); + }); +}); From bc999d0b5f13e603fabc7668fbc2c867a4a29526 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 7 Dec 2021 21:33:43 -0600 Subject: [PATCH 019/102] minor refactoring and more config util assertions --- src/config/config.server.ts | 29 ++++++++++++++--------- src/config/config.util.spec.ts | 43 ++++++++++++++++++++++++++++++++-- src/config/config.util.ts | 25 ++++++++++++++++---- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/config/config.server.ts b/src/config/config.server.ts index da33f553a3..5e8d8e1587 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -11,8 +11,6 @@ import { isNotEmpty } from '../app/shared/empty.util'; const CONFIG_PATH = join(process.cwd(), 'config'); -const APP_CONFIG_PATH = join(CONFIG_PATH, 'appConfig.json'); - type Environment = 'production' | 'development' | 'test'; const getBooleanFromString = (variable: string): boolean => { @@ -47,7 +45,7 @@ const getEnvironment = (): Environment => { const getLocalConfigPath = (env: Environment) => { // default to config/appConfig.json - let localConfigPath = APP_CONFIG_PATH; + let localConfigPath = join(CONFIG_PATH, 'appConfig.json'); // determine app config filename variations let envVariations; @@ -65,9 +63,9 @@ const getLocalConfigPath = (env: Environment) => { // check if any environment variations of app config exist for (const envVariation of envVariations) { - const altDistConfigPath = join(CONFIG_PATH, `appConfig.${envVariation}.json`); - if (fs.existsSync(altDistConfigPath)) { - localConfigPath = altDistConfigPath; + const envLocalConfigPath = join(CONFIG_PATH, `appConfig.${envVariation}.json`); + if (fs.existsSync(envLocalConfigPath)) { + localConfigPath = envLocalConfigPath; } } @@ -106,7 +104,6 @@ const overrideWithEnvironment = (config: Config, key: string = '') => { default: console.warn(`Unsupported environment variable type ${typeof innerConfig} ${variable}`); } - } } } @@ -122,6 +119,16 @@ const buildBaseUrl = (config: ServerConfig): void => { ].join(''); }; +/** + * Build app config with the following chain of override. + * + * local config -> environment local config -> external config -> environment variable + * + * Optionally save to file. + * + * @param destConfigPath optional path to save config file + * @returns app config + */ export const buildAppConfig = (destConfigPath?: string): AppConfig => { // start with default app config const appConfig: AppConfig = new DefaultAppConfig(); @@ -141,11 +148,11 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => { } // override with dist config - const distConfigPath = getLocalConfigPath(env); - if (fs.existsSync(distConfigPath)) { - overrideWithConfig(appConfig, distConfigPath); + const localConfigPath = getLocalConfigPath(env); + if (fs.existsSync(localConfigPath)) { + overrideWithConfig(appConfig, localConfigPath); } else { - console.warn(`Unable to find dist config file at ${distConfigPath}`); + console.warn(`Unable to find dist config file at ${localConfigPath}`); } // override with external config if specified by environment variable `APP_CONFIG_PATH` diff --git a/src/config/config.util.spec.ts b/src/config/config.util.spec.ts index cb2fdcdba2..f9989f5a83 100644 --- a/src/config/config.util.spec.ts +++ b/src/config/config.util.spec.ts @@ -1,17 +1,56 @@ import { environment } from '../environments/environment.prod'; import { extendEnvironmentWithAppConfig } from './config.util'; import { DefaultAppConfig } from './default-app-config'; +import { HandleThemeConfig } from './theme.model'; describe('Config Util', () => { describe('extendEnvironmentWithAppConfig', () => { it('should extend prod environment with app config', () => { const appConfig = new DefaultAppConfig(); - const originalMsToLive = appConfig.cache.msToLive.default; - expect(originalMsToLive).toEqual(15 * 60 * 1000); // 15 minute + expect(appConfig.cache.msToLive.default).toEqual(15 * 60 * 1000); // 15 minute + expect(appConfig.ui.rateLimiter.windowMs).toEqual(1 * 60 * 1000); // 1 minute + expect(appConfig.ui.rateLimiter.max).toEqual(500); + + expect(appConfig.submission.autosave.metadata).toEqual([]); + + expect(appConfig.themes.length).toEqual(1); + expect(appConfig.themes[0].name).toEqual('dspace'); + const msToLive = 1 * 60 * 1000; // 1 minute appConfig.cache.msToLive.default = msToLive; + + const rateLimiter = { + windowMs: 5 * 50 * 1000, // 5 minutes + max: 1000 + }; + appConfig.ui.rateLimiter = rateLimiter; + + const autoSaveMetadata = [ + 'dc.author', + 'dc.title' + ]; + + appConfig.submission.autosave.metadata = autoSaveMetadata; + + const customTheme: HandleThemeConfig = { + name: 'custom', + handle: '10673/1233' + }; + + appConfig.themes.push(customTheme); + extendEnvironmentWithAppConfig(environment, appConfig); + expect(environment.cache.msToLive.default).toEqual(msToLive); + expect(environment.ui.rateLimiter.windowMs).toEqual(rateLimiter.windowMs); + expect(environment.ui.rateLimiter.max).toEqual(rateLimiter.max); + expect(environment.submission.autosave.metadata[0]).toEqual(autoSaveMetadata[0]); + expect(environment.submission.autosave.metadata[1]).toEqual(autoSaveMetadata[1]); + + expect(environment.themes.length).toEqual(2); + expect(environment.themes[0].name).toEqual('dspace'); + expect(environment.themes[1].name).toEqual(customTheme.name); + expect((environment.themes[1] as HandleThemeConfig).handle).toEqual(customTheme.handle); }); }); }); diff --git a/src/config/config.util.ts b/src/config/config.util.ts index 9f2d7d349e..6c3b9e0f7a 100644 --- a/src/config/config.util.ts +++ b/src/config/config.util.ts @@ -7,21 +7,38 @@ import { hasNoValue } from '../app/shared/empty.util'; import { AppConfig } from './app-config.interface'; import { ThemeConfig } from './theme.model'; +/** + * Extend Angular environment with app config. + * + * @param env environment object + * @param appConfig app config + */ const extendEnvironmentWithAppConfig = (env: any, appConfig: AppConfig): void => { mergeConfig(env, appConfig); console.log(`Environment extended with app config`); }; -const mergeConfig = (config: any, appConfig: AppConfig): void => { +/** + * Merge one config into another. + * + * @param destinationConfig destination config + * @param sourceConfig source config + */ +const mergeConfig = (destinationConfig: any, sourceConfig: AppConfig): void => { const mergeOptions = { arrayMerge: (destinationArray, sourceArray, options) => sourceArray }; - Object.assign(config, merge.all([ - config, - appConfig + Object.assign(destinationConfig, merge.all([ + destinationConfig, + sourceConfig ], mergeOptions)); }; +/** + * Get default them config from environment. + * + * @returns default theme config + */ const getDefaultThemeConfig = (): ThemeConfig => { return environment.themes.find((themeConfig: any) => hasNoValue(themeConfig.regex) && From 10622008c407fdb77d3df24507cc580e75862685 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 8 Dec 2021 09:38:52 -0600 Subject: [PATCH 020/102] refactor config filename and convert to yaml --- README.md | 14 +- config/.gitignore | 3 +- config/appConfig.json | 8 -- config/config.example.yml | 231 +++++++++++++++++++++++++++++++ config/config.yml | 5 + docs/Configuration.md | 8 +- package.json | 6 +- scripts/env-to-yaml.ts | 32 +++++ server.ts | 2 +- src/app/app.component.ts | 2 - src/assets/.gitignore | 2 +- src/config/config.server.ts | 22 ++- src/config/default-app-config.ts | 24 ++-- src/main.browser.ts | 2 +- webpack/webpack.browser.ts | 2 +- yarn.lock | 12 ++ 16 files changed, 330 insertions(+), 45 deletions(-) delete mode 100644 config/appConfig.json create mode 100644 config/config.example.yml create mode 100644 config/config.yml create mode 100644 scripts/env-to-yaml.ts diff --git a/README.md b/README.md index 41df8d0223..06c977915a 100644 --- a/README.md +++ b/README.md @@ -104,10 +104,10 @@ Installing Default configuration file is located in `config/` folder. -To change the default configuration values, create local files that override the parameters you need to change. You can use `appConfig.json` as a starting point. +To override the default configuration values, create local files that override the parameters you need to change. You can use `config.example.yml` as a starting point. -- Create a new `appConfig.(dev or development).json` file in `config/` for a `development` environment; -- Create a new `appConfig.(prod or production).ts` file in `config/` for a `production` environment; +- Create a new `config.(dev or development).yml` file in `config/` for a `development` environment; +- Create a new `config.(prod or production).yml` file in `config/` for a `production` environment; The settings can also be overwritten using an environment file or environment variables. @@ -144,9 +144,9 @@ The same settings can also be overwritten by setting system environment variable export DSPACE_HOST=api7.dspace.org ``` -The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `APP_CONFIG_PATH` overrides **`appConfig.(prod or dev).json`** +The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`** -These configuration sources are collected **at run time**, and written to `dist/browser/assets/appConfig.json` for production and `src/app/assets/appConfig.json` for development. +These configuration sources are collected **at run time**, and written to `dist/browser/assets/config.json` for production and `src/app/assets/config.json` for development. The configuration file can be externalized by using environment variable `APP_CONFIG_PATH`. @@ -251,7 +251,7 @@ 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, 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 `appConfig.prod.json` or `appConfig.json`. You may override this using env variables, see [Configuring](#configuring). +Before you can run e2e tests, 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). 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. @@ -331,7 +331,7 @@ File Structure ``` dspace-angular ├── config * -│ └── appConfig.json * Default app config +│ └── config.yml * Default app config ├── cypress * Folder for Cypress (https://cypress.io/) / e2e tests │ ├── downloads * │ ├── fixtures * Folder for e2e/integration test files diff --git a/config/.gitignore b/config/.gitignore index e1899191e1..a420ca4302 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -1 +1,2 @@ -appConfig.*.json +config.*.yml +!config.example.yml diff --git a/config/appConfig.json b/config/appConfig.json deleted file mode 100644 index a70d8ea6ab..0000000000 --- a/config/appConfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "rest": { - "ssl": true, - "host": "api7.dspace.org", - "port": 443, - "nameSpace": "/server" - } -} diff --git a/config/config.example.yml b/config/config.example.yml new file mode 100644 index 0000000000..165d94b7bf --- /dev/null +++ b/config/config.example.yml @@ -0,0 +1,231 @@ +# NOTE: will log all redux actions and transfers in console +debug: false + +# Angular Universal server settings +# NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. +ui: + ssl: false + host: localhost + port: 4000 + # NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: / + # The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). + rateLimiter: + windowMs: 60000 # 1 minute + max: 500 # limit each IP to 500 requests per windowMs + +# The REST API server settings +# NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. +rest: + ssl: true + host: api7.dspace.org + port: 443 + # NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: /server + +# Caching settings +cache: + # NOTE: how long should objects be cached for by default + msToLive: + default: 900000 # 15 minutes + control: max-age=60 # revalidate browser + autoSync: + defaultTime: 0 + maxBufferSize: 100 + timePerMethod: + PATCH: 3 # time in seconds + +# Authentication settings +auth: + # Authentication UI settings + ui: + # the amount of time before the idle warning is shown + timeUntilIdle: 900000 # 15 minutes + # the amount of time the user has to react after the idle warning is shown before they are logged out. + idleGracePeriod: 300000 # 5 minutes + # Authentication REST settings + rest: + # If the rest token expires in less than this amount of time, it will be refreshed automatically. + # This is independent from the idle warning. + timeLeftBeforeTokenRefresh: 120000 # 2 minutes + +# Form settings +form: + # NOTE: Map server-side validators to comparative Angular form validators + validatorMap: + required: required + regex: pattern + +# Notification settings +notifications: + rtl: false + position: + - top + - right + maxStack: 8 + # NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically + timeOut: 5000 # 5 second + clickToClose: true + # NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' + animate: scale + +# Submission settings +submission: + autosave: + # NOTE: which metadata trigger an autosave + metadata: [] + # NOTE: after how many time (milliseconds) submission is saved automatically + # eg. timer: 5 * (1000 * 60); // 5 minutes + timer: 0 + icons: + metadata: + # NOTE: example of configuration + # # NOTE: metadata name + # - name: dc.author + # # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used + # style: fas fa-user + - name: dc.author + style: fas fa-user + # default configuration + - name: default + style: '' + authority: + confidence: + # NOTE: example of configuration + # # NOTE: confidence value + # - name: dc.author + # # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used + # style: fa-user + - value: 600 + style: text-success + - value: 500 + style: text-info + - value: 400 + style: text-warning + # default configuration + - value: default + style: text-muted + +# Default Language in which the UI will be rendered if the user's browser language is not an active language +defaultLanguage: en + +# Languages. DSpace Angular holds a message catalog for each of the following languages. +# When set to active, users will be able to switch to the use of this language in the user interface. +languages: + - code: en + label: English + active: true + - code: cs + label: Čeština + active: true + - code: de + label: Deutsch + active: true + - code: es + label: Español + active: true + - code: fr + label: Français + active: true + - code: lv + label: Latviešu + active: true + - code: hu + label: Magyar + active: true + - code: nl + label: Nederlands + active: true + - code: pt-PT + label: Português + active: true + - code: pt-BR + label: Português do Brasil + active: true + - code: fi + label: Suomi + active: true + +# Browse-By Pages +browseBy: + # Amount of years to display using jumps of one year (current year - oneYearLimit) + oneYearLimit: 10 + # Limit for years to display using jumps of five years (current year - fiveYearLimit) + fiveYearLimit: 30 + # The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) + defaultLowerLimit: 1900 + # List of all the active Browse-By types + # Adding a type will activate their Browse-By page and add them to the global navigation menu, + # as well as community and collection pages + # Allowed fields and their purpose: + # id: The browse id to use for fetching info from the rest api + # type: The type of Browse-By page to display + # metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') + types: + - id: title + type: title + - id: dateissued + type: date + metadataField: dc.date.issued + - id: author + type: metadata + - id: subject + type: metadata + +# Item Page Config +item: + edit: + undoTimeout: 10000 # 10 seconds + +# Collection Page Config +collection: + edit: + undoTimeout: 10000 # 10 seconds + +# Theme Config +themes: + # Add additional themes here. In the case where multiple themes match a route, the first one + # in this list will get priority. It is advisable to always have a theme that matches + # every route as the last one + # + # # A theme with a handle property will match the community, collection or item with the given + # # handle, and all collections and/or items within it + # - name: 'custom', + # handle: '10673/1233' + # + # # A theme with a regex property will match the route using a regular expression. If it + # # matches the route for a community or collection it will also apply to all collections + # # and/or items within it + # - name: 'custom', + # regex: 'collections\/e8043bc2.*' + # + # # A theme with a uuid property will match the community, collection or item with the given + # # ID, and all collections and/or items within it + # - name: 'custom', + # uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' + # + # # The extends property specifies an ancestor theme (by name). Whenever a themed component is not found + # # in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. + # - name: 'custom-A', + # extends: 'custom-B', + # # Any of the matching properties above can be used + # handle: '10673/34' + # + # - name: 'custom-B', + # extends: 'custom', + # handle: '10673/12' + # + # # A theme with only a name will match every route + # name: 'custom' + # + # # This theme will use the default bootstrap styling for DSpace components + # - name: BASE_THEME_NAME + # + - name: dspace + +# Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). +# For images, this enables a gallery viewer where you can zoom or page through images. +# For videos, this enables embedded video streaming +mediaViewer: + image: false + video: false diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000000..b5eecd112f --- /dev/null +++ b/config/config.yml @@ -0,0 +1,5 @@ +rest: + ssl: true + host: api7.dspace.org + port: 443 + nameSpace: /server diff --git a/docs/Configuration.md b/docs/Configuration.md index 82aaa42c40..d21a41e277 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1,15 +1,15 @@ # Configuration -Default configuration file is located in `config/` folder. All configuration options should be listed in the default configuration file `src/config/default-app-config.ts`. Please do not change this file directly! To change the default configuration values, create local files that override the parameters you need to change. You can use `appConfig.json` as a starting point. +Default configuration file is located at `config/config.yml`. All configuration options should be listed in the default typescript file `src/config/default-app-config.ts`. Please do not change this file directly! To override the default configuration values, create local files that override the parameters you need to change. You can use `config.example.yml` as a starting point. -- Create a new `appConfig.(dev or development).json` file in `config/` for `development` environment; -- Create a new `appConfig.(prod or production).json` file in `config/` for `production` environment; +- Create a new `config.(dev or development).yml` file in `config/` for `development` environment; +- Create a new `config.(prod or production).yml` file in `config/` for `production` environment; Alternatively, create a desired app config file at an external location and set the path as environment variable `APP_CONFIG_PATH`. e.g. ``` -APP_CONFIG_PATH=/usr/local/dspace/config/appConfig.json +APP_CONFIG_PATH=/usr/local/dspace/config/config.yml ``` Configuration options can be overridden by setting environment variables. diff --git a/package.json b/package.json index e0b07b1040..097f9f21ab 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "lint": "ng lint", "lint-fix": "ng lint --fix=true", "e2e": "ng e2e", - "clean:dev:config": "rimraf src/assets/appConfig.json", + "clean:dev:config": "rimraf src/assets/config.json", "clean:coverage": "rimraf coverage", "clean:dist": "rimraf dist", "clean:doc": "rimraf doc", @@ -35,7 +35,8 @@ "build:mirador": "webpack --config webpack/webpack.mirador.config.ts", "merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", "cypress:open": "cypress open", - "cypress:run": "cypress run" + "cypress:run": "cypress run", + "env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts" }, "browser": { "fs": false, @@ -90,6 +91,7 @@ "http-proxy-middleware": "^1.0.5", "https": "1.0.0", "js-cookie": "2.2.1", + "js-yaml": "^4.1.0", "json5": "^2.1.3", "jsonschema": "1.4.0", "jwt-decode": "^3.1.2", diff --git a/scripts/env-to-yaml.ts b/scripts/env-to-yaml.ts new file mode 100644 index 0000000000..47f876f20a --- /dev/null +++ b/scripts/env-to-yaml.ts @@ -0,0 +1,32 @@ +import * as fs from 'fs'; +import * as yaml from 'js-yaml'; +import { join } from 'path'; + +const args = process.argv.slice(2); + +if (args[0] === undefined) { + console.log(`Usage:\n\tyarn env:yaml [relative path to environment.ts file] (optional relative path to write yaml file)\n`); + process.exit(0); +} + +const envFullPath = join(process.cwd(), args[0]); + +if (!fs.existsSync(envFullPath)) { + console.error(`Error:\n${envFullPath} does not exist\n`); + process.exit(1); +} + +try { + const env = require(envFullPath); + + const config = yaml.dump(env); + if (args[1]) { + const ymlFullPath = join(process.cwd(), args[1]); + fs.writeFileSync(ymlFullPath, config); + } else { + console.log(config); + } +} catch (e) { + console.error(e); +} + diff --git a/server.ts b/server.ts index 70d01e7710..da3b877bc1 100644 --- a/server.ts +++ b/server.ts @@ -58,7 +58,7 @@ const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : ' const cookieParser = require('cookie-parser'); -const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/appConfig.json')); +const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/config.json')); // extend environment with app config for server extendEnvironmentWithAppConfig(environment, appConfig); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 2a7a885794..b1039f9c6d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -109,8 +109,6 @@ export class AppComponent implements OnInit, AfterViewInit { @Optional() private googleAnalyticsService: GoogleAnalyticsService, ) { - console.log(this.appConfig); - if (!isEqual(environment, this.appConfig)) { throw new Error('environment does not match app config!'); } diff --git a/src/assets/.gitignore b/src/assets/.gitignore index c25cf8c104..d344ba6b06 100644 --- a/src/assets/.gitignore +++ b/src/assets/.gitignore @@ -1 +1 @@ -appConfig.json +config.json diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 5e8d8e1587..25a04c20a7 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -1,5 +1,6 @@ import * as colors from 'colors'; import * as fs from 'fs'; +import * as yaml from 'js-yaml'; import { join } from 'path'; import { AppConfig } from './app-config.interface'; @@ -44,8 +45,12 @@ const getEnvironment = (): Environment => { }; const getLocalConfigPath = (env: Environment) => { - // default to config/appConfig.json - let localConfigPath = join(CONFIG_PATH, 'appConfig.json'); + // default to config/config.yml + let localConfigPath = join(CONFIG_PATH, 'config.yml'); + + if (!fs.existsSync(localConfigPath)) { + localConfigPath = join(CONFIG_PATH, 'config.yaml'); + } // determine app config filename variations let envVariations; @@ -63,9 +68,16 @@ const getLocalConfigPath = (env: Environment) => { // check if any environment variations of app config exist for (const envVariation of envVariations) { - const envLocalConfigPath = join(CONFIG_PATH, `appConfig.${envVariation}.json`); + let envLocalConfigPath = join(CONFIG_PATH, `config.${envVariation}.yml`); if (fs.existsSync(envLocalConfigPath)) { localConfigPath = envLocalConfigPath; + break; + } else { + envLocalConfigPath = join(CONFIG_PATH, `config.${envVariation}.yaml`); + if (fs.existsSync(envLocalConfigPath)) { + localConfigPath = envLocalConfigPath; + break; + } } } @@ -76,7 +88,7 @@ const overrideWithConfig = (config: Config, pathToConfig: string) => { try { console.log(`Overriding app config with ${pathToConfig}`); const externalConfig = fs.readFileSync(pathToConfig, 'utf8'); - mergeConfig(config, JSON.parse(externalConfig)); + mergeConfig(config, yaml.load(externalConfig)); } catch (err) { console.error(err); } @@ -190,7 +202,7 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => { if (isNotEmpty(destConfigPath)) { fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); - console.log(`Angular ${colors.bold('appConfig.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`); + console.log(`Angular ${colors.bold('config.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`); } return appConfig; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index bceea29b96..c1bb275921 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -20,7 +20,17 @@ import { UniversalConfig } from './universal-config.interface'; export class DefaultAppConfig implements AppConfig { production = false; - // Angular Universal server settings. + // Angular Universal settings + universal: UniversalConfig = { + preboot: true, + async: true, + time: false + }; + + // NOTE: will log all redux actions and transfers in console + debug = false; + + // Angular Universal server settings // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. ui: UIServerConfig = { ssl: false, @@ -36,7 +46,7 @@ export class DefaultAppConfig implements AppConfig { } }; - // The REST API server settings. + // The REST API server settings // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. rest: ServerConfig = { ssl: false, @@ -164,16 +174,6 @@ export class DefaultAppConfig implements AppConfig { } }; - // Angular Universal settings - universal: UniversalConfig = { - preboot: true, - async: true, - time: false - }; - - // NOTE: will log all redux actions and transfers in console - debug = false; - // Default Language in which the UI will be rendered if the user's browser language is not an active language defaultLanguage = 'en'; diff --git a/src/main.browser.ts b/src/main.browser.ts index c72ca0cf31..de4276ea93 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -34,7 +34,7 @@ const main = () => { return bootstrap(); } else { - return fetch('assets/appConfig.json') + return fetch('assets/config.json') .then((response) => response.json()) .then((appConfig: AppConfig) => { diff --git a/webpack/webpack.browser.ts b/webpack/webpack.browser.ts index 134eecb7ed..a71d749347 100644 --- a/webpack/webpack.browser.ts +++ b/webpack/webpack.browser.ts @@ -10,7 +10,7 @@ module.exports = Object.assign({}, commonExports, { }, devServer: { before(app, server) { - buildAppConfig(join(process.cwd(), 'src/assets/appConfig.json')); + buildAppConfig(join(process.cwd(), 'src/assets/config.json')); } } }); diff --git a/yarn.lock b/yarn.lock index db63c66040..6fe2896b20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2753,6 +2753,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + aria-query@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" @@ -8012,6 +8017,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" From 2bf880b21603879ba295ac48ca524968179b02b1 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 8 Dec 2021 19:31:05 -0600 Subject: [PATCH 021/102] correct typo in comment --- src/config/config.util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.util.ts b/src/config/config.util.ts index 6c3b9e0f7a..c45282269c 100644 --- a/src/config/config.util.ts +++ b/src/config/config.util.ts @@ -35,7 +35,7 @@ const mergeConfig = (destinationConfig: any, sourceConfig: AppConfig): void => { }; /** - * Get default them config from environment. + * Get default theme config from environment. * * @returns default theme config */ From d3b5e09e2ac39f1382ea28eb1eb5d848b7acf328 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 9 Dec 2021 17:48:59 +0100 Subject: [PATCH 022/102] [DSC-287] remove unused imports --- .../page-internal-server-error.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.ts b/src/app/page-internal-server-error/page-internal-server-error.component.ts index b2dc70dbdf..09b441969a 100644 --- a/src/app/page-internal-server-error/page-internal-server-error.component.ts +++ b/src/app/page-internal-server-error/page-internal-server-error.component.ts @@ -1,5 +1,4 @@ -import { ServerResponseService } from '../core/services/server-response.service'; -import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; /** * This component representing the `PageNotFound` DSpace page. From 549529c8896b5e14e4ce6798fb30d52d35b56f8f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 10 Dec 2021 14:18:51 +0100 Subject: [PATCH 023/102] 85262: Get submission object instead of sending empty patch + revert empty patch code --- .../json-patch-operations.service.ts | 16 +++++--------- .../submission-objects.effects.spec.ts | 7 ++++++ .../objects/submission-objects.effects.ts | 22 +++++++++++++------ 3 files changed, 28 insertions(+), 17 deletions(-) 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 f160376837..c3363f4db4 100644 --- a/src/app/core/json-patch/json-patch-operations.service.ts +++ b/src/app/core/json-patch/json-patch-operations.service.ts @@ -40,15 +40,13 @@ export abstract class JsonPatchOperationsService * observable of response */ - protected submitJsonPatchOperations(hrefObs: Observable, resourceType: string, resourceId?: string, allowEmptyRequest = false): Observable { + protected submitJsonPatchOperations(hrefObs: Observable, resourceType: string, resourceId?: string): Observable { const requestId = this.requestService.generateRequestId(); let startTransactionTime = null; - const [patchRequest$, emptyRequest$] = partition((request: PatchRequestDefinition) => allowEmptyRequest || isNotEmpty(request.body))(hrefObs.pipe( + const [patchRequest$, emptyRequest$] = partition((request: PatchRequestDefinition) => isNotEmpty(request.body))(hrefObs.pipe( mergeMap((endpointURL: string) => { return this.store.select(jsonPatchOperationsByResourceType(resourceType)).pipe( take(1), @@ -81,11 +79,11 @@ export abstract class JsonPatchOperationsService !allowEmptyRequest && isEmpty(request.body)), + filter((request: PatchRequestDefinition) => isEmpty(request.body)), tap(() => startTransactionTime = null), map(() => null)), patchRequest$.pipe( - filter((request: PatchRequestDefinition) => allowEmptyRequest || isNotEmpty(request.body)), + filter((request: PatchRequestDefinition) => isNotEmpty(request.body)), tap(() => this.store.dispatch(new StartTransactionPatchOperationsAction(resourceType, resourceId, startTransactionTime))), tap((request: PatchRequestDefinition) => this.requestService.send(request)), mergeMap(() => { @@ -143,18 +141,16 @@ export abstract class JsonPatchOperationsService * observable of response */ - public jsonPatchByResourceType(linkPath: string, scopeId: string, resourceType: string, allowEmptyRequest = false): Observable { + public jsonPatchByResourceType(linkPath: string, scopeId: string, resourceType: string): Observable { const href$ = this.halService.getEndpoint(linkPath).pipe( filter((href: string) => isNotEmpty(href)), distinctUntilChanged(), map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId))); - return this.submitJsonPatchOperations(href$, resourceType, undefined, allowEmptyRequest); + return this.submitJsonPatchOperations(href$, resourceType); } /** diff --git a/src/app/submission/objects/submission-objects.effects.spec.ts b/src/app/submission/objects/submission-objects.effects.spec.ts index e760f71941..b2bc054287 100644 --- a/src/app/submission/objects/submission-objects.effects.spec.ts +++ b/src/app/submission/objects/submission-objects.effects.spec.ts @@ -54,6 +54,8 @@ import { Item } from '../../core/shared/item.model'; import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; import { WorkflowItemDataService } from '../../core/submission/workflowitem-data.service'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +import { SubmissionObjectDataService } from '../../core/submission/submission-object-data.service'; +import { mockSubmissionObjectDataService } from '../../shared/testing/submission-oject-data-service.mock'; describe('SubmissionObjectEffects test suite', () => { let submissionObjectEffects: SubmissionObjectEffects; @@ -63,6 +65,7 @@ describe('SubmissionObjectEffects test suite', () => { let notificationsServiceStub; let submissionServiceStub; let submissionJsonPatchOperationsServiceStub; + let submissionObjectDataServiceStub; const collectionId: string = mockSubmissionCollectionId; const submissionId: string = mockSubmissionId; const submissionDefinitionResponse: any = mockSubmissionDefinitionResponse; @@ -75,6 +78,9 @@ describe('SubmissionObjectEffects test suite', () => { notificationsServiceStub = new NotificationsServiceStub(); submissionServiceStub = new SubmissionServiceStub(); submissionJsonPatchOperationsServiceStub = new SubmissionJsonPatchOperationsServiceStub(); + submissionObjectDataServiceStub = mockSubmissionObjectDataService; + + submissionServiceStub.hasUnsavedModification.and.returnValue(observableOf(true)); TestBed.configureTestingModule({ imports: [ @@ -99,6 +105,7 @@ describe('SubmissionObjectEffects test suite', () => { { provide: WorkflowItemDataService, useValue: {} }, { provide: WorkflowItemDataService, useValue: {} }, { provide: HALEndpointService, useValue: {} }, + { provide: SubmissionObjectDataService, useValue: submissionObjectDataServiceStub }, ], }); diff --git a/src/app/submission/objects/submission-objects.effects.ts b/src/app/submission/objects/submission-objects.effects.ts index 5432aa7a4c..257854f027 100644 --- a/src/app/submission/objects/submission-objects.effects.ts +++ b/src/app/submission/objects/submission-objects.effects.ts @@ -196,13 +196,21 @@ export class SubmissionObjectEffects { */ @Effect() saveAndDeposit$ = this.actions$.pipe( ofType(SubmissionObjectActionTypes.SAVE_AND_DEPOSIT_SUBMISSION), - withLatestFrom(this.store$), - switchMap(([action, currentState]: [SaveAndDepositSubmissionAction, any]) => { - return this.operationsService.jsonPatchByResourceType( - this.submissionService.getSubmissionObjectLinkName(), - action.payload.submissionId, - 'sections', - true).pipe( + withLatestFrom(this.submissionService.hasUnsavedModification()), + switchMap(([action, hasUnsavedModification]: [SaveAndDepositSubmissionAction, boolean]) => { + let response$: Observable; + if (hasUnsavedModification) { + response$ = this.operationsService.jsonPatchByResourceType( + this.submissionService.getSubmissionObjectLinkName(), + action.payload.submissionId, + 'sections') as Observable; + } else { + response$ = this.submissionObjectService.findById(action.payload.submissionId).pipe( + getFirstSucceededRemoteDataPayload(), + map((submissionObject: SubmissionObject) => [submissionObject]) + ); + } + return response$.pipe( map((response: SubmissionObject[]) => { if (this.canDeposit(response)) { return new DepositSubmissionAction(action.payload.submissionId); From e2614b9dadcd17ce84b809518c783dda3a6a75dc Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Mon, 13 Dec 2021 13:48:09 -0500 Subject: [PATCH 024/102] w2p-85847 Add Authority to browse indexes --- .../browse-by-date-page.component.ts | 2 +- .../browse-by-metadata-page.component.ts | 12 +++++++++--- .../browse-by-title-page.component.ts | 2 +- src/app/core/browse/browse.service.spec.ts | 17 ++++++++++++++++- src/app/core/browse/browse.service.ts | 5 ++++- .../browse-entry-list-element.component.html | 2 +- .../browse-entry-list-element.component.ts | 14 +++++++++++++- 7 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index 3158c3d7cc..2b1cd21f14 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -63,7 +63,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { this.browseId = params.id || this.defaultBrowseId; this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); - this.updatePageWithItems(searchOptions, this.value); + this.updatePageWithItems(searchOptions, this.value, undefined); this.updateParent(params.scope); this.updateStartsWithOptions(this.browseId, metadataField, params.scope); })); diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 3573ffb264..c6716afb5f 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -99,6 +99,11 @@ export class BrowseByMetadataPageComponent implements OnInit { */ value = ''; + /** + * The authority key (may be undefined) associated with {@link #value}. + */ + authority: string; + /** * The current startsWith option (fetched and updated from query-params) */ @@ -123,11 +128,12 @@ export class BrowseByMetadataPageComponent implements OnInit { }) ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { this.browseId = params.id || this.defaultBrowseId; + this.authority = params.authority; this.value = +params.value || params.value || ''; this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); if (isNotEmpty(this.value)) { - this.updatePageWithItems(searchOptions, this.value); + this.updatePageWithItems(searchOptions, this.value, this.authority); } else { this.updatePage(searchOptions); } @@ -166,8 +172,8 @@ export class BrowseByMetadataPageComponent implements OnInit { * scope: string } * @param value The value of the browse-entry to display items for */ - updatePageWithItems(searchOptions: BrowseEntrySearchOptions, value: string) { - this.items$ = this.browseService.getBrowseItemsFor(value, searchOptions); + updatePageWithItems(searchOptions: BrowseEntrySearchOptions, value: string, authority: string) { + this.items$ = this.browseService.getBrowseItemsFor(value, authority, searchOptions); } /** diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index b3a2ceed00..a2fcce7bce 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -46,7 +46,7 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { }) ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { this.browseId = params.id || this.defaultBrowseId; - this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined); + this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined, undefined); this.updateParent(params.scope); })); this.updateStartsWithTextOptions(); diff --git a/src/app/core/browse/browse.service.spec.ts b/src/app/core/browse/browse.service.spec.ts index a28add2e30..ac68fadb31 100644 --- a/src/app/core/browse/browse.service.spec.ts +++ b/src/app/core/browse/browse.service.spec.ts @@ -129,6 +129,7 @@ describe('BrowseService', () => { describe('getBrowseEntriesFor and findList', () => { // should contain special characters such that url encoding can be tested as well const mockAuthorName = 'Donald Smith & Sons'; + const mockAuthorityKey = 'some authority key ?=;'; beforeEach(() => { requestService = getMockRequestService(getRequestEntry$(true)); @@ -155,7 +156,7 @@ describe('BrowseService', () => { it('should call hrefOnlyDataService.findAllByHref with the expected href', () => { const expected = browseDefinitions[1]._links.items.href + '?filterValue=' + encodeURIComponent(mockAuthorName); - scheduler.schedule(() => service.getBrowseItemsFor(mockAuthorName, new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe()); + scheduler.schedule(() => service.getBrowseItemsFor(mockAuthorName, undefined, new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe()); scheduler.flush(); expect(getFirstUsedArgumentOfSpyMethod(hrefOnlyDataService.findAllByHref)).toBeObservable(cold('(a|)', { @@ -164,6 +165,20 @@ describe('BrowseService', () => { }); }); + describe('when getBrowseItemsFor is called with a valid filter value and authority key', () => { + it('should call hrefOnlyDataService.findAllByHref with the expected href', () => { + const expected = browseDefinitions[1]._links.items.href + + '?filterValue=' + encodeURIComponent(mockAuthorName) + + '&filterAuthority=' + encodeURIComponent(mockAuthorityKey); + + scheduler.schedule(() => service.getBrowseItemsFor(mockAuthorName, mockAuthorityKey, new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe()); + scheduler.flush(); + + expect(getFirstUsedArgumentOfSpyMethod(hrefOnlyDataService.findAllByHref)).toBeObservable(cold('(a|)', { + a: expected + })); + }); + }); }); describe('getBrowseURLFor', () => { diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index ffc6f313b9..05e625d6c1 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -105,7 +105,7 @@ export class BrowseService { * @param options Options to narrow down your search * @returns {Observable>>} */ - getBrowseItemsFor(filterValue: string, options: BrowseEntrySearchOptions): Observable>> { + getBrowseItemsFor(filterValue: string, filterAuthority: string, options: BrowseEntrySearchOptions): Observable>> { const href$ = this.getBrowseDefinitions().pipe( getBrowseDefinitionLinks(options.metadataDefinition), hasValueOperator(), @@ -132,6 +132,9 @@ export class BrowseService { if (isNotEmpty(filterValue)) { args.push(`filterValue=${encodeURIComponent(filterValue)}`); } + if (isNotEmpty(filterAuthority)) { + args.push(`filterAuthority=${encodeURIComponent(filterAuthority)}`); + } if (isNotEmpty(args)) { href = new URLCombiner(href, `?${args.join('&')}`).toString(); } diff --git a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html index e3455bf095..8577ee654c 100644 --- a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html +++ b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html @@ -1,5 +1,5 @@
- + {{object.value}} diff --git a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts index 1b0f1eef37..cfdf561f99 100644 --- a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts +++ b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts @@ -15,4 +15,16 @@ import { listableObjectComponent } from '../../object-collection/shared/listable * This component is automatically used to create a list view for BrowseEntry objects when used in ObjectCollectionComponent */ @listableObjectComponent(BrowseEntry, ViewMode.ListElement) -export class BrowseEntryListElementComponent extends AbstractListableElementComponent {} +export class BrowseEntryListElementComponent extends AbstractListableElementComponent { + + /** + * Get the query params to access the item page of this browse entry. + */ + public getQueryParams(): {[param: string]: any} { + return { + value: this.object.value, + authority: !!this.object.authority ? this.object.authority : undefined, + startsWith: undefined, + }; + } +} From b6904a4df90e9bb4e86ffc3bcadb20f34c531ae5 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 12:40:51 +0100 Subject: [PATCH 025/102] [DSC-287] add link button to home page --- .../page-internal-server-error.component.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.html b/src/app/page-internal-server-error/page-internal-server-error.component.html index 8e30236c4f..09b499b496 100644 --- a/src/app/page-internal-server-error/page-internal-server-error.component.html +++ b/src/app/page-internal-server-error/page-internal-server-error.component.html @@ -4,4 +4,7 @@

{{"500.help" | translate}}


-
\ No newline at end of file +

+ {{"500.link.home-page" | translate}} +

+ From ddcb1ecdf23c2f3310bba303a4bc929c117b801f Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 12:42:24 +0100 Subject: [PATCH 026/102] [DSC-287] invalidate root endpoint cache when redirected to 500 page in order to check every time the rest server availability --- src/app/core/data/root-data.service.ts | 7 +++++++ src/app/core/server-check/server-check.guard.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/src/app/core/data/root-data.service.ts b/src/app/core/data/root-data.service.ts index 8b4e836671..ce1b33a9e3 100644 --- a/src/app/core/data/root-data.service.ts +++ b/src/app/core/data/root-data.service.ts @@ -106,5 +106,12 @@ export class RootDataService { findAllByHref(href: string | Observable, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { return this.dataService.findAllByHref(href, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } + + /** + * Set to sale the root endpoint cache hit + */ + invalidateRootCache() { + this.requestService.setStaleByHrefSubstring('server/api'); + } } /* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/server-check/server-check.guard.ts b/src/app/core/server-check/server-check.guard.ts index b7982eee9a..017b75d373 100644 --- a/src/app/core/server-check/server-check.guard.ts +++ b/src/app/core/server-check/server-check.guard.ts @@ -32,6 +32,7 @@ export class ServerCheckGuard implements CanActivate { map((res: RemoteData) => res.hasSucceeded), tap((hasSucceeded: boolean) => { if (!hasSucceeded) { + this.rootDataService.invalidateRootCache(); this.router.navigateByUrl(getPageInternalServerErrorRoute()); } }), From 44fc86c9fe76fda5d6f18fab4786ebaae878fc93 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 15:22:55 +0100 Subject: [PATCH 027/102] [DSC-287] use CanActivateChild in order to check every time the rest server availability --- src/app/app-routing.module.ts | 4 +++- src/app/core/server-check/server-check.guard.spec.ts | 9 ++++++--- src/app/core/server-check/server-check.guard.ts | 8 ++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 3a47815f42..4a9810e4da 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -36,7 +36,9 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; RouterModule.forRoot([ { path: INTERNAL_SERVER_ERROR, component: ThemedPageInternalServerErrorComponent }, { - path: '', canActivate: [ServerCheckGuard, AuthBlockingGuard], + path: '', + canActivate: [AuthBlockingGuard], + canActivateChild: [ServerCheckGuard], children: [ { path: '', redirectTo: '/home', pathMatch: 'full' }, { diff --git a/src/app/core/server-check/server-check.guard.spec.ts b/src/app/core/server-check/server-check.guard.spec.ts index a9c03d1cd7..749912be85 100644 --- a/src/app/core/server-check/server-check.guard.spec.ts +++ b/src/app/core/server-check/server-check.guard.spec.ts @@ -13,7 +13,8 @@ describe('ServerCheckGuard', () => { let rootDataServiceStub: SpyObj; rootDataServiceStub = jasmine.createSpyObj('RootDataService', { - findRoot: jasmine.createSpy('findRoot') + findRoot: jasmine.createSpy('findRoot'), + invalidateRootCache: jasmine.createSpy('invalidateRootCache') }); router = jasmine.createSpyObj('Router', { navigateByUrl: jasmine.createSpy('navigateByUrl') @@ -37,10 +38,11 @@ describe('ServerCheckGuard', () => { }); it('should not redirect to error page', () => { - guard.canActivate({} as any, {} as any).pipe( + guard.canActivateChild({} as any, {} as any).pipe( take(1) ).subscribe((canActivate: boolean) => { expect(canActivate).toEqual(true); + expect(rootDataServiceStub.invalidateRootCache).not.toHaveBeenCalled(); expect(router.navigateByUrl).not.toHaveBeenCalled(); }); }); @@ -52,10 +54,11 @@ describe('ServerCheckGuard', () => { }); it('should redirect to error page', () => { - guard.canActivate({} as any, {} as any).pipe( + guard.canActivateChild({} as any, {} as any).pipe( take(1) ).subscribe((canActivate: boolean) => { expect(canActivate).toEqual(false); + expect(rootDataServiceStub.invalidateRootCache).toHaveBeenCalled(); expect(router.navigateByUrl).toHaveBeenCalledWith(getPageInternalServerErrorRoute()); }); }); diff --git a/src/app/core/server-check/server-check.guard.ts b/src/app/core/server-check/server-check.guard.ts index 017b75d373..af216bef0e 100644 --- a/src/app/core/server-check/server-check.guard.ts +++ b/src/app/core/server-check/server-check.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; @@ -16,18 +16,18 @@ import { getFirstCompletedRemoteData } from '../shared/operators'; * A guard that checks if root api endpoint is reachable. * If not redirect to 500 error page */ -export class ServerCheckGuard implements CanActivate { +export class ServerCheckGuard implements CanActivateChild { constructor(private router: Router, private rootDataService: RootDataService) { } /** * True when root api endpoint is reachable. */ - canActivate( + canActivateChild( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.rootDataService.findRoot().pipe( + return this.rootDataService.findRoot(false).pipe( getFirstCompletedRemoteData(), map((res: RemoteData) => res.hasSucceeded), tap((hasSucceeded: boolean) => { From d426f5f17985c579e08177a998434a503a455bfd Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 15:47:30 +0100 Subject: [PATCH 028/102] [DSC-287] fix test --- src/app/core/server-check/server-check.guard.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/core/server-check/server-check.guard.spec.ts b/src/app/core/server-check/server-check.guard.spec.ts index 749912be85..30d07234a3 100644 --- a/src/app/core/server-check/server-check.guard.spec.ts +++ b/src/app/core/server-check/server-check.guard.spec.ts @@ -26,6 +26,8 @@ describe('ServerCheckGuard', () => { afterEach(() => { router.navigateByUrl.calls.reset(); + rootDataServiceStub.invalidateRootCache.calls.reset(); + rootDataServiceStub.findRoot.calls.reset(); }); it('should be created', () => { From 0afa7c5bab0249400aaee424338c32692880a574 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 14 Dec 2021 18:01:34 -0600 Subject: [PATCH 029/102] update readme examples to yaml --- docs/Configuration.md | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index d21a41e277..af0efc1f02 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -18,15 +18,13 @@ Configuration options can be overridden by setting environment variables. When you start dspace-angular on node, it spins up an http server on which it listens for incoming connections. You can define the ip address and port the server should bind itsself to, and if ssl should be enabled not. By default it listens on `localhost:4000`. If you want it to listen on all your network connections, configure it to bind itself to `0.0.0.0`. To change this configuration, change the options `ui.host`, `ui.port` and `ui.ssl` in the appropriate configuration file (see above): -``` -{ - "ui": { - "ssl": false, - "host": "localhost", - "port": 4000, - "nameSpace": "/" - } -} + +```yaml +ui: + ssl: false + host: localhost + port: 4000 + nameSpace: / ``` Alternately you can set the following environment variables. If any of these are set, it will override all configuration files: @@ -47,14 +45,12 @@ or ## DSpace's REST endpoint dspace-angular connects to your DSpace installation by using its REST endpoint. To do so, you have to define the ip address, port and if ssl should be enabled. You can do this in a configuration file (see above) by adding the following options: -``` -{ - "rest": { - "ssl": true, - "host": "api7.dspace.org", - "port": 443, - "nameSpace": "/server" - } +```yaml +rest: + ssl: true + host: api7.dspace.org + port: 443 + nameSpace: /server } ``` From c7321f9a22a439398fee4c97f94202ed77cc293b Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 14 Dec 2021 18:02:27 -0600 Subject: [PATCH 030/102] update script comment --- scripts/test-rest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test-rest.ts b/scripts/test-rest.ts index 00455d69b5..aa3b64f62b 100644 --- a/scripts/test-rest.ts +++ b/scripts/test-rest.ts @@ -7,7 +7,7 @@ import { buildAppConfig } from '../src/config/config.server'; const appConfig: AppConfig = buildAppConfig(); /** - * Script to test the connection with the configured REST API (in the 'rest' settings of your appConfig.*.json) + * Script to test the connection with the configured REST API (in the 'rest' settings of your config.*.yaml) * * This script is useful to test for any Node.js connection issues with your REST API. * From b820794790eb514a2ef6bb0e69dc893917ea9894 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 14 Dec 2021 18:11:37 -0600 Subject: [PATCH 031/102] comment env-to-yaml script --- scripts/env-to-yaml.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/env-to-yaml.ts b/scripts/env-to-yaml.ts index 47f876f20a..edcdfd90b4 100644 --- a/scripts/env-to-yaml.ts +++ b/scripts/env-to-yaml.ts @@ -2,8 +2,15 @@ import * as fs from 'fs'; import * as yaml from 'js-yaml'; import { join } from 'path'; -const args = process.argv.slice(2); +/** + * Script to help convert previous version environment.*.ts to yaml. + * + * Usage (see package.json): + * + * yarn env:yaml [relative path to environment.ts file] (optional relative path to write yaml file) * + */ +const args = process.argv.slice(2); if (args[0] === undefined) { console.log(`Usage:\n\tyarn env:yaml [relative path to environment.ts file] (optional relative path to write yaml file)\n`); process.exit(0); From 71e40fdb6ef3d6e63b6232eee5dc333af2aaff3c Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 15 Dec 2021 14:04:38 +0100 Subject: [PATCH 032/102] 85979: only show security tab on profile page when canChangePassword FeatureID is true --- src/app/core/data/feature-authorization/feature-id.ts | 1 + src/app/profile-page/profile-page.component.html | 2 +- src/app/profile-page/profile-page.component.ts | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 15eba0e5db..b64de5100b 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -13,6 +13,7 @@ export enum FeatureID { CanManageGroup = 'canManageGroup', IsCollectionAdmin = 'isCollectionAdmin', IsCommunityAdmin = 'isCommunityAdmin', + CanChangePassword = 'canChangePassword', CanDownload = 'canDownload', CanRequestACopy = 'canRequestACopy', CanManageVersions = 'canManageVersions', diff --git a/src/app/profile-page/profile-page.component.html b/src/app/profile-page/profile-page.component.html index 619e4a2411..95d2be1274 100644 --- a/src/app/profile-page/profile-page.component.html +++ b/src/app/profile-page/profile-page.component.html @@ -7,7 +7,7 @@ -
+
{{'profile.card.security' | translate}}
; constructor(private authService: AuthService, private notificationsService: NotificationsService, private translate: TranslateService, - private epersonService: EPersonDataService) { + private epersonService: EPersonDataService, + private authorizationService: AuthorizationDataService) { } ngOnInit(): void { @@ -83,6 +87,7 @@ export class ProfilePageComponent implements OnInit { tap((user: EPerson) => this.currentUser = user) ); this.groupsRD$ = this.user$.pipe(switchMap((user: EPerson) => user.groups)); + this.canChangePassword$ = this.user$.pipe(switchMap((user:EPerson) => this.authorizationService.isAuthorized(FeatureID.CanChangePassword, user._links.self.href))); } /** From 2f022f505dd8c1543e77ab5b57287eebd5cf41c2 Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 15 Dec 2021 14:41:10 +0100 Subject: [PATCH 033/102] 85979: tests for canChangePassword UI changes --- .../profile-page/profile-page.component.html | 2 +- .../profile-page.component.spec.ts | 46 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/app/profile-page/profile-page.component.html b/src/app/profile-page/profile-page.component.html index 95d2be1274..ccfae0bba1 100644 --- a/src/app/profile-page/profile-page.component.html +++ b/src/app/profile-page/profile-page.component.html @@ -7,7 +7,7 @@
-
+
{{'profile.card.security' | translate}}
{ let component: ProfilePageComponent; @@ -28,10 +31,13 @@ describe('ProfilePageComponent', () => { let epersonService; let notificationsService; + let canChangePassword = new BehaviorSubject(true); + function init() { user = Object.assign(new EPerson(), { id: 'userId', - groups: createSuccessfulRemoteDataObject$(createPaginatedList([])) + groups: createSuccessfulRemoteDataObject$(createPaginatedList([])), + _links: {self: {href: 'test.com/uuid/1234567654321'}} }); initialState = { core: { @@ -74,6 +80,7 @@ describe('ProfilePageComponent', () => { { provide: EPersonDataService, useValue: epersonService }, { provide: NotificationsService, useValue: notificationsService }, { provide: AuthService, useValue: authService }, + { provide: AuthorizationDataService, useValue: jasmine.createSpyObj('authorizationService', { isAuthorized: canChangePassword }) }, provideMockStore({ initialState }), ], schemas: [NO_ERRORS_SCHEMA] @@ -183,7 +190,7 @@ describe('ProfilePageComponent', () => { component.setPasswordValue('testest'); component.setInvalid(false); - operations = [{op: 'add', path: '/password', value: 'testest'}]; + operations = [{ op: 'add', path: '/password', value: 'testest' }]; result = component.updateSecurity(); }); @@ -196,4 +203,35 @@ describe('ProfilePageComponent', () => { }); }); }); + + 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', () => { + 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', fakeAsync(() => { + expect(fixture.debugElement.query(By.css('.security-section'))).toBeNull(); + })); + }); + }); }); From 6594a3877fc1c3dfe258d048449eae87abcde34f Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 15 Dec 2021 15:13:27 +0100 Subject: [PATCH 034/102] fixed lint/lgtm issues --- src/app/profile-page/profile-page.component.spec.ts | 4 ++-- src/app/profile-page/profile-page.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/profile-page/profile-page.component.spec.ts b/src/app/profile-page/profile-page.component.spec.ts index adffe527d1..c7b644375c 100644 --- a/src/app/profile-page/profile-page.component.spec.ts +++ b/src/app/profile-page/profile-page.component.spec.ts @@ -1,5 +1,5 @@ import { ProfilePageComponent } from './profile-page.component'; -import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; import { VarDirective } from '../shared/utils/var.directive'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; @@ -31,7 +31,7 @@ describe('ProfilePageComponent', () => { let epersonService; let notificationsService; - let canChangePassword = new BehaviorSubject(true); + const canChangePassword = new BehaviorSubject(true); function init() { user = Object.assign(new EPerson(), { diff --git a/src/app/profile-page/profile-page.component.ts b/src/app/profile-page/profile-page.component.ts index 271b0277da..fece166a59 100644 --- a/src/app/profile-page/profile-page.component.ts +++ b/src/app/profile-page/profile-page.component.ts @@ -87,7 +87,7 @@ export class ProfilePageComponent implements OnInit { tap((user: EPerson) => this.currentUser = user) ); this.groupsRD$ = this.user$.pipe(switchMap((user: EPerson) => user.groups)); - this.canChangePassword$ = this.user$.pipe(switchMap((user:EPerson) => this.authorizationService.isAuthorized(FeatureID.CanChangePassword, user._links.self.href))); + this.canChangePassword$ = this.user$.pipe(switchMap((user: EPerson) => this.authorizationService.isAuthorized(FeatureID.CanChangePassword, user._links.self.href))); } /** From a1578303fa6f2939ecb26f3799b21781461a5164 Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 15 Dec 2021 16:15:06 +0100 Subject: [PATCH 035/102] 85993: Browse by from rest for menu and comcol page browse by --- src/app/navbar/navbar.component.ts | 72 +++++++++++-------- .../comcol-page-browse-by.component.ts | 41 +++++++---- 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index e741cea285..29541c77a0 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -6,7 +6,11 @@ import { MenuID, MenuItemType } from '../shared/menu/initial-menus-state'; import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; -import { environment } from '../../environments/environment'; +import { BrowseService } from '../core/browse/browse.service'; +import { getFirstCompletedRemoteData } from '../core/shared/operators'; +import { PaginatedList } from '../core/data/paginated-list.model'; +import { BrowseDefinition } from '../core/shared/browse-definition.model'; +import { RemoteData } from '../core/data/remote-data'; /** * Component representing the public navbar @@ -26,7 +30,8 @@ export class NavbarComponent extends MenuComponent { constructor(protected menuService: MenuService, protected injector: Injector, - public windowService: HostWindowService + public windowService: HostWindowService, + public browseService: BrowseService ) { super(menuService, injector); } @@ -52,37 +57,44 @@ export class NavbarComponent extends MenuComponent { text: `menu.section.browse_global_communities_and_collections`, link: `/community-list` } as LinkMenuItemModel - }, - /* News */ - { - id: 'browse_global', - active: false, - visible: true, - index: 1, - model: { - type: MenuItemType.TEXT, - text: 'menu.section.browse_global' - } as TextMenuItemModel, - }, + } ]; // Read the different Browse-By types from config and add them to the browse menu - const types = environment.browseBy.types; - types.forEach((typeConfig) => { - menuList.push({ - id: `browse_global_by_${typeConfig.id}`, - parentID: 'browse_global', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: `menu.section.browse_global_by_${typeConfig.id}`, - link: `/browse/${typeConfig.id}` - } as LinkMenuItemModel + this.browseService.getBrowseDefinitions() + .pipe(getFirstCompletedRemoteData>()) + .subscribe((browseDefListRD: RemoteData>) => { + if (browseDefListRD.hasSucceeded) { + browseDefListRD.payload.page.forEach((browseDef: BrowseDefinition) => { + menuList.push({ + id: `browse_global_by_${browseDef.id}`, + parentID: 'browse_global', + active: false, + visible: true, + model: { + type: MenuItemType.LINK, + text: `menu.section.browse_global_by_${browseDef.id}`, + link: `/browse/${browseDef.id}` + } as LinkMenuItemModel + }); + }); + menuList.push( + /* Browse */ + { + id: 'browse_global', + active: false, + visible: true, + index: 1, + model: { + type: MenuItemType.TEXT, + text: 'menu.section.browse_global' + } as TextMenuItemModel, + },) + } + menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, { + shouldPersistOnRouteChange: true + }))); }); - }); - menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, { - shouldPersistOnRouteChange: true - }))); + } } diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts index 01912dbcaa..5dec1e27f8 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -6,6 +6,11 @@ import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interf import { environment } from '../../../environments/environment'; import { getCommunityPageRoute } from '../../community-page/community-page-routing-paths'; import { getCollectionPageRoute } from '../../collection-page/collection-page-routing-paths'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { PaginatedList } from '../../core/data/paginated-list.model'; +import { BrowseDefinition } from '../../core/shared/browse-definition.model'; +import { RemoteData } from '../../core/data/remote-data'; +import { BrowseService } from '../../core/browse/browse.service'; export interface ComColPageNavOption { id: string; @@ -40,30 +45,38 @@ export class ComcolPageBrowseByComponent implements OnInit { constructor( private route: ActivatedRoute, - private router: Router) { + private router: Router, + public browseService: BrowseService + ) { } ngOnInit(): void { - this.allOptions = environment.browseBy.types - .map((config: BrowseByTypeConfig) => ({ - id: config.id, - label: `browse.comcol.by.${config.id}`, - routerLink: `/browse/${config.id}`, - params: { scope: this.id } - })); + this.browseService.getBrowseDefinitions() + .pipe(getFirstCompletedRemoteData>()) + .subscribe((browseDefListRD: RemoteData>) => { + if (browseDefListRD.hasSucceeded) { + this.allOptions = browseDefListRD.payload.page + .map((config: BrowseDefinition) => ({ + id: config.id, + label: `browse.comcol.by.${config.id}`, + routerLink: `/browse/${config.id}`, + params: { scope: this.id } + })); + } + }); if (this.contentType === 'collection') { - this.allOptions = [ { + this.allOptions = [{ id: this.id, label: 'collection.page.browse.recent.head', routerLink: getCollectionPageRoute(this.id) - }, ...this.allOptions ]; + }, ...this.allOptions]; } else if (this.contentType === 'community') { this.allOptions = [{ - id: this.id, - label: 'community.all-lists.head', - routerLink: getCommunityPageRoute(this.id) - }, ...this.allOptions ]; + id: this.id, + label: 'community.all-lists.head', + routerLink: getCommunityPageRoute(this.id) + }, ...this.allOptions]; } this.currentOptionId$ = this.route.params.pipe( From dd69bc65ab6938aaf2684c597f3a3f246ac4ea2c Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 15 Dec 2021 16:21:09 +0100 Subject: [PATCH 036/102] Fixes to tests --- src/app/profile-page/profile-page.component.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/profile-page/profile-page.component.spec.ts b/src/app/profile-page/profile-page.component.spec.ts index c7b644375c..ce7f2f1acb 100644 --- a/src/app/profile-page/profile-page.component.spec.ts +++ b/src/app/profile-page/profile-page.component.spec.ts @@ -204,7 +204,7 @@ describe('ProfilePageComponent', () => { }); }); - describe('canChangePassword$', () => { + fdescribe('canChangePassword$', () => { describe('when the user is allowed to change their password', () => { beforeEach(() => { canChangePassword.next(true); @@ -229,9 +229,10 @@ describe('ProfilePageComponent', () => { getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: false }); }); - it('should not show the security section on the page', fakeAsync(() => { + it('should not show the security section on the page', () => { + fixture.detectChanges(); expect(fixture.debugElement.query(By.css('.security-section'))).toBeNull(); - })); + }); }); }); }); From b7d01127a536b6f8650ac331477351373e5229f1 Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 15 Dec 2021 16:21:55 +0100 Subject: [PATCH 037/102] Removed unnecessary import --- src/app/profile-page/profile-page.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/profile-page/profile-page.component.spec.ts b/src/app/profile-page/profile-page.component.spec.ts index ce7f2f1acb..59e62c6a16 100644 --- a/src/app/profile-page/profile-page.component.spec.ts +++ b/src/app/profile-page/profile-page.component.spec.ts @@ -1,5 +1,5 @@ import { ProfilePageComponent } from './profile-page.component'; -import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { VarDirective } from '../shared/utils/var.directive'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; From 7529fcde35cb5b6fe52676b47c1cafefc3429629 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 15 Dec 2021 16:46:40 +0100 Subject: [PATCH 038/102] remove fdescribe --- src/app/profile-page/profile-page.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/profile-page/profile-page.component.spec.ts b/src/app/profile-page/profile-page.component.spec.ts index 59e62c6a16..f48b894d8d 100644 --- a/src/app/profile-page/profile-page.component.spec.ts +++ b/src/app/profile-page/profile-page.component.spec.ts @@ -204,7 +204,7 @@ describe('ProfilePageComponent', () => { }); }); - fdescribe('canChangePassword$', () => { + describe('canChangePassword$', () => { describe('when the user is allowed to change their password', () => { beforeEach(() => { canChangePassword.next(true); From a4d91c37a7fc9e9d734bbd62864023628b26661c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 15 Dec 2021 22:38:08 +0100 Subject: [PATCH 039/102] [CST-4633] Create search.module and resolve dependencies issues --- .../access-control/access-control.module.ts | 4 +- .../admin-registries.module.ts | 4 +- .../bitstream-formats.module.ts | 4 +- .../admin-search-page/admin-search.module.ts | 2 + .../admin-workflow.module.ts | 2 + src/app/admin/admin.module.ts | 2 +- .../bitstream-page/bitstream-page.module.ts | 6 +- src/app/browse-by/browse-by.module.ts | 2 + .../collection-form.component.ts | 6 +- .../collection-form/collection-form.module.ts | 4 + .../collection-page/collection-page.module.ts | 4 +- .../create-collection-page.component.ts | 4 +- .../delete-collection-page.component.ts | 4 +- .../collection-metadata.component.ts | 2 +- .../collection-roles.component.spec.ts | 2 + .../edit-collection-page.component.ts | 4 +- .../edit-collection-page.module.ts | 8 +- .../community-form.component.ts | 6 +- .../community-form/community-form.module.ts | 4 + .../community-page/community-page.module.ts | 4 +- .../create-community-page.component.ts | 2 +- .../delete-community-page.component.ts | 4 +- .../community-metadata.component.ts | 2 +- .../community-roles.component.spec.ts | 2 + .../edit-community-page.component.ts | 4 +- .../edit-community-page.module.ts | 6 +- .../journal-entities.module.ts | 2 + .../research-entities.module.ts | 8 +- .../edit-item-page/edit-item-page.module.ts | 9 +- src/app/item-page/item-shared.module.ts | 36 ++++ .../related-entities-search.component.ts | 2 +- .../my-dspace-page/my-dspace-page.module.ts | 2 + src/app/profile-page/profile-page.module.ts | 4 +- src/app/search-page/search-page.module.ts | 2 + .../comcol-form/comcol-form.component.html | 0 .../comcol-form/comcol-form.component.scss | 0 .../comcol-form/comcol-form.component.spec.ts | 22 +- .../comcol-form/comcol-form.component.ts | 50 ++--- .../create-comcol-page.component.spec.ts | 18 +- .../create-comcol-page.component.ts | 24 +-- .../delete-comcol-page.component.spec.ts | 16 +- .../delete-comcol-page.component.ts | 16 +- .../comcol-metadata.component.spec.ts | 12 +- .../comcol-metadata.component.ts | 18 +- .../comcol-role/comcol-role.component.html | 0 .../comcol-role/comcol-role.component.scss | 0 .../comcol-role/comcol-role.component.spec.ts | 10 +- .../comcol-role/comcol-role.component.ts | 22 +- .../edit-comcol-page.component.html | 0 .../edit-comcol-page.component.spec.ts | 6 +- .../edit-comcol-page.component.ts | 6 +- .../comcol-page-browse-by.component.html | 0 .../comcol-page-browse-by.component.scss | 0 .../comcol-page-browse-by.component.ts | 8 +- .../comcol-page-content.component.html | 0 .../comcol-page-content.component.scss | 0 .../comcol-page-content.component.ts | 0 .../comcol-page-handle.component.html | 0 .../comcol-page-handle.component.scss | 0 .../comcol-page-handle.component.spec.ts | 0 .../comcol-page-handle.component.ts | 0 .../comcol-page-header.component.html | 0 .../comcol-page-header.component.scss | 0 .../comcol-page-header.component.ts | 0 .../comcol-page-logo.component.html | 0 .../comcol-page-logo.component.scss | 0 .../comcol-page-logo.component.ts | 2 +- src/app/shared/comcol/comcol.module.ts | 42 ++++ src/app/shared/form/form.module.ts | 82 ++++++++ .../resource-policies.module.ts | 52 +++++ src/app/shared/search/search.module.ts | 103 +++++++++ src/app/shared/shared.module.ts | 196 ++---------------- src/app/submission/submission.module.ts | 22 +- src/themes/custom/theme.module.ts | 9 +- src/themes/dspace/theme.module.ts | 7 + 75 files changed, 568 insertions(+), 338 deletions(-) create mode 100644 src/app/item-page/item-shared.module.ts rename src/app/shared/{ => comcol}/comcol-forms/comcol-form/comcol-form.component.html (100%) rename src/app/shared/{ => comcol}/comcol-forms/comcol-form/comcol-form.component.scss (100%) rename src/app/shared/{ => comcol}/comcol-forms/comcol-form/comcol-form.component.spec.ts (93%) rename src/app/shared/{ => comcol}/comcol-forms/comcol-form/comcol-form.component.ts (85%) rename src/app/shared/{ => comcol}/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts (90%) rename src/app/shared/{ => comcol}/comcol-forms/create-comcol-page/create-comcol-page.component.ts (77%) rename src/app/shared/{ => comcol}/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts (91%) rename src/app/shared/{ => comcol}/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts (80%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts (92%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts (79%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html (100%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.scss (100%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts (94%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts (82%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/edit-comcol-page.component.html (100%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts (91%) rename src/app/shared/{ => comcol}/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts (90%) rename src/app/shared/{ => comcol}/comcol-page-browse-by/comcol-page-browse-by.component.html (100%) rename src/app/shared/{ => comcol}/comcol-page-browse-by/comcol-page-browse-by.component.scss (100%) rename src/app/shared/{ => comcol}/comcol-page-browse-by/comcol-page-browse-by.component.ts (86%) rename src/app/shared/{ => comcol}/comcol-page-content/comcol-page-content.component.html (100%) rename src/app/shared/{ => comcol}/comcol-page-content/comcol-page-content.component.scss (100%) rename src/app/shared/{ => comcol}/comcol-page-content/comcol-page-content.component.ts (100%) rename src/app/shared/{ => comcol}/comcol-page-handle/comcol-page-handle.component.html (100%) rename src/app/shared/{ => comcol}/comcol-page-handle/comcol-page-handle.component.scss (100%) rename src/app/shared/{ => comcol}/comcol-page-handle/comcol-page-handle.component.spec.ts (100%) rename src/app/shared/{ => comcol}/comcol-page-handle/comcol-page-handle.component.ts (100%) rename src/app/shared/{ => comcol}/comcol-page-header/comcol-page-header.component.html (100%) rename src/app/shared/{ => comcol}/comcol-page-header/comcol-page-header.component.scss (100%) rename src/app/shared/{ => comcol}/comcol-page-header/comcol-page-header.component.ts (100%) rename src/app/shared/{ => comcol}/comcol-page-logo/comcol-page-logo.component.html (100%) rename src/app/shared/{ => comcol}/comcol-page-logo/comcol-page-logo.component.scss (100%) rename src/app/shared/{ => comcol}/comcol-page-logo/comcol-page-logo.component.ts (95%) create mode 100644 src/app/shared/comcol/comcol.module.ts create mode 100644 src/app/shared/form/form.module.ts create mode 100644 src/app/shared/resource-policies/resource-policies.module.ts create mode 100644 src/app/shared/search/search.module.ts diff --git a/src/app/access-control/access-control.module.ts b/src/app/access-control/access-control.module.ts index 0e872458bd..891238bbed 100644 --- a/src/app/access-control/access-control.module.ts +++ b/src/app/access-control/access-control.module.ts @@ -9,13 +9,15 @@ import { GroupFormComponent } from './group-registry/group-form/group-form.compo import { MembersListComponent } from './group-registry/group-form/members-list/members-list.component'; import { SubgroupsListComponent } from './group-registry/group-form/subgroup-list/subgroups-list.component'; import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; +import { FormModule } from '../shared/form/form.module'; @NgModule({ imports: [ CommonModule, SharedModule, RouterModule, - AccessControlRoutingModule + AccessControlRoutingModule, + FormModule ], declarations: [ EPeopleRegistryComponent, diff --git a/src/app/admin/admin-registries/admin-registries.module.ts b/src/app/admin/admin-registries/admin-registries.module.ts index 5c82bb2ec9..65f7b61419 100644 --- a/src/app/admin/admin-registries/admin-registries.module.ts +++ b/src/app/admin/admin-registries/admin-registries.module.ts @@ -8,6 +8,7 @@ import { SharedModule } from '../../shared/shared.module'; import { MetadataSchemaFormComponent } from './metadata-registry/metadata-schema-form/metadata-schema-form.component'; import { MetadataFieldFormComponent } from './metadata-schema/metadata-field-form/metadata-field-form.component'; import { BitstreamFormatsModule } from './bitstream-formats/bitstream-formats.module'; +import { FormModule } from '../../shared/form/form.module'; @NgModule({ imports: [ @@ -15,7 +16,8 @@ import { BitstreamFormatsModule } from './bitstream-formats/bitstream-formats.mo SharedModule, RouterModule, BitstreamFormatsModule, - AdminRegistriesRoutingModule + AdminRegistriesRoutingModule, + FormModule ], declarations: [ MetadataRegistryComponent, diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.module.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.module.ts index 1667a07c0b..afbe35a1f6 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.module.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.module.ts @@ -7,13 +7,15 @@ import { FormatFormComponent } from './format-form/format-form.component'; import { EditBitstreamFormatComponent } from './edit-bitstream-format/edit-bitstream-format.component'; import { BitstreamFormatsRoutingModule } from './bitstream-formats-routing.module'; import { AddBitstreamFormatComponent } from './add-bitstream-format/add-bitstream-format.component'; +import { FormModule } from '../../../shared/form/form.module'; @NgModule({ imports: [ CommonModule, SharedModule, RouterModule, - BitstreamFormatsRoutingModule + BitstreamFormatsRoutingModule, + FormModule ], declarations: [ BitstreamFormatsComponent, diff --git a/src/app/admin/admin-search-page/admin-search.module.ts b/src/app/admin/admin-search-page/admin-search.module.ts index ecf8d8a2d2..353d6dd498 100644 --- a/src/app/admin/admin-search-page/admin-search.module.ts +++ b/src/app/admin/admin-search-page/admin-search.module.ts @@ -10,6 +10,7 @@ import { CollectionAdminSearchResultGridElementComponent } from './admin-search- import { ItemAdminSearchResultActionsComponent } from './admin-search-results/item-admin-search-result-actions.component'; import { JournalEntitiesModule } from '../../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../../entity-groups/research-entities/research-entities.module'; +import { SearchModule } from '../../shared/search/search.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -24,6 +25,7 @@ const ENTRY_COMPONENTS = [ @NgModule({ imports: [ + SearchModule, SharedModule.withEntryComponents(), JournalEntitiesModule.withEntryComponents(), ResearchEntitiesModule.withEntryComponents() diff --git a/src/app/admin/admin-workflow-page/admin-workflow.module.ts b/src/app/admin/admin-workflow-page/admin-workflow.module.ts index 02e5e01023..85e8f00a46 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow.module.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow.module.ts @@ -5,6 +5,7 @@ import { WorkflowItemSearchResultAdminWorkflowGridElementComponent } from './adm import { WorkflowItemAdminWorkflowActionsComponent } from './admin-workflow-search-results/workflow-item-admin-workflow-actions.component'; import { WorkflowItemSearchResultAdminWorkflowListElementComponent } from './admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component'; import { AdminWorkflowPageComponent } from './admin-workflow-page.component'; +import { SearchModule } from '../../shared/search/search.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -14,6 +15,7 @@ const ENTRY_COMPONENTS = [ @NgModule({ imports: [ + SearchModule, SharedModule.withEntryComponents() ], declarations: [ diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts index 6c756dd851..b28a0cf89e 100644 --- a/src/app/admin/admin.module.ts +++ b/src/app/admin/admin.module.ts @@ -24,7 +24,7 @@ const ENTRY_COMPONENTS = [ AccessControlModule, AdminSearchModule.withEntryComponents(), AdminWorkflowModuleModule.withEntryComponents(), - SharedModule, + SharedModule ], declarations: [ AdminCurationTasksComponent, diff --git a/src/app/bitstream-page/bitstream-page.module.ts b/src/app/bitstream-page/bitstream-page.module.ts index 80e5ad14e3..d168a06db2 100644 --- a/src/app/bitstream-page/bitstream-page.module.ts +++ b/src/app/bitstream-page/bitstream-page.module.ts @@ -4,6 +4,8 @@ import { SharedModule } from '../shared/shared.module'; import { EditBitstreamPageComponent } from './edit-bitstream-page/edit-bitstream-page.component'; import { BitstreamPageRoutingModule } from './bitstream-page-routing.module'; import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component'; +import { FormModule } from '../shared/form/form.module'; +import { ResourcePoliciesModule } from '../shared/resource-policies/resource-policies.module'; /** * This module handles all components that are necessary for Bitstream related pages @@ -12,7 +14,9 @@ import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bit imports: [ CommonModule, SharedModule, - BitstreamPageRoutingModule + BitstreamPageRoutingModule, + FormModule, + ResourcePoliciesModule ], declarations: [ BitstreamAuthorizationsComponent, diff --git a/src/app/browse-by/browse-by.module.ts b/src/app/browse-by/browse-by.module.ts index cd44e52b81..e1dfaacea5 100644 --- a/src/app/browse-by/browse-by.module.ts +++ b/src/app/browse-by/browse-by.module.ts @@ -6,6 +6,7 @@ import { BrowseByMetadataPageComponent } from './browse-by-metadata-page/browse- import { BrowseByDatePageComponent } from './browse-by-date-page/browse-by-date-page.component'; import { BrowseBySwitcherComponent } from './browse-by-switcher/browse-by-switcher.component'; import { ThemedBrowseBySwitcherComponent } from './browse-by-switcher/themed-browse-by-switcher.component'; +import { ComcolModule } from '../shared/comcol/comcol.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -17,6 +18,7 @@ const ENTRY_COMPONENTS = [ @NgModule({ imports: [ CommonModule, + ComcolModule, SharedModule ], declarations: [ diff --git a/src/app/collection-page/collection-form/collection-form.component.ts b/src/app/collection-page/collection-form/collection-form.component.ts index 7835ccc8e5..bb84153835 100644 --- a/src/app/collection-page/collection-form/collection-form.component.ts +++ b/src/app/collection-page/collection-form/collection-form.component.ts @@ -10,7 +10,7 @@ import { } from '@ng-dynamic-forms/core'; import { Collection } from '../../core/shared/collection.model'; -import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component'; +import { ComColFormComponent } from '../../shared/comcol/comcol-forms/comcol-form/comcol-form.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { CommunityDataService } from '../../core/data/community-data.service'; import { AuthService } from '../../core/auth/auth.service'; @@ -28,8 +28,8 @@ import { NONE_ENTITY_TYPE } from '../../core/shared/item-relationships/item-type */ @Component({ selector: 'ds-collection-form', - styleUrls: ['../../shared/comcol-forms/comcol-form/comcol-form.component.scss'], - templateUrl: '../../shared/comcol-forms/comcol-form/comcol-form.component.html' + styleUrls: ['../../shared/comcol/comcol-forms/comcol-form/comcol-form.component.scss'], + templateUrl: '../../shared/comcol/comcol-forms/comcol-form/comcol-form.component.html' }) export class CollectionFormComponent extends ComColFormComponent implements OnInit { /** diff --git a/src/app/collection-page/collection-form/collection-form.module.ts b/src/app/collection-page/collection-form/collection-form.module.ts index a9ff5f3ea9..ddf18f0586 100644 --- a/src/app/collection-page/collection-form/collection-form.module.ts +++ b/src/app/collection-page/collection-form/collection-form.module.ts @@ -2,9 +2,13 @@ import { NgModule } from '@angular/core'; import { CollectionFormComponent } from './collection-form.component'; import { SharedModule } from '../../shared/shared.module'; +import { ComcolModule } from '../../shared/comcol/comcol.module'; +import { FormModule } from '../../shared/form/form.module'; @NgModule({ imports: [ + ComcolModule, + FormModule, SharedModule ], declarations: [ diff --git a/src/app/collection-page/collection-page.module.ts b/src/app/collection-page/collection-page.module.ts index a13731ed23..3652823200 100644 --- a/src/app/collection-page/collection-page.module.ts +++ b/src/app/collection-page/collection-page.module.ts @@ -14,6 +14,7 @@ import { SearchService } from '../core/shared/search/search.service'; import { StatisticsModule } from '../statistics/statistics.module'; import { CollectionFormModule } from './collection-form/collection-form.module'; import { ThemedCollectionPageComponent } from './themed-collection-page.component'; +import { ComcolModule } from '../shared/comcol/comcol.module'; @NgModule({ imports: [ @@ -22,7 +23,8 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen CollectionPageRoutingModule, StatisticsModule.forRoot(), EditItemPageModule, - CollectionFormModule + CollectionFormModule, + ComcolModule ], declarations: [ CollectionPageComponent, diff --git a/src/app/collection-page/create-collection-page/create-collection-page.component.ts b/src/app/collection-page/create-collection-page/create-collection-page.component.ts index a38739c407..a938b37ce1 100644 --- a/src/app/collection-page/create-collection-page/create-collection-page.component.ts +++ b/src/app/collection-page/create-collection-page/create-collection-page.component.ts @@ -2,12 +2,12 @@ import { Component } from '@angular/core'; import { CommunityDataService } from '../../core/data/community-data.service'; import { RouteService } from '../../core/services/route.service'; import { Router } from '@angular/router'; -import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component'; +import { CreateComColPageComponent } from '../../shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component'; import { Collection } from '../../core/shared/collection.model'; import { CollectionDataService } from '../../core/data/collection-data.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import {RequestService} from '../../core/data/request.service'; +import { RequestService } from '../../core/data/request.service'; /** * Component that represents the page where a user can create a new Collection diff --git a/src/app/collection-page/delete-collection-page/delete-collection-page.component.ts b/src/app/collection-page/delete-collection-page/delete-collection-page.component.ts index 8daba0abfc..3e30373070 100644 --- a/src/app/collection-page/delete-collection-page/delete-collection-page.component.ts +++ b/src/app/collection-page/delete-collection-page/delete-collection-page.component.ts @@ -1,11 +1,11 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { DeleteComColPageComponent } from '../../shared/comcol-forms/delete-comcol-page/delete-comcol-page.component'; +import { DeleteComColPageComponent } from '../../shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { CollectionDataService } from '../../core/data/collection-data.service'; import { Collection } from '../../core/shared/collection.model'; import { TranslateService } from '@ngx-translate/core'; -import {RequestService} from '../../core/data/request.service'; +import { RequestService } from '../../core/data/request.service'; /** * Component that represents the page where a user can delete an existing Collection diff --git a/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts b/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts index 8cb10775ac..cfaad3767e 100644 --- a/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts +++ b/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; +import { ComcolMetadataComponent } from '../../../shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; import { Collection } from '../../../core/shared/collection.model'; import { CollectionDataService } from '../../../core/data/collection-data.service'; import { ActivatedRoute, Router } from '@angular/router'; diff --git a/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.spec.ts b/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.spec.ts index 401065a661..985290a592 100644 --- a/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.spec.ts +++ b/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.spec.ts @@ -12,6 +12,7 @@ import { RequestService } from '../../../core/data/request.service'; import { RouterTestingModule } from '@angular/router/testing'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ComcolModule } from '../../../shared/comcol/comcol.module'; describe('CollectionRolesComponent', () => { @@ -65,6 +66,7 @@ describe('CollectionRolesComponent', () => { TestBed.configureTestingModule({ imports: [ + ComcolModule, SharedModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), diff --git a/src/app/collection-page/edit-collection-page/edit-collection-page.component.ts b/src/app/collection-page/edit-collection-page/edit-collection-page.component.ts index aff1995a14..62fbb3ee3d 100644 --- a/src/app/collection-page/edit-collection-page/edit-collection-page.component.ts +++ b/src/app/collection-page/edit-collection-page/edit-collection-page.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component'; +import { EditComColPageComponent } from '../../shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component'; import { Collection } from '../../core/shared/collection.model'; import { getCollectionPageRoute } from '../collection-page-routing-paths'; @@ -9,7 +9,7 @@ import { getCollectionPageRoute } from '../collection-page-routing-paths'; */ @Component({ selector: 'ds-edit-collection', - templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html' + templateUrl: '../../shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.html' }) export class EditCollectionPageComponent extends EditComColPageComponent { type = 'collection'; diff --git a/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts b/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts index 0b09542fa0..45612be41a 100644 --- a/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts +++ b/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts @@ -10,6 +10,9 @@ import { CollectionSourceComponent } from './collection-source/collection-source import { CollectionAuthorizationsComponent } from './collection-authorizations/collection-authorizations.component'; import { CollectionFormModule } from '../collection-form/collection-form.module'; import { CollectionSourceControlsComponent } from './collection-source/collection-source-controls/collection-source-controls.component'; +import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-policies.module'; +import { FormModule } from '../../shared/form/form.module'; +import { ComcolModule } from '../../shared/comcol/comcol.module'; /** * Module that contains all components related to the Edit Collection page administrator functionality @@ -19,7 +22,10 @@ import { CollectionSourceControlsComponent } from './collection-source/collectio CommonModule, SharedModule, EditCollectionPageRoutingModule, - CollectionFormModule + CollectionFormModule, + ResourcePoliciesModule, + FormModule, + ComcolModule ], declarations: [ EditCollectionPageComponent, diff --git a/src/app/community-page/community-form/community-form.component.ts b/src/app/community-page/community-form/community-form.component.ts index 59480e8f68..a3730fd418 100644 --- a/src/app/community-page/community-form/community-form.component.ts +++ b/src/app/community-page/community-form/community-form.component.ts @@ -6,7 +6,7 @@ import { DynamicTextAreaModel } from '@ng-dynamic-forms/core'; import { Community } from '../../core/shared/community.model'; -import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component'; +import { ComColFormComponent } from '../../shared/comcol/comcol-forms/comcol-form/comcol-form.component'; import { TranslateService } from '@ngx-translate/core'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { CommunityDataService } from '../../core/data/community-data.service'; @@ -19,8 +19,8 @@ import { ObjectCacheService } from '../../core/cache/object-cache.service'; */ @Component({ selector: 'ds-community-form', - styleUrls: ['../../shared/comcol-forms/comcol-form/comcol-form.component.scss'], - templateUrl: '../../shared/comcol-forms/comcol-form/comcol-form.component.html' + styleUrls: ['../../shared/comcol/comcol-forms/comcol-form/comcol-form.component.scss'], + templateUrl: '../../shared/comcol/comcol-forms/comcol-form/comcol-form.component.html' }) export class CommunityFormComponent extends ComColFormComponent { /** diff --git a/src/app/community-page/community-form/community-form.module.ts b/src/app/community-page/community-form/community-form.module.ts index 36dbea5c0d..925d218973 100644 --- a/src/app/community-page/community-form/community-form.module.ts +++ b/src/app/community-page/community-form/community-form.module.ts @@ -2,9 +2,13 @@ import { NgModule } from '@angular/core'; import { CommunityFormComponent } from './community-form.component'; import { SharedModule } from '../../shared/shared.module'; +import { ComcolModule } from '../../shared/comcol/comcol.module'; +import { FormModule } from '../../shared/form/form.module'; @NgModule({ imports: [ + ComcolModule, + FormModule, SharedModule ], declarations: [ diff --git a/src/app/community-page/community-page.module.ts b/src/app/community-page/community-page.module.ts index 3ae75f166c..724b762e90 100644 --- a/src/app/community-page/community-page.module.ts +++ b/src/app/community-page/community-page.module.ts @@ -12,6 +12,7 @@ import { DeleteCommunityPageComponent } from './delete-community-page/delete-com import { StatisticsModule } from '../statistics/statistics.module'; import { CommunityFormModule } from './community-form/community-form.module'; import { ThemedCommunityPageComponent } from './themed-community-page.component'; +import { ComcolModule } from '../shared/comcol/comcol.module'; const DECLARATIONS = [CommunityPageComponent, ThemedCommunityPageComponent, @@ -26,7 +27,8 @@ const DECLARATIONS = [CommunityPageComponent, SharedModule, CommunityPageRoutingModule, StatisticsModule.forRoot(), - CommunityFormModule + CommunityFormModule, + ComcolModule ], declarations: [ ...DECLARATIONS diff --git a/src/app/community-page/create-community-page/create-community-page.component.ts b/src/app/community-page/create-community-page/create-community-page.component.ts index be3385b92f..b332fad100 100644 --- a/src/app/community-page/create-community-page/create-community-page.component.ts +++ b/src/app/community-page/create-community-page/create-community-page.component.ts @@ -3,7 +3,7 @@ import { Community } from '../../core/shared/community.model'; import { CommunityDataService } from '../../core/data/community-data.service'; import { RouteService } from '../../core/services/route.service'; import { Router } from '@angular/router'; -import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component'; +import { CreateComColPageComponent } from '../../shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { RequestService } from '../../core/data/request.service'; diff --git a/src/app/community-page/delete-community-page/delete-community-page.component.ts b/src/app/community-page/delete-community-page/delete-community-page.component.ts index ec51076bbc..0cccc503e1 100644 --- a/src/app/community-page/delete-community-page/delete-community-page.component.ts +++ b/src/app/community-page/delete-community-page/delete-community-page.component.ts @@ -2,10 +2,10 @@ import { Component } from '@angular/core'; import { Community } from '../../core/shared/community.model'; import { CommunityDataService } from '../../core/data/community-data.service'; import { ActivatedRoute, Router } from '@angular/router'; -import { DeleteComColPageComponent } from '../../shared/comcol-forms/delete-comcol-page/delete-comcol-page.component'; +import { DeleteComColPageComponent } from '../../shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import {RequestService} from '../../core/data/request.service'; +import { RequestService } from '../../core/data/request.service'; /** * Component that represents the page where a user can delete an existing Community diff --git a/src/app/community-page/edit-community-page/community-metadata/community-metadata.component.ts b/src/app/community-page/edit-community-page/community-metadata/community-metadata.component.ts index c4bb88289f..a2dbfa6eb6 100644 --- a/src/app/community-page/edit-community-page/community-metadata/community-metadata.component.ts +++ b/src/app/community-page/edit-community-page/community-metadata/community-metadata.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; +import { ComcolMetadataComponent } from '../../../shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; import { ActivatedRoute, Router } from '@angular/router'; import { Community } from '../../../core/shared/community.model'; import { CommunityDataService } from '../../../core/data/community-data.service'; diff --git a/src/app/community-page/edit-community-page/community-roles/community-roles.component.spec.ts b/src/app/community-page/edit-community-page/community-roles/community-roles.component.spec.ts index b61705126c..d1188df02d 100644 --- a/src/app/community-page/edit-community-page/community-roles/community-roles.component.spec.ts +++ b/src/app/community-page/edit-community-page/community-roles/community-roles.component.spec.ts @@ -12,6 +12,7 @@ import { SharedModule } from '../../../shared/shared.module'; import { RouterTestingModule } from '@angular/router/testing'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ComcolModule } from '../../../shared/comcol/comcol.module'; describe('CommunityRolesComponent', () => { @@ -50,6 +51,7 @@ describe('CommunityRolesComponent', () => { TestBed.configureTestingModule({ imports: [ + ComcolModule, SharedModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), diff --git a/src/app/community-page/edit-community-page/edit-community-page.component.ts b/src/app/community-page/edit-community-page/edit-community-page.component.ts index 836384ab84..54a6ee4944 100644 --- a/src/app/community-page/edit-community-page/edit-community-page.component.ts +++ b/src/app/community-page/edit-community-page/edit-community-page.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { Community } from '../../core/shared/community.model'; import { ActivatedRoute, Router } from '@angular/router'; -import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component'; +import { EditComColPageComponent } from '../../shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component'; import { getCommunityPageRoute } from '../community-page-routing-paths'; /** @@ -9,7 +9,7 @@ import { getCommunityPageRoute } from '../community-page-routing-paths'; */ @Component({ selector: 'ds-edit-community', - templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html' + templateUrl: '../../shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.html' }) export class EditCommunityPageComponent extends EditComColPageComponent { type = 'community'; diff --git a/src/app/community-page/edit-community-page/edit-community-page.module.ts b/src/app/community-page/edit-community-page/edit-community-page.module.ts index 3f7511f700..2b0fc73f2a 100644 --- a/src/app/community-page/edit-community-page/edit-community-page.module.ts +++ b/src/app/community-page/edit-community-page/edit-community-page.module.ts @@ -8,6 +8,8 @@ import { CommunityMetadataComponent } from './community-metadata/community-metad import { CommunityRolesComponent } from './community-roles/community-roles.component'; import { CommunityAuthorizationsComponent } from './community-authorizations/community-authorizations.component'; import { CommunityFormModule } from '../community-form/community-form.module'; +import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-policies.module'; +import { ComcolModule } from '../../shared/comcol/comcol.module'; /** * Module that contains all components related to the Edit Community page administrator functionality @@ -17,7 +19,9 @@ import { CommunityFormModule } from '../community-form/community-form.module'; CommonModule, SharedModule, EditCommunityPageRoutingModule, - CommunityFormModule + CommunityFormModule, + ComcolModule, + ResourcePoliciesModule ], declarations: [ EditCommunityPageComponent, diff --git a/src/app/entity-groups/journal-entities/journal-entities.module.ts b/src/app/entity-groups/journal-entities/journal-entities.module.ts index dc88490eac..3bf861e10d 100644 --- a/src/app/entity-groups/journal-entities/journal-entities.module.ts +++ b/src/app/entity-groups/journal-entities/journal-entities.module.ts @@ -19,6 +19,7 @@ import { JournalVolumeSearchResultGridElementComponent } from './item-grid-eleme import { JournalVolumeSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/journal-volume/journal-volume-sidebar-search-list-element.component'; import { JournalIssueSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/journal-issue/journal-issue-sidebar-search-list-element.component'; import { JournalSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/journal/journal-sidebar-search-list-element.component'; +import { ItemSharedModule } from '../../item-page/item-shared.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -45,6 +46,7 @@ const ENTRY_COMPONENTS = [ @NgModule({ imports: [ CommonModule, + ItemSharedModule, SharedModule ], declarations: [ diff --git a/src/app/entity-groups/research-entities/research-entities.module.ts b/src/app/entity-groups/research-entities/research-entities.module.ts index c9d6c2d5b6..721a22be08 100644 --- a/src/app/entity-groups/research-entities/research-entities.module.ts +++ b/src/app/entity-groups/research-entities/research-entities.module.ts @@ -1,5 +1,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { SharedModule } from '../../shared/shared.module'; import { OrgUnitComponent } from './item-pages/org-unit/org-unit.component'; import { PersonComponent } from './item-pages/person/person.component'; @@ -27,6 +28,7 @@ import { ExternalSourceEntryListSubmissionElementComponent } from './submission/ import { OrgUnitSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/org-unit/org-unit-sidebar-search-list-element.component'; import { PersonSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/person/person-sidebar-search-list-element.component'; import { ProjectSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/project/project-sidebar-search-list-element.component'; +import { ItemSharedModule } from '../../item-page/item-shared.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -65,7 +67,9 @@ const COMPONENTS = [ @NgModule({ imports: [ CommonModule, - SharedModule + ItemSharedModule, + SharedModule, + NgbTooltipModule ], declarations: [ ...COMPONENTS, @@ -79,7 +83,7 @@ export class ResearchEntitiesModule { static withEntryComponents() { return { ngModule: ResearchEntitiesModule, - providers: ENTRY_COMPONENTS.map((component) => ({provide: component})) + providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })) }; } } diff --git a/src/app/item-page/edit-item-page/edit-item-page.module.ts b/src/app/item-page/edit-item-page/edit-item-page.module.ts index 11b1d585ba..97901bd7c8 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.module.ts @@ -1,5 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; + +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; + import { SharedModule } from '../../shared/shared.module'; import { EditItemPageRoutingModule } from './edit-item-page.routing.module'; import { EditItemPageComponent } from './edit-item-page.component'; @@ -31,6 +34,8 @@ import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.co import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component'; import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe'; +import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-policies.module'; + /** * Module that contains all components related to the Edit Item page administrator functionality @@ -39,9 +44,11 @@ import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe'; imports: [ CommonModule, SharedModule, + NgbTooltipModule, EditItemPageRoutingModule, SearchPageModule, - DragDropModule + DragDropModule, + ResourcePoliciesModule ], declarations: [ EditItemPageComponent, diff --git a/src/app/item-page/item-shared.module.ts b/src/app/item-page/item-shared.module.ts new file mode 100644 index 0000000000..b191b6c4b3 --- /dev/null +++ b/src/app/item-page/item-shared.module.ts @@ -0,0 +1,36 @@ +import { RelatedEntitiesSearchComponent } from './simple/related-entities/related-entities-search/related-entities-search.component'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SearchModule } from '../shared/search/search.module'; +import { SharedModule } from '../shared/shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { DYNAMIC_FORM_CONTROL_MAP_FN } from '@ng-dynamic-forms/core'; +import { dsDynamicFormControlMapFn } from '../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component'; +import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component'; + +const COMPONENTS = [ + RelatedEntitiesSearchComponent, + TabbedRelatedEntitiesSearchComponent +]; + +@NgModule({ + declarations: [ + ...COMPONENTS + ], + imports: [ + CommonModule, + SearchModule, + SharedModule, + TranslateModule + ], + exports: [ + ...COMPONENTS + ], + providers: [ + { + provide: DYNAMIC_FORM_CONTROL_MAP_FN, + useValue: dsDynamicFormControlMapFn + } + ] +}) +export class ItemSharedModule { } diff --git a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts index 46c02a613a..781e72795b 100644 --- a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts +++ b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts @@ -10,7 +10,7 @@ import { getFilterByRelation } from '../../../../shared/utils/relation-query.uti }) /** * A component to show related items as search results. - * Related items can be facetted, or queried using an + * Related items can be faceted, or queried using an * optional search box. */ export class RelatedEntitiesSearchComponent implements OnInit { diff --git a/src/app/my-dspace-page/my-dspace-page.module.ts b/src/app/my-dspace-page/my-dspace-page.module.ts index a5a18effbc..183edf2728 100644 --- a/src/app/my-dspace-page/my-dspace-page.module.ts +++ b/src/app/my-dspace-page/my-dspace-page.module.ts @@ -14,6 +14,7 @@ import { MyDspaceSearchModule } from './my-dspace-search.module'; import { MyDSpaceNewSubmissionDropdownComponent } from './my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component'; import { MyDSpaceNewExternalDropdownComponent } from './my-dspace-new-submission/my-dspace-new-external-dropdown/my-dspace-new-external-dropdown.component'; import { ThemedMyDSpacePageComponent } from './themed-my-dspace-page.component'; +import { SearchModule } from '../shared/search/search.module'; const DECLARATIONS = [ MyDSpacePageComponent, @@ -29,6 +30,7 @@ const DECLARATIONS = [ imports: [ CommonModule, SharedModule, + SearchModule, MyDspacePageRoutingModule, MyDspaceSearchModule.withEntryComponents() ], diff --git a/src/app/profile-page/profile-page.module.ts b/src/app/profile-page/profile-page.module.ts index 0b2274f2d4..dc9595140b 100644 --- a/src/app/profile-page/profile-page.module.ts +++ b/src/app/profile-page/profile-page.module.ts @@ -6,12 +6,14 @@ import { ProfilePageComponent } from './profile-page.component'; import { ProfilePageMetadataFormComponent } from './profile-page-metadata-form/profile-page-metadata-form.component'; import { ProfilePageSecurityFormComponent } from './profile-page-security-form/profile-page-security-form.component'; import { ThemedProfilePageComponent } from './themed-profile-page.component'; +import { FormModule } from '../shared/form/form.module'; @NgModule({ imports: [ ProfilePageRoutingModule, CommonModule, - SharedModule + SharedModule, + FormModule ], exports: [ ProfilePageSecurityFormComponent, diff --git a/src/app/search-page/search-page.module.ts b/src/app/search-page/search-page.module.ts index 0cf6164f8a..d7015974d8 100644 --- a/src/app/search-page/search-page.module.ts +++ b/src/app/search-page/search-page.module.ts @@ -14,6 +14,7 @@ import { SearchConfigurationService } from '../core/shared/search/search-configu import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; import { ThemedSearchPageComponent } from './themed-search-page.component'; +import { SearchModule } from '../shared/search/search.module'; const components = [ SearchPageComponent, @@ -25,6 +26,7 @@ const components = [ @NgModule({ imports: [ CommonModule, + SearchModule, SharedModule.withEntryComponents(), CoreModule.forRoot(), StatisticsModule.forRoot(), diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.html similarity index 100% rename from src/app/shared/comcol-forms/comcol-form/comcol-form.component.html rename to src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.html diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.scss b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.scss similarity index 100% rename from src/app/shared/comcol-forms/comcol-form/comcol-form.component.scss rename to src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.scss diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.spec.ts similarity index 93% rename from src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts rename to src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.spec.ts index c7c6e2e5e7..e5c2033d8b 100644 --- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.spec.ts @@ -7,19 +7,19 @@ import { RouterTestingModule } from '@angular/router/testing'; import { DynamicFormControlModel, DynamicFormService, DynamicInputModel } from '@ng-dynamic-forms/core'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; -import { AuthService } from '../../../core/auth/auth.service'; -import { ObjectCacheService } from '../../../core/cache/object-cache.service'; -import { RequestService } from '../../../core/data/request.service'; -import { RestRequestMethod } from '../../../core/data/rest-request-method'; -import { Community } from '../../../core/shared/community.model'; -import { hasValue } from '../../empty.util'; -import { AuthServiceMock } from '../../mocks/auth.service.mock'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; -import { VarDirective } from '../../utils/var.directive'; +import { AuthService } from '../../../../core/auth/auth.service'; +import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; +import { RequestService } from '../../../../core/data/request.service'; +import { RestRequestMethod } from '../../../../core/data/rest-request-method'; +import { Community } from '../../../../core/shared/community.model'; +import { hasValue } from '../../../empty.util'; +import { AuthServiceMock } from '../../../mocks/auth.service.mock'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { VarDirective } from '../../../utils/var.directive'; import { ComColFormComponent } from './comcol-form.component'; import { Operation } from 'fast-json-patch'; -import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; describe('ComColFormComponent', () => { let comp: ComColFormComponent; diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts similarity index 85% rename from src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts rename to src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts index ef7a5e55e8..c3ba9dcaee 100644 --- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts +++ b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts @@ -1,39 +1,27 @@ -import { - Component, - EventEmitter, - Input, - OnDestroy, - OnInit, - Output, - ViewChild -} from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { - DynamicFormControlModel, - DynamicFormService, - DynamicInputModel -} from '@ng-dynamic-forms/core'; +import { DynamicFormControlModel, DynamicFormService, DynamicInputModel } from '@ng-dynamic-forms/core'; import { TranslateService } from '@ngx-translate/core'; import { FileUploader } from 'ng2-file-upload'; import { BehaviorSubject, combineLatest as observableCombineLatest, Subscription } from 'rxjs'; -import { AuthService } from '../../../core/auth/auth.service'; -import { ObjectCacheService } from '../../../core/cache/object-cache.service'; -import { ComColDataService } from '../../../core/data/comcol-data.service'; -import { RemoteData } from '../../../core/data/remote-data'; -import { RequestService } from '../../../core/data/request.service'; -import { RestRequestMethod } from '../../../core/data/rest-request-method'; -import { Bitstream } from '../../../core/shared/bitstream.model'; -import { Collection } from '../../../core/shared/collection.model'; -import { Community } from '../../../core/shared/community.model'; -import { MetadataMap, MetadataValue } from '../../../core/shared/metadata.models'; -import { ResourceType } from '../../../core/shared/resource-type'; -import { hasValue, isNotEmpty } from '../../empty.util'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { UploaderOptions } from '../../uploader/uploader-options.model'; -import { UploaderComponent } from '../../uploader/uploader.component'; +import { AuthService } from '../../../../core/auth/auth.service'; +import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; +import { ComColDataService } from '../../../../core/data/comcol-data.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { RequestService } from '../../../../core/data/request.service'; +import { RestRequestMethod } from '../../../../core/data/rest-request-method'; +import { Bitstream } from '../../../../core/shared/bitstream.model'; +import { Collection } from '../../../../core/shared/collection.model'; +import { Community } from '../../../../core/shared/community.model'; +import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models'; +import { ResourceType } from '../../../../core/shared/resource-type'; +import { hasValue, isNotEmpty } from '../../../empty.util'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { UploaderOptions } from '../../../uploader/uploader-options.model'; +import { UploaderComponent } from '../../../uploader/uploader.component'; import { Operation } from 'fast-json-patch'; -import { NoContent } from '../../../core/shared/NoContent.model'; -import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { NoContent } from '../../../../core/shared/NoContent.model'; +import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; /** * A form for creating and editing Communities or Collections diff --git a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts b/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts similarity index 90% rename from src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts rename to src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts index 05c3e50e4b..0104de83ca 100644 --- a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { CommunityDataService } from '../../../core/data/community-data.service'; -import { RouteService } from '../../../core/services/route.service'; +import { CommunityDataService } from '../../../../core/data/community-data.service'; +import { RouteService } from '../../../../core/services/route.service'; import { Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; -import { Community } from '../../../core/shared/community.model'; -import { SharedModule } from '../../shared.module'; +import { Community } from '../../../../core/shared/community.model'; +import { SharedModule } from '../../../shared.module'; import { CommonModule } from '@angular/common'; import { RouterTestingModule } from '@angular/router/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { CreateComColPageComponent } from './create-comcol-page.component'; -import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; -import { ComColDataService } from '../../../core/data/comcol-data.service'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; -import { RequestService } from '../../../core/data/request.service'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; +import { ComColDataService } from '../../../../core/data/comcol-data.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { RequestService } from '../../../../core/data/request.service'; import { getTestScheduler } from 'jasmine-marbles'; describe('CreateComColPageComponent', () => { diff --git a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts b/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts similarity index 77% rename from src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts rename to src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts index b1be5ae9be..2db9fcc20c 100644 --- a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts +++ b/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts @@ -3,18 +3,18 @@ import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { mergeMap, take } from 'rxjs/operators'; -import { ComColDataService } from '../../../core/data/comcol-data.service'; -import { CommunityDataService } from '../../../core/data/community-data.service'; -import { RemoteData } from '../../../core/data/remote-data'; -import { RouteService } from '../../../core/services/route.service'; -import { Community } from '../../../core/shared/community.model'; -import { getFirstSucceededRemoteDataPayload, } from '../../../core/shared/operators'; -import { ResourceType } from '../../../core/shared/resource-type'; -import { hasValue, isNotEmpty, isNotUndefined } from '../../empty.util'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { RequestParam } from '../../../core/cache/models/request-param.model'; -import { RequestService } from '../../../core/data/request.service'; -import { Collection } from '../../../core/shared/collection.model'; +import { ComColDataService } from '../../../../core/data/comcol-data.service'; +import { CommunityDataService } from '../../../../core/data/community-data.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { RouteService } from '../../../../core/services/route.service'; +import { Community } from '../../../../core/shared/community.model'; +import { getFirstSucceededRemoteDataPayload, } from '../../../../core/shared/operators'; +import { ResourceType } from '../../../../core/shared/resource-type'; +import { hasValue, isNotEmpty, isNotUndefined } from '../../../empty.util'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { RequestParam } from '../../../../core/cache/models/request-param.model'; +import { RequestService } from '../../../../core/data/request.service'; +import { Collection } from '../../../../core/shared/collection.model'; /** * Component representing the create page for communities and collections diff --git a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts similarity index 91% rename from src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts rename to src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts index 6eaf102808..6aab79c5d4 100644 --- a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { CommunityDataService } from '../../../core/data/community-data.service'; +import { CommunityDataService } from '../../../../core/data/community-data.service'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; -import { Community } from '../../../core/shared/community.model'; -import { SharedModule } from '../../shared.module'; +import { Community } from '../../../../core/shared/community.model'; +import { SharedModule } from '../../../shared.module'; import { CommonModule } from '@angular/common'; import { RouterTestingModule } from '@angular/router/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { DeleteComColPageComponent } from './delete-comcol-page.component'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; -import { RequestService } from '../../../core/data/request.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { RequestService } from '../../../../core/data/request.service'; import { getTestScheduler } from 'jasmine-marbles'; -import { ComColDataService } from '../../../core/data/comcol-data.service'; -import { createFailedRemoteDataObject$, createNoContentRemoteDataObject$ } from '../../remote-data.utils'; +import { ComColDataService } from '../../../../core/data/comcol-data.service'; +import { createFailedRemoteDataObject$, createNoContentRemoteDataObject$ } from '../../../remote-data.utils'; describe('DeleteComColPageComponent', () => { let comp: DeleteComColPageComponent; diff --git a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts similarity index 80% rename from src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts rename to src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts index 8579becc83..f781b71f3f 100644 --- a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts +++ b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts @@ -1,16 +1,16 @@ import { Component, OnInit } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; -import { RemoteData } from '../../../core/data/remote-data'; +import { RemoteData } from '../../../../core/data/remote-data'; import { first, map } from 'rxjs/operators'; -import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; -import { NoContent } from '../../../core/shared/NoContent.model'; -import { RequestService } from '../../../core/data/request.service'; -import { ComColDataService } from '../../../core/data/comcol-data.service'; -import { Community } from '../../../core/shared/community.model'; -import { Collection } from '../../../core/shared/collection.model'; +import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; +import { NoContent } from '../../../../core/shared/NoContent.model'; +import { RequestService } from '../../../../core/data/request.service'; +import { ComColDataService } from '../../../../core/data/comcol-data.service'; +import { Community } from '../../../../core/shared/community.model'; +import { Collection } from '../../../../core/shared/collection.model'; /** * Component representing the delete page for communities and collections diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts similarity index 92% rename from src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts index 9c8d616383..6588d9cbac 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts @@ -5,16 +5,16 @@ import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; -import { ComColDataService } from '../../../../core/data/comcol-data.service'; -import { Community } from '../../../../core/shared/community.model'; -import { NotificationsService } from '../../../notifications/notifications.service'; -import { SharedModule } from '../../../shared.module'; -import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { ComColDataService } from '../../../../../core/data/comcol-data.service'; +import { Community } from '../../../../../core/shared/community.model'; +import { NotificationsService } from '../../../../notifications/notifications.service'; +import { SharedModule } from '../../../../shared.module'; +import { NotificationsServiceStub } from '../../../../testing/notifications-service.stub'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ -} from '../../../remote-data.utils'; +} from '../../../../remote-data.utils'; import { ComcolMetadataComponent } from './comcol-metadata.component'; describe('ComColMetadataComponent', () => { diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts similarity index 79% rename from src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts index cc68ad4970..5bd51ea650 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts @@ -1,17 +1,17 @@ import { Component, OnInit } from '@angular/core'; -import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; import { Observable } from 'rxjs'; -import { RemoteData } from '../../../../core/data/remote-data'; +import { RemoteData } from '../../../../../core/data/remote-data'; import { ActivatedRoute, Router } from '@angular/router'; import { first, map, take } from 'rxjs/operators'; -import { getFirstSucceededRemoteData, getFirstCompletedRemoteData } from '../../../../core/shared/operators'; -import { hasValue, isEmpty } from '../../../empty.util'; -import { ResourceType } from '../../../../core/shared/resource-type'; -import { ComColDataService } from '../../../../core/data/comcol-data.service'; -import { NotificationsService } from '../../../notifications/notifications.service'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteData } from '../../../../../core/shared/operators'; +import { hasValue, isEmpty } from '../../../../empty.util'; +import { ResourceType } from '../../../../../core/shared/resource-type'; +import { ComColDataService } from '../../../../../core/data/comcol-data.service'; +import { NotificationsService } from '../../../../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { Community } from '../../../../core/shared/community.model'; -import { Collection } from '../../../../core/shared/collection.model'; +import { Community } from '../../../../../core/shared/community.model'; +import { Collection } from '../../../../../core/shared/collection.model'; @Component({ selector: 'ds-comcol-metadata', diff --git a/src/app/shared/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 similarity index 100% rename from src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.scss b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.scss similarity index 100% rename from src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.scss rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.scss diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts similarity index 94% rename from src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts index cc5b64b9b5..175abe48e4 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts @@ -1,15 +1,15 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ComcolRoleComponent } from './comcol-role.component'; -import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { GroupDataService } from '../../../../../core/eperson/group-data.service'; import { By } from '@angular/platform-browser'; -import { SharedModule } from '../../../shared.module'; import { TranslateModule } from '@ngx-translate/core'; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { RequestService } from '../../../../core/data/request.service'; +import { RequestService } from '../../../../../core/data/request.service'; import { of as observableOf } from 'rxjs'; import { RouterTestingModule } from '@angular/router/testing'; -import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ComcolModule } from '../../../comcol.module'; describe('ComcolRoleComponent', () => { @@ -32,7 +32,7 @@ describe('ComcolRoleComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - SharedModule, + ComcolModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NoopAnimationsModule diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts similarity index 82% rename from src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts index 4eb364a3cc..7ed88fae1c 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts @@ -1,17 +1,17 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Group } from '../../../../core/eperson/models/group.model'; -import { Community } from '../../../../core/shared/community.model'; +import { Group } from '../../../../../core/eperson/models/group.model'; +import { Community } from '../../../../../core/shared/community.model'; import { BehaviorSubject, Observable } from 'rxjs'; -import { GroupDataService } from '../../../../core/eperson/group-data.service'; -import { Collection } from '../../../../core/shared/collection.model'; +import { GroupDataService } from '../../../../../core/eperson/group-data.service'; +import { Collection } from '../../../../../core/shared/collection.model'; import { filter, map, switchMap } from 'rxjs/operators'; -import { getAllCompletedRemoteData, getFirstCompletedRemoteData } from '../../../../core/shared/operators'; -import { RequestService } from '../../../../core/data/request.service'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { HALLink } from '../../../../core/shared/hal-link.model'; -import { getGroupEditRoute } from '../../../../access-control/access-control-routing-paths'; -import { hasNoValue, hasValue } from '../../../empty.util'; -import { NoContent } from '../../../../core/shared/NoContent.model'; +import { getAllCompletedRemoteData, getFirstCompletedRemoteData } from '../../../../../core/shared/operators'; +import { RequestService } from '../../../../../core/data/request.service'; +import { RemoteData } from '../../../../../core/data/remote-data'; +import { HALLink } from '../../../../../core/shared/hal-link.model'; +import { getGroupEditRoute } from '../../../../../access-control/access-control-routing-paths'; +import { hasNoValue, hasValue } from '../../../../empty.util'; +import { NoContent } from '../../../../../core/shared/NoContent.model'; /** * Component for managing a community or collection role. diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html b/src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.html similarity index 100% rename from src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.html diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts similarity index 91% rename from src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts index d13958bb79..d3d936c9f7 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts @@ -2,12 +2,12 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; -import { Community } from '../../../core/shared/community.model'; -import { SharedModule } from '../../shared.module'; +import { Community } from '../../../../core/shared/community.model'; +import { SharedModule } from '../../../shared.module'; import { CommonModule } from '@angular/common'; import { RouterTestingModule } from '@angular/router/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { EditComColPageComponent } from './edit-comcol-page.component'; describe('EditComColPageComponent', () => { diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts similarity index 90% rename from src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts rename to src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts index 793065096f..48eb9aec96 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts @@ -4,9 +4,9 @@ import { Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; -import { RemoteData } from '../../../core/data/remote-data'; -import { isNotEmpty } from '../../empty.util'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { isNotEmpty } from '../../../empty.util'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; /** * Component representing the edit page for communities and collections diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html similarity index 100% rename from src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html rename to src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.scss b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.scss similarity index 100% rename from src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.scss rename to src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.scss diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts similarity index 86% rename from src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts rename to src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts index 01912dbcaa..b65ad5140c 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -2,10 +2,10 @@ import { Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; -import { environment } from '../../../environments/environment'; -import { getCommunityPageRoute } from '../../community-page/community-page-routing-paths'; -import { getCollectionPageRoute } from '../../collection-page/collection-page-routing-paths'; +import { BrowseByTypeConfig } from '../../../../config/browse-by-type-config.interface'; +import { environment } from '../../../../environments/environment'; +import { getCommunityPageRoute } from '../../../community-page/community-page-routing-paths'; +import { getCollectionPageRoute } from '../../../collection-page/collection-page-routing-paths'; export interface ComColPageNavOption { id: string; diff --git a/src/app/shared/comcol-page-content/comcol-page-content.component.html b/src/app/shared/comcol/comcol-page-content/comcol-page-content.component.html similarity index 100% rename from src/app/shared/comcol-page-content/comcol-page-content.component.html rename to src/app/shared/comcol/comcol-page-content/comcol-page-content.component.html diff --git a/src/app/shared/comcol-page-content/comcol-page-content.component.scss b/src/app/shared/comcol/comcol-page-content/comcol-page-content.component.scss similarity index 100% rename from src/app/shared/comcol-page-content/comcol-page-content.component.scss rename to src/app/shared/comcol/comcol-page-content/comcol-page-content.component.scss diff --git a/src/app/shared/comcol-page-content/comcol-page-content.component.ts b/src/app/shared/comcol/comcol-page-content/comcol-page-content.component.ts similarity index 100% rename from src/app/shared/comcol-page-content/comcol-page-content.component.ts rename to src/app/shared/comcol/comcol-page-content/comcol-page-content.component.ts diff --git a/src/app/shared/comcol-page-handle/comcol-page-handle.component.html b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html similarity index 100% rename from src/app/shared/comcol-page-handle/comcol-page-handle.component.html rename to src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html diff --git a/src/app/shared/comcol-page-handle/comcol-page-handle.component.scss b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.scss similarity index 100% rename from src/app/shared/comcol-page-handle/comcol-page-handle.component.scss rename to src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.scss diff --git a/src/app/shared/comcol-page-handle/comcol-page-handle.component.spec.ts b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.spec.ts similarity index 100% rename from src/app/shared/comcol-page-handle/comcol-page-handle.component.spec.ts rename to src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.spec.ts diff --git a/src/app/shared/comcol-page-handle/comcol-page-handle.component.ts b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.ts similarity index 100% rename from src/app/shared/comcol-page-handle/comcol-page-handle.component.ts rename to src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.ts diff --git a/src/app/shared/comcol-page-header/comcol-page-header.component.html b/src/app/shared/comcol/comcol-page-header/comcol-page-header.component.html similarity index 100% rename from src/app/shared/comcol-page-header/comcol-page-header.component.html rename to src/app/shared/comcol/comcol-page-header/comcol-page-header.component.html diff --git a/src/app/shared/comcol-page-header/comcol-page-header.component.scss b/src/app/shared/comcol/comcol-page-header/comcol-page-header.component.scss similarity index 100% rename from src/app/shared/comcol-page-header/comcol-page-header.component.scss rename to src/app/shared/comcol/comcol-page-header/comcol-page-header.component.scss diff --git a/src/app/shared/comcol-page-header/comcol-page-header.component.ts b/src/app/shared/comcol/comcol-page-header/comcol-page-header.component.ts similarity index 100% rename from src/app/shared/comcol-page-header/comcol-page-header.component.ts rename to src/app/shared/comcol/comcol-page-header/comcol-page-header.component.ts diff --git a/src/app/shared/comcol-page-logo/comcol-page-logo.component.html b/src/app/shared/comcol/comcol-page-logo/comcol-page-logo.component.html similarity index 100% rename from src/app/shared/comcol-page-logo/comcol-page-logo.component.html rename to src/app/shared/comcol/comcol-page-logo/comcol-page-logo.component.html diff --git a/src/app/shared/comcol-page-logo/comcol-page-logo.component.scss b/src/app/shared/comcol/comcol-page-logo/comcol-page-logo.component.scss similarity index 100% rename from src/app/shared/comcol-page-logo/comcol-page-logo.component.scss rename to src/app/shared/comcol/comcol-page-logo/comcol-page-logo.component.scss diff --git a/src/app/shared/comcol-page-logo/comcol-page-logo.component.ts b/src/app/shared/comcol/comcol-page-logo/comcol-page-logo.component.ts similarity index 95% rename from src/app/shared/comcol-page-logo/comcol-page-logo.component.ts rename to src/app/shared/comcol/comcol-page-logo/comcol-page-logo.component.ts index f3c2d4e80d..a9761bfd5f 100644 --- a/src/app/shared/comcol-page-logo/comcol-page-logo.component.ts +++ b/src/app/shared/comcol/comcol-page-logo/comcol-page-logo.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; -import { Bitstream } from '../../core/shared/bitstream.model'; +import { Bitstream } from '../../../core/shared/bitstream.model'; @Component({ selector: 'ds-comcol-page-logo', diff --git a/src/app/shared/comcol/comcol.module.ts b/src/app/shared/comcol/comcol.module.ts new file mode 100644 index 0000000000..664f14a098 --- /dev/null +++ b/src/app/shared/comcol/comcol.module.ts @@ -0,0 +1,42 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ComcolPageContentComponent } from './comcol-page-content/comcol-page-content.component'; +import { ComcolPageHandleComponent } from './comcol-page-handle/comcol-page-handle.component'; +import { ComcolPageHeaderComponent } from './comcol-page-header/comcol-page-header.component'; +import { ComcolPageLogoComponent } from './comcol-page-logo/comcol-page-logo.component'; +import { ComColFormComponent } from './comcol-forms/comcol-form/comcol-form.component'; +import { CreateComColPageComponent } from './comcol-forms/create-comcol-page/create-comcol-page.component'; +import { EditComColPageComponent } from './comcol-forms/edit-comcol-page/edit-comcol-page.component'; +import { DeleteComColPageComponent } from './comcol-forms/delete-comcol-page/delete-comcol-page.component'; +import { ComcolPageBrowseByComponent } from './comcol-page-browse-by/comcol-page-browse-by.component'; +import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role/comcol-role.component'; +import { SharedModule } from '../shared.module'; +import { FormModule } from '../form/form.module'; + +const COMPONENTS = [ + ComcolPageContentComponent, + ComcolPageHandleComponent, + ComcolPageHeaderComponent, + ComcolPageLogoComponent, + ComColFormComponent, + CreateComColPageComponent, + EditComColPageComponent, + DeleteComColPageComponent, + ComcolPageBrowseByComponent, + ComcolRoleComponent, +]; + +@NgModule({ + declarations: [ + ...COMPONENTS + ], + imports: [ + CommonModule, + FormModule, + SharedModule + ], + exports: [ + ...COMPONENTS + ] +}) +export class ComcolModule { } diff --git a/src/app/shared/form/form.module.ts b/src/app/shared/form/form.module.ts new file mode 100644 index 0000000000..62ab5bd647 --- /dev/null +++ b/src/app/shared/form/form.module.ts @@ -0,0 +1,82 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormComponent } from './form.component'; +import { DsDynamicFormComponent } from './builder/ds-dynamic-form-ui/ds-dynamic-form.component'; +import { + DsDynamicFormControlContainerComponent, + dsDynamicFormControlMapFn +} from './builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component'; +import { DsDynamicListComponent } from './builder/ds-dynamic-form-ui/models/list/dynamic-list.component'; +import { DsDynamicLookupComponent } from './builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component'; +import { DsDynamicDisabledComponent } from './builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component'; +import { DsDynamicLookupRelationModalComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component'; +import { DsDynamicScrollableDropdownComponent } from './builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component'; +import { DsDynamicTagComponent } from './builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component'; +import { DsDynamicOneboxComponent } from './builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component'; +import { DsDynamicRelationGroupComponent } from './builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components'; +import { DsDatePickerComponent } from './builder/ds-dynamic-form-ui/models/date-picker/date-picker.component'; +import { DsDynamicFormGroupComponent } from './builder/ds-dynamic-form-ui/models/form-group/dynamic-form-group.component'; +import { DsDynamicFormArrayComponent } from './builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component'; +import { DsDatePickerInlineComponent } from './builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component'; +import { DsDynamicLookupRelationSearchTabComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component'; +import { DsDynamicLookupRelationSelectionTabComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component'; +import { DsDynamicLookupRelationExternalSourceTabComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component'; +import { SharedModule } from '../shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { SearchModule } from '../search/search.module'; +import { DYNAMIC_FORM_CONTROL_MAP_FN, DynamicFormsCoreModule } from '@ng-dynamic-forms/core'; +import { ExistingMetadataListElementComponent } from './builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component'; +import { ExistingRelationListElementComponent } from './builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component'; +import { ExternalSourceEntryImportModalComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component'; +import { CustomSwitchComponent } from './builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component'; +import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; + +const COMPONENTS = [ + CustomSwitchComponent, + DsDynamicFormComponent, + DsDynamicFormControlContainerComponent, + DsDynamicListComponent, + DsDynamicLookupComponent, + DsDynamicLookupRelationSearchTabComponent, + DsDynamicLookupRelationSelectionTabComponent, + DsDynamicLookupRelationExternalSourceTabComponent, + DsDynamicDisabledComponent, + DsDynamicLookupRelationModalComponent, + DsDynamicScrollableDropdownComponent, + DsDynamicTagComponent, + DsDynamicOneboxComponent, + DsDynamicRelationGroupComponent, + DsDatePickerComponent, + DsDynamicFormGroupComponent, + DsDynamicFormArrayComponent, + DsDatePickerInlineComponent, + ExistingMetadataListElementComponent, + ExistingRelationListElementComponent, + ExternalSourceEntryImportModalComponent, + FormComponent +]; + +@NgModule({ + declarations: [ + ...COMPONENTS + ], + imports: [ + CommonModule, + DynamicFormsCoreModule, + DynamicFormsNGBootstrapUIModule, + SearchModule, + SharedModule, + TranslateModule + ], + exports: [ + ...COMPONENTS + ], + providers: [ + { + provide: DYNAMIC_FORM_CONTROL_MAP_FN, + useValue: dsDynamicFormControlMapFn + } + ] +}) +export class FormModule { +} diff --git a/src/app/shared/resource-policies/resource-policies.module.ts b/src/app/shared/resource-policies/resource-policies.module.ts new file mode 100644 index 0000000000..6eeaddce36 --- /dev/null +++ b/src/app/shared/resource-policies/resource-policies.module.ts @@ -0,0 +1,52 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { TranslateModule } from '@ngx-translate/core'; + +import { ResourcePoliciesComponent } from './resource-policies.component'; +import { ResourcePolicyFormComponent } from './form/resource-policy-form.component'; +import { ResourcePolicyEditComponent } from './edit/resource-policy-edit.component'; +import { ResourcePolicyCreateComponent } from './create/resource-policy-create.component'; +import { FormModule } from '../form/form.module'; +import { ResourcePolicyResolver } from './resolvers/resource-policy.resolver'; +import { ResourcePolicyTargetResolver } from './resolvers/resource-policy-target.resolver'; +import { EpersonGroupListComponent } from './form/eperson-group-list/eperson-group-list.component'; +import { GroupSearchBoxComponent } from './form/eperson-group-list/group-search-box/group-search-box.component'; +import { EpersonSearchBoxComponent } from './form/eperson-group-list/eperson-search-box/eperson-search-box.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { SharedModule } from '../shared.module'; + +const COMPONENTS = [ + ResourcePoliciesComponent, + ResourcePolicyFormComponent, + ResourcePolicyEditComponent, + ResourcePolicyCreateComponent, + EpersonGroupListComponent, + EpersonSearchBoxComponent, + GroupSearchBoxComponent +]; + +const PROVIDERS = [ + ResourcePolicyResolver, + ResourcePolicyTargetResolver +]; + +@NgModule({ + declarations: [ + ...COMPONENTS + ], + imports: [ + NgbModule, + CommonModule, + FormModule, + TranslateModule, + SharedModule + ], + providers: [ + ...PROVIDERS + ], + exports: [ + ...COMPONENTS + ] +}) +export class ResourcePoliciesModule { } diff --git a/src/app/shared/search/search.module.ts b/src/app/shared/search/search.module.ts new file mode 100644 index 0000000000..d5816d2a1b --- /dev/null +++ b/src/app/shared/search/search.module.ts @@ -0,0 +1,103 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SearchSwitchConfigurationComponent } from './search-switch-configuration/search-switch-configuration.component'; +import { SearchFiltersComponent } from './search-filters/search-filters.component'; +import { SearchFilterComponent } from './search-filters/search-filter/search-filter.component'; +import { SearchFacetFilterComponent } from './search-filters/search-filter/search-facet-filter/search-facet-filter.component'; +import { SearchLabelsComponent } from './search-labels/search-labels.component'; +import { SearchLabelComponent } from './search-labels/search-label/search-label.component'; +import { SearchFacetFilterWrapperComponent } from './search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component'; +import { SearchRangeFilterComponent } from './search-filters/search-filter/search-range-filter/search-range-filter.component'; +import { SearchTextFilterComponent } from './search-filters/search-filter/search-text-filter/search-text-filter.component'; +import { SearchHierarchyFilterComponent } from './search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component'; +import { SearchBooleanFilterComponent } from './search-filters/search-filter/search-boolean-filter/search-boolean-filter.component'; +import { SearchFacetOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component'; +import { SearchFacetSelectedOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component'; +import { SearchFacetRangeOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component'; +import { SearchAuthorityFilterComponent } from './search-filters/search-filter/search-authority-filter/search-authority-filter.component'; +import { SearchSidebarComponent } from './search-sidebar/search-sidebar.component'; +import { SearchSettingsComponent } from './search-settings/search-settings.component'; +import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component'; +import { SearchObjects } from './search-objects.model'; +import { FacetConfigResponse } from './facet-config-response.model'; +import { FacetValues } from './facet-values.model'; +import { SearchResult } from './search-result.model'; +import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core'; +import { MissingTranslationHelper } from '../translate/missing-translation.helper'; +import { SharedModule } from '../shared.module'; +import { SearchResultsComponent } from './search-results/search-results.component'; + +const COMPONENTS = [ + SearchResultsComponent, + SearchSidebarComponent, + SearchSettingsComponent, + SearchFiltersComponent, + SearchFilterComponent, + SearchFacetFilterComponent, + SearchLabelsComponent, + SearchLabelComponent, + SearchFacetFilterWrapperComponent, + SearchRangeFilterComponent, + SearchTextFilterComponent, + SearchHierarchyFilterComponent, + SearchBooleanFilterComponent, + SearchFacetOptionComponent, + SearchFacetSelectedOptionComponent, + SearchFacetRangeOptionComponent, + SearchAuthorityFilterComponent, + SearchSwitchConfigurationComponent, + ConfigurationSearchPageComponent, + ThemedConfigurationSearchPageComponent +]; + +const ENTRY_COMPONENTS = [ + SearchFacetFilterComponent, + SearchRangeFilterComponent, + SearchTextFilterComponent, + SearchHierarchyFilterComponent, + SearchBooleanFilterComponent, + SearchFacetOptionComponent, + SearchFacetSelectedOptionComponent, + SearchFacetRangeOptionComponent, + SearchAuthorityFilterComponent, +]; + +/** + * Declaration needed to make sure all decorator functions are called in time + */ +export const MODELS = [ + SearchObjects, + FacetConfigResponse, + FacetValues, + SearchResult +]; + +@NgModule({ + declarations: [ + ...COMPONENTS + ], + imports: [ + CommonModule, + TranslateModule.forChild({ + missingTranslationHandler: { provide: MissingTranslationHandler, useClass: MissingTranslationHelper }, + useDefaultLang: true + }), + SharedModule.withEntryComponents() + ], + exports: [ + ...COMPONENTS + ] +}) +export class SearchModule { + /** + * NOTE: this method allows to resolve issue with components that using a custom decorator + * which are not loaded during SSR otherwise + */ + static withEntryComponents() { + return { + ngModule: SearchModule, + providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })) + }; + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 2b64260f46..d88445a930 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -6,17 +6,21 @@ import { CdkTreeModule } from '@angular/cdk/tree'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { NouisliderModule } from 'ng2-nouislider'; -import { NgbDatepickerModule, NgbModule, NgbTimepickerModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; +import { + NgbDatepickerModule, + NgbDropdownModule, + NgbNavModule, + NgbPaginationModule, + NgbTimepickerModule, + NgbTooltipModule, + NgbTypeaheadModule +} from '@ng-bootstrap/ng-bootstrap'; import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core'; import { NgxPaginationModule } from 'ngx-pagination'; import { FileUploadModule } from 'ng2-file-upload'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; -import { DYNAMIC_FORM_CONTROL_MAP_FN, DynamicFormsCoreModule } from '@ng-dynamic-forms/core'; -import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; import { TextMaskModule } from 'angular2-text-mask'; import { MomentModule } from 'ngx-moment'; - -import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role/comcol-role.component'; import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.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'; @@ -35,10 +39,6 @@ import { CommunityGridElementComponent } from './object-grid/community-grid-elem 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 { ComcolPageContentComponent } from './comcol-page-content/comcol-page-content.component'; -import { ComcolPageHandleComponent } from './comcol-page-handle/comcol-page-handle.component'; -import { ComcolPageHeaderComponent } from './comcol-page-header/comcol-page-header.component'; -import { ComcolPageLogoComponent } from './comcol-page-logo/comcol-page-logo.component'; import { ErrorComponent } from './error/error.component'; import { LoadingComponent } from './loading/loading.component'; import { PaginationComponent } from './pagination/pagination.component'; @@ -49,14 +49,6 @@ import { ViewModeSwitchComponent } from './view-mode-switch/view-mode-switch.com import { VarDirective } from './utils/var.directive'; import { AuthNavMenuComponent } from './auth-nav-menu/auth-nav-menu.component'; import { LogOutComponent } from './log-out/log-out.component'; -import { FormComponent } from './form/form.component'; -import { DsDynamicOneboxComponent } from './form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component'; -import { DsDynamicScrollableDropdownComponent } from './form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component'; -import { - DsDynamicFormControlContainerComponent, - dsDynamicFormControlMapFn, -} from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component'; -import { DsDynamicFormComponent } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form.component'; import { DragClickDirective } from './utils/drag-click.directive'; import { TruncatePipe } from './utils/truncate.pipe'; import { TruncatableComponent } from './truncatable/truncatable.component'; @@ -64,15 +56,7 @@ import { TruncatableService } from './truncatable/truncatable.service'; import { TruncatablePartComponent } from './truncatable/truncatable-part/truncatable-part.component'; import { UploaderComponent } from './uploader/uploader.component'; import { ChipsComponent } from './chips/chips.component'; -import { DsDynamicTagComponent } from './form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component'; -import { DsDynamicListComponent } from './form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component'; -import { DsDynamicFormGroupComponent } from './form/builder/ds-dynamic-form-ui/models/form-group/dynamic-form-group.component'; -import { DsDynamicFormArrayComponent } from './form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component'; -import { DsDynamicRelationGroupComponent } from './form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components'; -import { DsDatePickerInlineComponent } from './form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component'; import { NumberPickerComponent } from './number-picker/number-picker.component'; -import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component'; -import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.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'; @@ -101,14 +85,9 @@ 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 { ComColFormComponent } from './comcol-forms/comcol-form/comcol-form.component'; -import { CreateComColPageComponent } from './comcol-forms/create-comcol-page/create-comcol-page.component'; -import { EditComColPageComponent } from './comcol-forms/edit-comcol-page/edit-comcol-page.component'; -import { DeleteComColPageComponent } from './comcol-forms/delete-comcol-page/delete-comcol-page.component'; import { ObjectValuesPipe } from './utils/object-values-pipe'; import { InListValidator } from './utils/in-list-validator.directive'; import { AutoFocusDirective } from './utils/auto-focus.directive'; -import { ComcolPageBrowseByComponent } from './comcol-page-browse-by/comcol-page-browse-by.component'; 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'; @@ -127,32 +106,11 @@ 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 { DsDynamicLookupRelationModalComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component'; -import { SearchResultsComponent } from './search/search-results/search-results.component'; -import { SearchSidebarComponent } from './search/search-sidebar/search-sidebar.component'; -import { SearchSettingsComponent } from './search/search-settings/search-settings.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 { SearchFiltersComponent } from './search/search-filters/search-filters.component'; -import { SearchFilterComponent } from './search/search-filters/search-filter/search-filter.component'; -import { SearchFacetFilterComponent } from './search/search-filters/search-filter/search-facet-filter/search-facet-filter.component'; -import { SearchLabelsComponent } from './search/search-labels/search-labels.component'; -import { SearchFacetFilterWrapperComponent } from './search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component'; -import { SearchRangeFilterComponent } from './search/search-filters/search-filter/search-range-filter/search-range-filter.component'; -import { SearchTextFilterComponent } from './search/search-filters/search-filter/search-text-filter/search-text-filter.component'; -import { SearchHierarchyFilterComponent } from './search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component'; -import { SearchBooleanFilterComponent } from './search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component'; -import { SearchFacetOptionComponent } from './search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component'; -import { SearchFacetSelectedOptionComponent } from './search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component'; -import { SearchFacetRangeOptionComponent } from './search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component'; -import { SearchSwitchConfigurationComponent } from './search/search-switch-configuration/search-switch-configuration.component'; -import { SearchAuthorityFilterComponent } from './search/search-filters/search-filter/search-authority-filter/search-authority-filter.component'; -import { DsDynamicDisabledComponent } from './form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component'; -import { DsDynamicLookupRelationSearchTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component'; -import { DsDynamicLookupRelationSelectionTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component'; import { PageSizeSelectorComponent } from './page-size-selector/page-size-selector.component'; import { AbstractTrackableComponent } from './trackable/abstract-trackable.component'; -import { ComcolMetadataComponent } from './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'; @@ -164,48 +122,34 @@ import { MetadataRepresentationDirective } from './metadata-representation/metad 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 { SearchLabelComponent } from './search/search-labels/search-label/search-label.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 { DsDynamicLookupRelationExternalSourceTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component'; -import { ExternalSourceEntryImportModalComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component'; import { ImportableListItemControlComponent } from './object-collection/shared/importable-list-item-control/importable-list-item-control.component'; -import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.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'; import { LogInShibbolethComponent } from './log-in/methods/shibboleth/log-in-shibboleth.component'; import { LogInPasswordComponent } from './log-in/methods/password/log-in-password.component'; import { LogInComponent } from './log-in/log-in.component'; -import { CustomSwitchComponent } from './form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component'; import { BundleListElementComponent } from './object-list/bundle-list-element/bundle-list-element.component'; import { MissingTranslationHelper } from './translate/missing-translation.helper'; import { ItemVersionsNoticeComponent } from './item/item-versions/notice/item-versions-notice.component'; 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 { ExistingRelationListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.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 { ImpersonateNavbarComponent } from './impersonate-navbar/impersonate-navbar.component'; -import { ResourcePoliciesComponent } from './resource-policies/resource-policies.component'; import { NgForTrackByIdDirective } from './ng-for-track-by-id.directive'; -import { ResourcePolicyFormComponent } from './resource-policies/form/resource-policy-form.component'; -import { EpersonGroupListComponent } from './resource-policies/form/eperson-group-list/eperson-group-list.component'; -import { ResourcePolicyTargetResolver } from './resource-policies/resolvers/resource-policy-target.resolver'; -import { ResourcePolicyResolver } from './resource-policies/resolvers/resource-policy.resolver'; -import { EpersonSearchBoxComponent } from './resource-policies/form/eperson-group-list/eperson-search-box/eperson-search-box.component'; -import { GroupSearchBoxComponent } from './resource-policies/form/eperson-group-list/group-search-box/group-search-box.component'; import { FileDownloadLinkComponent } from './file-download-link/file-download-link.component'; import { CollectionDropdownComponent } from './collection-dropdown/collection-dropdown.component'; import { EntityDropdownComponent } from './entity-dropdown/entity-dropdown.component'; -import { DsSelectComponent } from './ds-select/ds-select.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'; @@ -219,53 +163,35 @@ import { HoverClassDirective } from './hover-class.directive'; import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component'; import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component'; import { ItemSearchResultGridElementComponent } from './object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component'; -import { ResourcePolicyEditComponent } from './resource-policies/edit/resource-policy-edit.component'; -import { ResourcePolicyCreateComponent } from './resource-policies/create/resource-policy-create.component'; -import { SearchObjects } from './search/search-objects.model'; -import { SearchResult } from './search/search-result.model'; -import { FacetConfigResponse } from './search/facet-config-response.model'; -import { FacetValues } from './search/facet-values.model'; 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 { RelatedItemsComponent } from '../item-page/simple/related-items/related-items-component'; -import { TabbedRelatedEntitiesSearchComponent } from '../item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component'; -import { RelatedEntitiesSearchComponent } from '../item-page/simple/related-entities/related-entities-search/related-entities-search.component'; -import { ConfigurationSearchPageComponent } from '../search-page/configuration-search-page.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 { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.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 { 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'; - -/** - * Declaration needed to make sure all decorator functions are called in time - */ -export const MODELS = [ - SearchObjects, - FacetConfigResponse, - FacetValues, - SearchResult -]; +import { DsSelectComponent } from './ds-select/ds-select.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here CommonModule, SortablejsModule, - DynamicFormsCoreModule, - DynamicFormsNGBootstrapUIModule, FileUploadModule, FormsModule, InfiniteScrollModule, - NgbModule, + NgbNavModule, NgbDatepickerModule, NgbTimepickerModule, NgbTypeaheadModule, NgxPaginationModule, + NgbPaginationModule, + NgbDropdownModule, + NgbTooltipModule, ReactiveFormsModule, RouterModule, NouisliderModule, @@ -302,34 +228,9 @@ const COMPONENTS = [ AuthNavMenuComponent, UserMenuComponent, ChipsComponent, - ComcolPageContentComponent, - ComcolPageHandleComponent, - ComcolPageHeaderComponent, - ComcolPageLogoComponent, - ComColFormComponent, - CreateComColPageComponent, - EditComColPageComponent, - DeleteComColPageComponent, - ComcolPageBrowseByComponent, - ComcolRoleComponent, - DsDynamicFormComponent, - DsDynamicFormControlContainerComponent, - DsDynamicListComponent, - DsDynamicLookupComponent, - DsDynamicDisabledComponent, - DsDynamicLookupRelationModalComponent, - DsDynamicScrollableDropdownComponent, - DsDynamicTagComponent, - DsDynamicOneboxComponent, - DsDynamicRelationGroupComponent, - DsDatePickerComponent, - DsDynamicFormGroupComponent, - DsDynamicFormArrayComponent, - DsDatePickerInlineComponent, DsSelectComponent, ErrorComponent, FileSectionComponent, - FormComponent, LangSwitchComponent, LoadingComponent, LogInComponent, @@ -382,26 +283,9 @@ const COMPONENTS = [ CommunitySearchResultListElementComponent, CollectionSearchResultListElementComponent, BrowseByComponent, - SearchResultsComponent, - SearchSidebarComponent, - SearchSettingsComponent, + CollectionSearchResultGridElementComponent, CommunitySearchResultGridElementComponent, - SearchFiltersComponent, - SearchFilterComponent, - SearchFacetFilterComponent, - SearchLabelsComponent, - SearchLabelComponent, - SearchFacetFilterWrapperComponent, - SearchRangeFilterComponent, - SearchTextFilterComponent, - SearchHierarchyFilterComponent, - SearchBooleanFilterComponent, - SearchFacetOptionComponent, - SearchFacetSelectedOptionComponent, - SearchFacetRangeOptionComponent, - SearchSwitchConfigurationComponent, - SearchAuthorityFilterComponent, PageSizeSelectorComponent, ListableObjectComponentLoaderComponent, CollectionListElementComponent, @@ -414,15 +298,14 @@ const COMPONENTS = [ TypeBadgeComponent, BrowseByComponent, AbstractTrackableComponent, - CustomSwitchComponent, + ItemSelectComponent, CollectionSelectComponent, MetadataRepresentationLoaderComponent, SelectableListItemControlComponent, - ExternalSourceEntryImportModalComponent, + ImportableListItemControlComponent, - ExistingMetadataListElementComponent, - ExistingRelationListElementComponent, + LogInShibbolethComponent, LogInPasswordComponent, LogInContainerComponent, @@ -431,13 +314,6 @@ const COMPONENTS = [ ItemVersionsNoticeComponent, ModifyItemOverviewComponent, ImpersonateNavbarComponent, - ResourcePoliciesComponent, - ResourcePolicyFormComponent, - ResourcePolicyEditComponent, - ResourcePolicyCreateComponent, - EpersonGroupListComponent, - EpersonSearchBoxComponent, - GroupSearchBoxComponent, FileDownloadLinkComponent, BitstreamDownloadPageComponent, BitstreamRequestACopyPageComponent, @@ -458,10 +334,7 @@ const COMPONENTS = [ PlainTextMetadataListElementComponent, ItemMetadataListElementComponent, MetadataRepresentationListElementComponent, - DsDynamicLookupRelationSearchTabComponent, - DsDynamicLookupRelationSelectionTabComponent, ItemMetadataRepresentationListElementComponent, - DsDynamicLookupRelationExternalSourceTabComponent, BundleListElementComponent, StartsWithDateComponent, StartsWithTextComponent, @@ -502,18 +375,7 @@ const ENTRY_COMPONENTS = [ PlainTextMetadataListElementComponent, ItemMetadataListElementComponent, MetadataRepresentationListElementComponent, - CustomSwitchComponent, ItemMetadataRepresentationListElementComponent, - SearchResultsComponent, - SearchFacetFilterComponent, - SearchRangeFilterComponent, - SearchTextFilterComponent, - SearchHierarchyFilterComponent, - SearchBooleanFilterComponent, - SearchFacetOptionComponent, - SearchFacetSelectedOptionComponent, - SearchFacetRangeOptionComponent, - SearchAuthorityFilterComponent, LogInPasswordComponent, LogInShibbolethComponent, BundleListElementComponent, @@ -539,11 +401,6 @@ const ENTRY_COMPONENTS = [ ScopeSelectorModalComponent, ]; -const SHARED_SEARCH_PAGE_COMPONENTS = [ - ConfigurationSearchPageComponent, - ThemedConfigurationSearchPageComponent -]; - const SHARED_ITEM_PAGE_COMPONENTS = [ MetadataFieldWrapperComponent, MetadataValuesComponent, @@ -553,20 +410,13 @@ const SHARED_ITEM_PAGE_COMPONENTS = [ GenericItemPageFieldComponent, MetadataRepresentationListComponent, RelatedItemsComponent, - RelatedEntitiesSearchComponent, - TabbedRelatedEntitiesSearchComponent + ]; const PROVIDERS = [ TruncatableService, MockAdminGuard, - AbstractTrackableComponent, - { - provide: DYNAMIC_FORM_CONTROL_MAP_FN, - useValue: dsDynamicFormControlMapFn - }, - ResourcePolicyResolver, - ResourcePolicyTargetResolver + AbstractTrackableComponent ]; const DIRECTIVES = [ @@ -599,7 +449,6 @@ const DIRECTIVES = [ ...COMPONENTS, ...DIRECTIVES, ...SHARED_ITEM_PAGE_COMPONENTS, - ...SHARED_SEARCH_PAGE_COMPONENTS, ItemVersionsSummaryModalComponent, ItemVersionsDeleteModalComponent, ], @@ -611,7 +460,6 @@ const DIRECTIVES = [ ...PIPES, ...COMPONENTS, ...SHARED_ITEM_PAGE_COMPONENTS, - ...SHARED_SEARCH_PAGE_COMPONENTS, ...DIRECTIVES, TranslateModule ] @@ -628,7 +476,7 @@ export class SharedModule { static withEntryComponents() { return { ngModule: SharedModule, - providers: ENTRY_COMPONENTS.map((component) => ({provide: component})) + providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })) }; } } diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index e8c96284c0..5f14b5c246 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -37,6 +37,8 @@ import { ResearchEntitiesModule } from '../entity-groups/research-entities/resea import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component'; import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component'; import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component'; +import { FormModule } from '../shared/form/form.module'; +import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; const DECLARATIONS = [ SubmissionSectionUploadAccessConditionsComponent, @@ -73,15 +75,17 @@ const ENTRY_COMPONENTS = [ ]; @NgModule({ - imports: [ - CommonModule, - CoreModule.forRoot(), - SharedModule, - StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig), - EffectsModule.forFeature(submissionEffects), - JournalEntitiesModule.withEntryComponents(), - ResearchEntitiesModule.withEntryComponents(), - ], + imports: [ + CommonModule, + CoreModule.forRoot(), + SharedModule, + StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig), + EffectsModule.forFeature(submissionEffects), + JournalEntitiesModule.withEntryComponents(), + ResearchEntitiesModule.withEntryComponents(), + FormModule, + NgbAccordionModule + ], declarations: DECLARATIONS, exports: DECLARATIONS, providers: [ diff --git a/src/themes/custom/theme.module.ts b/src/themes/custom/theme.module.ts index ba5f660012..a931da5a2a 100644 --- a/src/themes/custom/theme.module.ts +++ b/src/themes/custom/theme.module.ts @@ -79,7 +79,10 @@ 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 { FileSectionComponent} from './app/item-page/simple/field-components/file-section/file-section.component'; +import { FileSectionComponent } from './app/item-page/simple/field-components/file-section/file-section.component'; +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'; const DECLARATIONS = [ FileSectionComponent, @@ -168,6 +171,10 @@ const DECLARATIONS = [ SubmissionModule, MyDSpacePageModule, MyDspaceSearchModule, + SearchModule, + FormsModule, + ResourcePoliciesModule, + ComcolModule ], declarations: DECLARATIONS }) diff --git a/src/themes/dspace/theme.module.ts b/src/themes/dspace/theme.module.ts index b03d5af5b4..2a774eb9c8 100644 --- a/src/themes/dspace/theme.module.ts +++ b/src/themes/dspace/theme.module.ts @@ -43,6 +43,9 @@ import { MyDSpacePageModule } from '../../app/my-dspace-page/my-dspace-page.modu import { NavbarComponent } from './app/navbar/navbar.component'; import { HeaderComponent } from './app/header/header.component'; import { HeaderNavbarWrapperComponent } from './app/header-nav-wrapper/header-navbar-wrapper.component'; +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'; const DECLARATIONS = [ HomeNewsComponent, @@ -94,6 +97,10 @@ const DECLARATIONS = [ SubmissionModule, MyDSpacePageModule, MyDspaceSearchModule, + SearchModule, + FormsModule, + ResourcePoliciesModule, + ComcolModule ], declarations: DECLARATIONS }) From 21fcc9dde8c4b9a94f0d186053b6dd95bbfcc10b Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 16 Dec 2021 00:25:59 -0600 Subject: [PATCH 040/102] prefix dspace environment variables --- README.md | 29 ++++++++++++++++++----- docs/Configuration.md | 19 +++++---------- src/config/config.server.ts | 47 +++++++++++++++++++++++-------------- 3 files changed, 58 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index d21d54ac33..b4ae6c884b 100644 --- a/README.md +++ b/README.md @@ -120,11 +120,6 @@ DSPACE_HOST # The host name of the angular application DSPACE_PORT # The port number of the angular application DSPACE_NAMESPACE # The namespace of the angular application DSPACE_SSL # Whether the angular application uses SSL [true/false] - -DSPACE_REST_HOST # The host name of the REST application -DSPACE_REST_PORT # The port number of the REST application -DSPACE_REST_NAMESPACE # The namespace of the REST application -DSPACE_REST_SSL # Whether the angular REST uses SSL [true/false] ``` All other settings can be set using the following convention for naming the environment variables: @@ -134,14 +129,36 @@ All other settings can be set using the following convention for naming the envi e.g. -``` +```bash +# The host name of the REST application +dspace.rest.host => DSPACE_REST_HOST + +# The port number of the REST application +dspace.rest.port => DSPACE_REST_PORT + +# The namespace of the REST application +dspace.rest.nameSpace => DSPACE_REST_NAMESPACE + +# Whether the angular REST uses SSL [true/false] +dspace.rest.ssl => DSPACE_REST_SSL + cache.msToLive.default => CACHE_MSTOLIVE_DEFAULT auth.ui.timeUntilIdle => AUTH_UI_TIMEUNTILIDLE ``` +The equavelant to the non-conventional legacy settings: + +```bash +DSPACE_UI_HOST => DSPACE_HOST +DSPACE_UI_PORT => DSPACE_PORT +DSPACE_UI_NAMESPACE => DSPACE_NAMESPACE +DSPACE_UI_SSL => DSPACE_SSL +``` + The same settings can also be overwritten by setting system environment variables instead, E.g.: ```bash export DSPACE_HOST=api7.dspace.org +export DSPACE_UI_PORT=4200 ``` The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`** diff --git a/docs/Configuration.md b/docs/Configuration.md index af0efc1f02..193ca0f57d 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -5,11 +5,11 @@ Default configuration file is located at `config/config.yml`. All configuration - Create a new `config.(dev or development).yml` file in `config/` for `development` environment; - Create a new `config.(prod or production).yml` file in `config/` for `production` environment; -Alternatively, create a desired app config file at an external location and set the path as environment variable `APP_CONFIG_PATH`. +Alternatively, create a desired app config file at an external location and set the path as environment variable `DSPACE_APP_CONFIG_PATH`. e.g. ``` -APP_CONFIG_PATH=/usr/local/dspace/config/config.yml +DSPACE_APP_CONFIG_PATH=/usr/local/dspace/config/config.yml ``` Configuration options can be overridden by setting environment variables. @@ -36,10 +36,10 @@ Alternately you can set the following environment variables. If any of these are ``` or ``` - UI_SSL=true - UI_HOST=localhost - UI_PORT=4000 - UI_NAMESPACE=/ + DSPACE_UI_SSL=true + DSPACE_UI_HOST=localhost + DSPACE_UI_PORT=4000 + DSPACE_UI_NAMESPACE=/ ``` ## DSpace's REST endpoint @@ -61,13 +61,6 @@ Alternately you can set the following environment variables. If any of these are DSPACE_REST_PORT=443 DSPACE_REST_NAMESPACE=/server ``` -or -``` - REST_SSL=true - REST_HOST=api7.dspace.org - REST_PORT=443 - REST_NAMESPACE=/server -``` ## Environment variable naming convention diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 25a04c20a7..36d7d1f9d8 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -14,6 +14,14 @@ const CONFIG_PATH = join(process.cwd(), 'config'); type Environment = 'production' | 'development' | 'test'; +const DSPACE = (key: string): string => { + return `DSPACE_${key}`; +}; + +const ENV = (key: string, prefix = false): any => { + return prefix ? process.env[DSPACE(key)] : process.env[key]; +}; + const getBooleanFromString = (variable: string): boolean => { return variable === 'true' || variable === '1'; }; @@ -24,8 +32,8 @@ const getNumberFromString = (variable: string): number => { const getEnvironment = (): Environment => { let environment: Environment = 'development'; - if (isNotEmpty(process.env.NODE_ENV)) { - switch (process.env.NODE_ENV) { + if (isNotEmpty(ENV('NODE_ENV'))) { + switch (ENV('NODE_ENV')) { case 'prod': case 'production': environment = 'production'; @@ -37,7 +45,7 @@ const getEnvironment = (): Environment => { case 'development': break; default: - console.warn(`Unknown NODE_ENV ${process.env.NODE_ENV}. Defaulting to development`); + console.warn(`Unknown NODE_ENV ${ENV('NODE_ENV')}. Defaulting to development`); } } @@ -102,19 +110,20 @@ const overrideWithEnvironment = (config: Config, key: string = '') => { if (typeof innerConfig === 'object') { overrideWithEnvironment(innerConfig, variable); } else { - if (isNotEmpty(process.env[variable])) { - console.log(`Applying environment variable ${variable} with value ${process.env[variable]}`); + const value = ENV(variable, true); + if (isNotEmpty(value)) { + console.log(`Applying environment variable ${DSPACE(variable)} with value ${value}`); switch (typeof innerConfig) { case 'number': - config[property] = getNumberFromString(process.env[variable]); + config[property] = getNumberFromString(value); break; case 'boolean': - config[property] = getBooleanFromString(process.env[variable]); + config[property] = getBooleanFromString(value); break; case 'string': - config[property] = process.env[variable]; + config[property] = value; default: - console.warn(`Unsupported environment variable type ${typeof innerConfig} ${variable}`); + console.warn(`Unsupported environment variable type ${typeof innerConfig} ${DSPACE(variable)}`); } } } @@ -122,6 +131,8 @@ const overrideWithEnvironment = (config: Config, key: string = '') => { } }; + + const buildBaseUrl = (config: ServerConfig): void => { config.baseUrl = [ config.ssl ? 'https://' : 'http://', @@ -168,7 +179,7 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => { } // override with external config if specified by environment variable `APP_CONFIG_PATH` - const externalConfigPath = process.env.APP_CONFIG_PATH; + const externalConfigPath = ENV('APP_CONFIG_PATH', true); if (isNotEmpty(externalConfigPath)) { if (fs.existsSync(externalConfigPath)) { overrideWithConfig(appConfig, externalConfigPath); @@ -181,16 +192,16 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => { overrideWithEnvironment(appConfig); // apply existing non convention UI environment variables - appConfig.ui.host = isNotEmpty(process.env.DSPACE_HOST) ? process.env.DSPACE_HOST : appConfig.ui.host; - appConfig.ui.port = isNotEmpty(process.env.DSPACE_PORT) ? getNumberFromString(process.env.DSPACE_PORT) : appConfig.ui.port; - appConfig.ui.nameSpace = isNotEmpty(process.env.DSPACE_NAMESPACE) ? process.env.DSPACE_NAMESPACE : appConfig.ui.nameSpace; - appConfig.ui.ssl = isNotEmpty(process.env.DSPACE_SSL) ? getBooleanFromString(process.env.DSPACE_SSL) : appConfig.ui.ssl; + appConfig.ui.host = isNotEmpty(ENV('HOST', true)) ? ENV('HOST', true) : appConfig.ui.host; + appConfig.ui.port = isNotEmpty(ENV('PORT', true)) ? getNumberFromString(ENV('PORT', true)) : appConfig.ui.port; + appConfig.ui.nameSpace = isNotEmpty(ENV('NAMESPACE', true)) ? ENV('NAMESPACE', true) : appConfig.ui.nameSpace; + appConfig.ui.ssl = isNotEmpty(ENV('SSL', true)) ? getBooleanFromString(ENV('SSL', true)) : appConfig.ui.ssl; // apply existing non convention REST environment variables - appConfig.rest.host = isNotEmpty(process.env.DSPACE_REST_HOST) ? process.env.DSPACE_REST_HOST : appConfig.rest.host; - appConfig.rest.port = isNotEmpty(process.env.DSPACE_REST_PORT) ? getNumberFromString(process.env.DSPACE_REST_PORT) : appConfig.rest.port; - appConfig.rest.nameSpace = isNotEmpty(process.env.DSPACE_REST_NAMESPACE) ? process.env.DSPACE_REST_NAMESPACE : appConfig.rest.nameSpace; - appConfig.rest.ssl = isNotEmpty(process.env.DSPACE_REST_SSL) ? getBooleanFromString(process.env.DSPACE_REST_SSL) : appConfig.rest.ssl; + appConfig.rest.host = isNotEmpty(ENV('REST_HOST', true)) ? ENV('REST_HOST', true) : appConfig.rest.host; + appConfig.rest.port = isNotEmpty(ENV('REST_PORT', true)) ? getNumberFromString(ENV('REST_PORT', true)) : appConfig.rest.port; + appConfig.rest.nameSpace = isNotEmpty(ENV('REST_NAMESPACE', true)) ? ENV('REST_NAMESPACE', true) : appConfig.rest.nameSpace; + appConfig.rest.ssl = isNotEmpty(ENV('REST_SSL', true)) ? getBooleanFromString(ENV('REST_SSL', true)) : appConfig.rest.ssl; // apply build defined production appConfig.production = env === 'production'; From b6ae15fbd2332aa69af6efad9d2705fdcfb37491 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 16 Dec 2021 10:00:08 +0100 Subject: [PATCH 041/102] [CST-4633] move search.component to search module and create search models folder --- .../collection-item-mapper.component.spec.ts | 2 +- .../collection-item-mapper.component.ts | 11 ++--- .../collection-page.component.ts | 9 +--- src/app/core/data/bundle-data.service.ts | 2 +- src/app/core/data/external-source.service.ts | 2 +- .../facet-config-response-parsing.service.ts | 4 +- .../facet-value-response-parsing.service.ts | 4 +- src/app/core/data/item-data.service.ts | 2 +- .../core/data/lookup-relation.service.spec.ts | 4 +- src/app/core/data/lookup-relation.service.ts | 4 +- .../data/mydspace-response-parsing.service.ts | 2 +- .../data/search-response-parsing.service.ts | 2 +- .../core/data/version-history-data.service.ts | 2 +- src/app/core/shared/operators.ts | 2 +- .../search-configuration.service.spec.ts | 4 +- .../search/search-configuration.service.ts | 13 +++--- .../search/search-filter.service.spec.ts | 4 +- .../shared/search/search-filter.service.ts | 2 +- .../core/shared/search/search.service.spec.ts | 7 +--- src/app/core/shared/search/search.service.ts | 15 +++---- .../item-bitstreams.component.ts | 7 +--- ...-drag-and-drop-bitstream-list.component.ts | 2 +- .../item-collection-mapper.component.spec.ts | 4 +- .../item-collection-mapper.component.ts | 9 ++-- .../edit-relationship-list.component.ts | 10 ++++- .../my-dspace-configuration.service.spec.ts | 5 +-- .../my-dspace-new-submission.component.ts | 2 +- .../my-dspace-page.component.scss | 2 +- .../my-dspace-page.component.spec.ts | 2 +- .../my-dspace-page.component.ts | 15 ++----- .../my-dspace-results.component.ts | 4 +- ...onfiguration-search-page.component.spec.ts | 2 +- .../configuration-search-page.component.ts | 14 ++----- src/app/search-page/search-page.module.ts | 2 - .../search-page/search-tracker.component.ts | 6 +-- ...uthorized-collection-selector.component.ts | 2 +- .../dso-selector.component.spec.ts | 2 +- .../dso-selector/dso-selector.component.ts | 6 +-- ...ynamic-form-control-container.component.ts | 2 +- ...ic-lookup-relation-modal.component.spec.ts | 4 +- ...dynamic-lookup-relation-modal.component.ts | 2 +- ...tion-external-source-tab.component.spec.ts | 2 +- ...-relation-external-source-tab.component.ts | 2 +- ...nal-source-entry-import-modal.component.ts | 4 +- ...okup-relation-search-tab.component.spec.ts | 4 +- ...ic-lookup-relation-search-tab.component.ts | 2 +- ...p-relation-selection-tab.component.spec.ts | 2 +- ...lookup-relation-selection-tab.component.ts | 2 +- .../item-versions/item-versions.component.ts | 2 +- ...aimed-approved-task-search-result.model.ts | 2 +- ...aimed-declined-task-search-result.model.ts | 2 +- .../claimed-task-search-result.model.ts | 2 +- .../shared/collection-search-result.model.ts | 2 +- .../shared/community-search-result.model.ts | 2 +- .../shared/item-search-result.model.ts | 2 +- .../shared/pool-task-search-result.model.ts | 2 +- .../workflow-item-search-result.model.ts | 2 +- .../workspace-item-search-result.model.ts | 2 +- .../item-detail-preview-field.component.ts | 2 +- .../item-detail-preview.component.ts | 2 +- .../search-result-detail-element.component.ts | 2 +- .../search-result-grid-element.component.ts | 2 +- .../item-list-preview.component.ts | 2 +- .../search-result-list-element.component.ts | 2 +- ...ebar-search-list-element.component.spec.ts | 2 +- .../sidebar-search-list-element.component.ts | 2 +- .../page-size-selector.component.ts | 2 +- .../facet-config-response.model.ts | 10 ++--- .../search/{ => models}/facet-value.model.ts | 4 +- .../search/{ => models}/facet-values.model.ts | 10 ++--- .../search/{ => models}/filter-type.model.ts | 0 .../paginated-search-options.model.spec.ts | 6 +-- .../paginated-search-options.model.ts | 8 ++-- .../search-filter-config.model.ts | 16 +++---- .../{ => models}/search-filter.model.ts | 0 .../{ => models}/search-objects.model.ts | 10 ++--- .../{ => models}/search-options.model.spec.ts | 3 +- .../{ => models}/search-options.model.ts | 8 ++-- .../search-query-response.model.ts | 4 +- .../{ => models}/search-result.model.ts | 18 ++++---- .../facet-config-response.resouce-type.ts | 2 +- .../types}/facet-values.resource-type.ts | 2 +- .../search-filter-config.resource-type.ts | 2 +- .../types}/search-objects.resource-type.ts | 2 +- .../types}/search-result.resource-type.ts | 2 +- .../search-authority-filter.component.ts | 2 +- .../search-boolean-filter.component.ts | 7 +--- .../search-facet-option.component.spec.ts | 8 ++-- .../search-facet-option.component.ts | 4 +- ...earch-facet-range-option.component.spec.ts | 8 ++-- .../search-facet-range-option.component.ts | 4 +- ...ch-facet-selected-option.component.spec.ts | 8 ++-- .../search-facet-selected-option.component.ts | 4 +- .../search-facet-filter-wrapper.component.ts | 4 +- .../search-facet-filter.component.spec.ts | 6 +-- .../search-facet-filter.component.ts | 12 ++++-- .../search-filter-type-decorator.ts | 3 +- .../search-filter/search-filter.actions.ts | 2 +- .../search-filter.component.spec.ts | 4 +- .../search-filter/search-filter.component.ts | 2 +- .../search-hierarchy-filter.component.ts | 7 +--- .../search-range-filter.component.spec.ts | 6 +-- .../search-range-filter.component.ts | 10 +++-- .../search-text-filter.component.ts | 11 ++--- .../search-filters.component.ts | 2 +- .../search-results.component.ts | 4 +- .../search-settings.component.ts | 2 +- .../search-sidebar.component.ts | 2 +- .../search}/search.component.html | 13 +++++- .../search}/search.component.scss | 0 .../search}/search.component.spec.ts | 28 ++++++------- .../search}/search.component.ts | 42 ++++++++++--------- src/app/shared/search/search.module.ts | 10 +++-- src/app/shared/search/search.utils.spec.ts | 4 +- src/app/shared/search/search.utils.ts | 4 +- src/app/statistics/statistics.service.spec.ts | 2 +- src/app/statistics/statistics.service.ts | 2 +- ...bmission-import-external.component.spec.ts | 2 +- .../configuration-search-page.component.ts | 6 +-- 119 files changed, 290 insertions(+), 309 deletions(-) rename src/app/shared/search/{ => models}/facet-config-response.model.ts (62%) rename src/app/shared/search/{ => models}/facet-value.model.ts (82%) rename src/app/shared/search/{ => models}/facet-values.model.ts (72%) rename src/app/shared/search/{ => models}/filter-type.model.ts (100%) rename src/app/shared/search/{ => models}/paginated-search-options.model.spec.ts (87%) rename src/app/shared/search/{ => models}/paginated-search-options.model.ts (82%) rename src/app/shared/search/{ => models}/search-filter-config.model.ts (78%) rename src/app/shared/search/{ => models}/search-filter.model.ts (100%) rename src/app/shared/search/{ => models}/search-objects.model.ts (68%) rename src/app/shared/search/{ => models}/search-options.model.spec.ts (90%) rename src/app/shared/search/{ => models}/search-options.model.ts (91%) rename src/app/shared/search/{ => models}/search-query-response.model.ts (87%) rename src/app/shared/search/{ => models}/search-result.model.ts (59%) rename src/app/shared/search/{ => models/types}/facet-config-response.resouce-type.ts (75%) rename src/app/shared/search/{ => models/types}/facet-values.resource-type.ts (74%) rename src/app/shared/search/{ => models/types}/search-filter-config.resource-type.ts (75%) rename src/app/shared/search/{ => models/types}/search-objects.resource-type.ts (74%) rename src/app/shared/search/{ => models/types}/search-result.resource-type.ts (73%) rename src/app/{search-page => shared/search}/search.component.html (89%) rename src/app/{search-page => shared/search}/search.component.scss (100%) rename src/app/{search-page => shared/search}/search.component.spec.ts (84%) rename src/app/{search-page => shared/search}/search.component.ts (77%) 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 0dfd013449..e8d8d3eb11 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 @@ -33,7 +33,7 @@ import { ErrorComponent } from '../../shared/error/error.component'; import { LoadingComponent } from '../../shared/loading/loading.component'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; import { SearchService } from '../../core/shared/search/search.service'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject, diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts index 9a93457436..3172616efc 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts @@ -9,10 +9,11 @@ import { Collection } from '../../core/shared/collection.model'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { map, startWith, switchMap, take } from 'rxjs/operators'; import { - getRemoteDataPayload, - getFirstSucceededRemoteData, - toDSpaceObjectListRD, - getFirstCompletedRemoteData, getAllSucceededRemoteData + getAllSucceededRemoteData, + getFirstCompletedRemoteData, + getFirstSucceededRemoteData, + getRemoteDataPayload, + toDSpaceObjectListRD } from '../../core/shared/operators'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; @@ -24,7 +25,7 @@ import { CollectionDataService } from '../../core/data/collection-data.service'; import { isNotEmpty } from '../../shared/empty.util'; import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { SearchService } from '../../core/shared/search/search.service'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { NoContent } from '../../core/shared/NoContent.model'; diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index 366e1da7b1..820fd0c4a8 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -1,13 +1,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { - BehaviorSubject, - combineLatest as observableCombineLatest, - Observable, - Subject -} from 'rxjs'; +import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, Subject } from 'rxjs'; import { filter, map, mergeMap, startWith, switchMap, take } from 'rxjs/operators'; -import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; import { SearchService } from '../core/shared/search/search.service'; import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; import { CollectionDataService } from '../core/data/collection-data.service'; diff --git a/src/app/core/data/bundle-data.service.ts b/src/app/core/data/bundle-data.service.ts index bff21d2c8d..3c885c0afd 100644 --- a/src/app/core/data/bundle-data.service.ts +++ b/src/app/core/data/bundle-data.service.ts @@ -20,7 +20,7 @@ import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { FindListOptions, GetRequest } from './request.models'; import { RequestService } from './request.service'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { Bitstream } from '../shared/bitstream.model'; import { RequestEntryState } from './request.reducer'; diff --git a/src/app/core/data/external-source.service.ts b/src/app/core/data/external-source.service.ts index a3a0a532ec..d2fc9e6d96 100644 --- a/src/app/core/data/external-source.service.ts +++ b/src/app/core/data/external-source.service.ts @@ -12,7 +12,7 @@ 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'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { hasValue, isNotEmptyOperator } from '../../shared/empty.util'; import { RemoteData } from './remote-data'; import { PaginatedList } from './paginated-list.model'; 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..8c24bd61d9 100644 --- a/src/app/core/data/facet-config-response-parsing.service.ts +++ b/src/app/core/data/facet-config-response-parsing.service.ts @@ -1,11 +1,11 @@ import { Injectable } from '@angular/core'; -import { SearchFilterConfig } from '../../shared/search/search-filter-config.model'; +import { SearchFilterConfig } from '../../shared/search/models/search-filter-config.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 { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; -import { FacetConfigResponse } from '../../shared/search/facet-config-response.model'; +import { FacetConfigResponse } from '../../shared/search/models/facet-config-response.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..12a2d4ba8c 100644 --- a/src/app/core/data/facet-value-response-parsing.service.ts +++ b/src/app/core/data/facet-value-response-parsing.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; -import { FacetValue } from '../../shared/search/facet-value.model'; +import { FacetValue } from '../../shared/search/models/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 { FacetValues } from '../../shared/search/models/facet-values.model'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; @Injectable() diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index c31b6b3c97..a8d380124e 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -25,7 +25,7 @@ import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { DeleteRequest, FindListOptions, GetRequest, PostRequest, PutRequest, RestRequest } from './request.models'; import { RequestService } from './request.service'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { Bundle } from '../shared/bundle.model'; import { MetadataMap } from '../shared/metadata.models'; import { BundleDataService } from './bundle-data.service'; diff --git a/src/app/core/data/lookup-relation.service.spec.ts b/src/app/core/data/lookup-relation.service.spec.ts index 876336bfa9..c9e523f796 100644 --- a/src/app/core/data/lookup-relation.service.spec.ts +++ b/src/app/core/data/lookup-relation.service.spec.ts @@ -5,9 +5,9 @@ import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.util import { createPaginatedList } from '../../shared/testing/utils.test'; import { buildPaginatedList } from './paginated-list.model'; import { PageInfo } from '../shared/page-info.model'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { RelationshipOptions } from '../../shared/form/builder/models/relationship-options.model'; -import { SearchResult } from '../../shared/search/search-result.model'; +import { SearchResult } from '../../shared/search/models/search-result.model'; import { Item } from '../shared/item.model'; import { skip, take } from 'rxjs/operators'; import { ExternalSource } from '../shared/external-source.model'; diff --git a/src/app/core/data/lookup-relation.service.ts b/src/app/core/data/lookup-relation.service.ts index 7ecf3a19cc..7808a24e92 100644 --- a/src/app/core/data/lookup-relation.service.ts +++ b/src/app/core/data/lookup-relation.service.ts @@ -1,11 +1,11 @@ import { ExternalSourceService } from './external-source.service'; import { SearchService } from '../shared/search/search.service'; import { concat, distinctUntilChanged, map, multicast, startWith, take, takeWhile } from 'rxjs/operators'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { Observable, ReplaySubject } from 'rxjs'; import { RemoteData } from './remote-data'; import { PaginatedList } from './paginated-list.model'; -import { SearchResult } from '../../shared/search/search-result.model'; +import { SearchResult } from '../../shared/search/models/search-result.model'; import { DSpaceObject } from '../shared/dspace-object.model'; import { RelationshipOptions } from '../../shared/form/builder/models/relationship-options.model'; import { Item } from '../shared/item.model'; diff --git a/src/app/core/data/mydspace-response-parsing.service.ts b/src/app/core/data/mydspace-response-parsing.service.ts index f71eaeb811..e111aca9dd 100644 --- a/src/app/core/data/mydspace-response-parsing.service.ts +++ b/src/app/core/data/mydspace-response-parsing.service.ts @@ -4,7 +4,7 @@ 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 { SearchObjects } from '../../shared/search/models/search-objects.model'; import { MetadataMap, MetadataValue } from '../shared/metadata.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts index be2fbe90fc..814a2f8d1f 100644 --- a/src/app/core/data/search-response-parsing.service.ts +++ b/src/app/core/data/search-response-parsing.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { hasValue } from '../../shared/empty.util'; -import { SearchObjects } from '../../shared/search/search-objects.model'; +import { SearchObjects } from '../../shared/search/models/search-objects.model'; import { ParsedResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; diff --git a/src/app/core/data/version-history-data.service.ts b/src/app/core/data/version-history-data.service.ts index 4268516e6b..9aa4f055ff 100644 --- a/src/app/core/data/version-history-data.service.ts +++ b/src/app/core/data/version-history-data.service.ts @@ -12,7 +12,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { FindListOptions, PostRequest, RestRequest } from './request.models'; import { Observable, of } from 'rxjs'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { RemoteData } from './remote-data'; import { PaginatedList } from './paginated-list.model'; import { Version } from '../shared/version.model'; diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index 3be04447ab..ea2a0283eb 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -13,7 +13,7 @@ import { withLatestFrom } from 'rxjs/operators'; import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util'; -import { SearchResult } from '../../shared/search/search-result.model'; +import { SearchResult } from '../../shared/search/models/search-result.model'; import { PaginatedList } from '../data/paginated-list.model'; import { RemoteData } from '../data/remote-data'; import { RestRequest } from '../data/request.models'; 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 805ecd0486..96f9ac5018 100644 --- a/src/app/core/shared/search/search-configuration.service.spec.ts +++ b/src/app/core/shared/search/search-configuration.service.spec.ts @@ -2,8 +2,8 @@ import { SearchConfigurationService } from './search-configuration.service'; import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; -import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; -import { SearchFilter } from '../../../shared/search/search-filter.model'; +import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; +import { SearchFilter } from '../../../shared/search/models/search-filter.model'; import { of as observableOf } from 'rxjs'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index 8c37fbc8f5..3b162a60b9 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -7,26 +7,23 @@ import { combineLatest as observableCombineLatest, merge as observableMerge, Observable, + of, Subscription } from 'rxjs'; import { distinctUntilChanged, filter, map, startWith, switchMap, take } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { SearchOptions } from '../../../shared/search/search-options.model'; -import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; -import { SearchFilter } from '../../../shared/search/search-filter.model'; +import { SearchOptions } from '../../../shared/search/models/search-options.model'; +import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; +import { SearchFilter } from '../../../shared/search/models/search-filter.model'; import { RemoteData } from '../../data/remote-data'; import { DSpaceObjectType } from '../dspace-object-type.model'; import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; import { RouteService } from '../../services/route.service'; -import { - getAllSucceededRemoteDataPayload, - getFirstSucceededRemoteData -} from '../operators'; +import { getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData } from '../operators'; import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { SearchConfig } from './search-filters/search-config.model'; import { SearchService } from './search.service'; -import { of } from 'rxjs'; import { PaginationService } from '../../pagination/pagination.service'; /** diff --git a/src/app/core/shared/search/search-filter.service.spec.ts b/src/app/core/shared/search/search-filter.service.spec.ts index 045b2b17c9..a42bf8e5f6 100644 --- a/src/app/core/shared/search/search-filter.service.spec.ts +++ b/src/app/core/shared/search/search-filter.service.spec.ts @@ -10,8 +10,8 @@ import { SearchFilterToggleAction } from '../../../shared/search/search-filters/search-filter/search-filter.actions'; import { SearchFiltersState } from '../../../shared/search/search-filters/search-filter/search-filter.reducer'; -import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model'; -import { FilterType } from '../../../shared/search/filter-type.model'; +import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model'; +import { FilterType } from '../../../shared/search/models/filter-type.model'; import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; import { of as observableOf } from 'rxjs'; import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; diff --git a/src/app/core/shared/search/search-filter.service.ts b/src/app/core/shared/search/search-filter.service.ts index 84d7268abb..00125e31f5 100644 --- a/src/app/core/shared/search/search-filter.service.ts +++ b/src/app/core/shared/search/search-filter.service.ts @@ -16,7 +16,7 @@ import { SearchFilterToggleAction } from '../../../shared/search/search-filters/search-filter/search-filter.actions'; import { hasValue, isNotEmpty, } from '../../../shared/empty.util'; -import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model'; +import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model'; import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; import { RouteService } from '../../services/route.service'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; diff --git a/src/app/core/shared/search/search.service.spec.ts b/src/app/core/shared/search/search.service.spec.ts index 00f10230c3..f9b768655e 100644 --- a/src/app/core/shared/search/search.service.spec.ts +++ b/src/app/core/shared/search/search.service.spec.ts @@ -9,7 +9,7 @@ import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; import { RouterStub } from '../../../shared/testing/router.stub'; 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 { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; import { RemoteData } from '../../data/remote-data'; import { RequestEntry } from '../../data/request.reducer'; import { getMockRequestService } from '../../../shared/mocks/request.service.mock'; @@ -21,11 +21,8 @@ import { RouteService } from '../../services/route.service'; import { routeServiceStub } from '../../../shared/testing/route-service.stub'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; -import { SearchObjects } from '../../../shared/search/search-objects.model'; +import { SearchObjects } from '../../../shared/search/models/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'; diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 91916a35ac..f70416594d 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -14,24 +14,24 @@ import { GenericConstructor } from '../generic-constructor'; import { HALEndpointService } from '../hal-endpoint.service'; import { URLCombiner } from '../../url-combiner/url-combiner'; import { hasValue, hasValueOperator, isNotEmpty } from '../../../shared/empty.util'; -import { SearchOptions } from '../../../shared/search/search-options.model'; -import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model'; +import { SearchOptions } from '../../../shared/search/models/search-options.model'; +import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model'; import { SearchResponseParsingService } from '../../data/search-response-parsing.service'; -import { SearchObjects } from '../../../shared/search/search-objects.model'; +import { SearchObjects } from '../../../shared/search/models/search-objects.model'; import { FacetValueResponseParsingService } from '../../data/facet-value-response-parsing.service'; import { FacetConfigResponseParsingService } from '../../data/facet-config-response-parsing.service'; -import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; import { CommunityDataService } from '../../data/community-data.service'; import { ViewMode } from '../view-mode.model'; import { DSpaceObjectDataService } from '../../data/dspace-object-data.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../operators'; import { RouteService } from '../../services/route.service'; -import { SearchResult } from '../../../shared/search/search-result.model'; +import { SearchResult } from '../../../shared/search/models/search-result.model'; import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model'; import { getSearchResultFor } from '../../../shared/search/search-result-element-decorator'; -import { FacetConfigResponse } from '../../../shared/search/facet-config-response.model'; -import { FacetValues } from '../../../shared/search/facet-values.model'; +import { FacetConfigResponse } from '../../../shared/search/models/facet-config-response.model'; +import { FacetValues } from '../../../shared/search/models/facet-values.model'; import { SearchConfig } from './search-filters/search-config.model'; import { PaginationService } from '../../pagination/pagination.service'; import { SearchConfigurationService } from './search-configuration.service'; @@ -407,6 +407,7 @@ export class SearchService implements OnDestroy { /** * Changes the current view mode in the current URL * @param {ViewMode} viewMode Mode to switch to + * @param {string[]} searchLinkParts */ setViewMode(viewMode: ViewMode, searchLinkParts?: string[]) { this.paginationService.getCurrentPagination(this.searchConfigurationService.paginationID, new PaginationComponentOptions()).pipe(take(1)) diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts index d66c5d060d..42e40b8f6d 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts @@ -15,14 +15,11 @@ import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../../core import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { Bundle } from '../../../core/shared/bundle.model'; -import { - FieldUpdate, - FieldUpdates -} from '../../../core/data/object-updates/object-updates.reducer'; +import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer'; import { Bitstream } from '../../../core/shared/bitstream.model'; import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { BundleDataService } from '../../../core/data/bundle-data.service'; -import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; import { ResponsiveColumnSizes } from '../../../shared/responsive-table-sizes/responsive-column-sizes'; import { ResponsiveTableSizes } from '../../../shared/responsive-table-sizes/responsive-table-sizes'; import { NoContent } from '../../../core/shared/NoContent.model'; diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts index f3f00abf92..2c81a4e2cb 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts @@ -5,7 +5,7 @@ import { Bitstream } from '../../../../../core/shared/bitstream.model'; import { ObjectUpdatesService } from '../../../../../core/data/object-updates/object-updates.service'; import { BundleDataService } from '../../../../../core/data/bundle-data.service'; import { switchMap } from 'rxjs/operators'; -import { PaginatedSearchOptions } from '../../../../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../../shared/search/models/paginated-search-options.model'; import { ResponsiveTableSizes } from '../../../../../shared/responsive-table-sizes/responsive-table-sizes'; import { followLink } from '../../../../../shared/utils/follow-link-config.model'; import { ObjectValuesPipe } from '../../../../../shared/utils/object-values-pipe'; diff --git a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.spec.ts b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.spec.ts index b5473fa02d..c073f2d63e 100644 --- a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.spec.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; import { EventEmitter } from '@angular/core'; -import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; @@ -25,7 +25,7 @@ import { ObjectSelectService } from '../../../shared/object-select/object-select import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { PaginationComponent } from '../../../shared/pagination/pagination.component'; import { SearchFormComponent } from '../../../shared/search-form/search-form.component'; -import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; import { HostWindowServiceStub } from '../../../shared/testing/host-window-service.stub'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service.stub'; diff --git a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts index a7dc8dc1e5..a71fe5b385 100644 --- a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts +++ b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts @@ -9,11 +9,12 @@ import { PaginatedList } from '../../../core/data/paginated-list.model'; import { Collection } from '../../../core/shared/collection.model'; import { Item } from '../../../core/shared/item.model'; import { + getAllSucceededRemoteData, + getFirstCompletedRemoteData, + getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload, getRemoteDataPayload, - getFirstSucceededRemoteData, - toDSpaceObjectListRD, - getAllSucceededRemoteData, getFirstCompletedRemoteData + toDSpaceObjectListRD } from '../../../core/shared/operators'; import { ActivatedRoute, Router } from '@angular/router'; import { filter, map, startWith, switchMap, take } from 'rxjs/operators'; @@ -22,7 +23,7 @@ import { TranslateService } from '@ngx-translate/core'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; import { hasValue, isNotEmpty } from '../../../shared/empty.util'; -import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SearchService } from '../../../core/shared/search/search.service'; import { NoContent } from '../../../core/shared/NoContent.model'; diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index fe7ff0aacf..c7dd6799d7 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -3,7 +3,13 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; -import { combineLatest as observableCombineLatest, from as observableFrom, BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { + BehaviorSubject, + combineLatest as observableCombineLatest, + from as observableFrom, + Observable, + Subscription +} from 'rxjs'; import { FieldUpdate, FieldUpdates, @@ -25,7 +31,7 @@ import { ItemType } from '../../../../core/shared/item-relationships/item-type.m import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component'; import { RelationshipOptions } from '../../../../shared/form/builder/models/relationship-options.model'; import { SelectableListService } from '../../../../shared/object-list/selectable-list/selectable-list.service'; -import { SearchResult } from '../../../../shared/search/search-result.model'; +import { SearchResult } from '../../../../shared/search/models/search-result.model'; import { followLink } from '../../../../shared/utils/follow-link-config.model'; import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { RemoteData } from '../../../../core/data/remote-data'; diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts index fa278da967..b4926d7b92 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts @@ -1,16 +1,15 @@ import { of as observableOf } from 'rxjs'; import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; -import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; -import { SearchFilter } from '../shared/search/search-filter.model'; +import { SearchFilter } from '../shared/search/models/search-filter.model'; import { ActivatedRouteStub } from '../shared/testing/active-router.stub'; import { RoleServiceMock } from '../shared/mocks/role-service.mock'; import { cold, hot } from 'jasmine-marbles'; import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; -import { PaginationService } from '../core/pagination/pagination.service'; describe('MyDSpaceConfigurationService', () => { let service: MyDSpaceConfigurationService; diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts index 580afd8ad4..127d266138 100644 --- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts +++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts @@ -13,7 +13,7 @@ import { UploaderOptions } from '../../shared/uploader/uploader-options.model'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { NotificationType } from '../../shared/notifications/models/notification-type'; import { hasValue } from '../../shared/empty.util'; -import { SearchResult } from '../../shared/search/search-result.model'; +import { SearchResult } from '../../shared/search/models/search-result.model'; import { CollectionSelectorComponent } from '../collection-selector/collection-selector.component'; import { UploaderComponent } from '../../shared/uploader/uploader.component'; import { UploaderError } from '../../shared/uploader/uploader-error.model'; diff --git a/src/app/my-dspace-page/my-dspace-page.component.scss b/src/app/my-dspace-page/my-dspace-page.component.scss index b35b2ba709..b9b3e5d2f6 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.scss +++ b/src/app/my-dspace-page/my-dspace-page.component.scss @@ -1 +1 @@ -@import '../search-page/search.component.scss'; +@import '../shared/search/search.component'; 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 b4b75b42a0..6d8bfc274c 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 @@ -21,7 +21,7 @@ import { routeServiceStub } from '../shared/testing/route-service.stub'; import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service.stub'; import { SearchService } from '../core/shared/search/search.service'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; -import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { SearchFilterService } from '../core/shared/search/search-filter.service'; import { RoleDirective } from '../shared/roles/role.directive'; diff --git a/src/app/my-dspace-page/my-dspace-page.component.ts b/src/app/my-dspace-page/my-dspace-page.component.ts index 89a4023be8..bac1713230 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.ts +++ b/src/app/my-dspace-page/my-dspace-page.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - Inject, - InjectionToken, - Input, - OnInit -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, InjectionToken, Input, OnInit } from '@angular/core'; import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs'; import { map, switchMap, tap } from 'rxjs/operators'; @@ -15,7 +8,7 @@ import { RemoteData } from '../core/data/remote-data'; import { DSpaceObject } from '../core/shared/dspace-object.model'; import { pushInOut } from '../shared/animations/push'; import { HostWindowService } from '../shared/host-window.service'; -import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; import { SearchService } from '../core/shared/search/search.service'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { hasValue } from '../shared/empty.util'; @@ -27,10 +20,10 @@ import { SearchConfigurationService } from '../core/shared/search/search-configu import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; import { ViewMode } from '../core/shared/view-mode.model'; import { MyDSpaceRequest } from '../core/data/request.models'; -import { SearchResult } from '../shared/search/search-result.model'; +import { SearchResult } from '../shared/search/models/search-result.model'; import { Context } from '../core/shared/context.model'; import { SortOptions } from '../core/cache/models/sort-options.model'; -import { SearchObjects } from '../shared/search/search-objects.model'; +import { SearchObjects } from '../shared/search/models/search-objects.model'; export const MYDSPACE_ROUTE = '/mydspace'; export const SEARCH_CONFIG_SERVICE: InjectionToken = new InjectionToken('searchConfigurationService'); diff --git a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts b/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts index 77f27e9d42..cc8f7c8fc3 100644 --- a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts +++ b/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts @@ -6,8 +6,8 @@ import { PaginatedList } from '../../core/data/paginated-list.model'; import { ViewMode } from '../../core/shared/view-mode.model'; import { isEmpty } from '../../shared/empty.util'; import { Context } from '../../core/shared/context.model'; -import { SearchResult } from '../../shared/search/search-result.model'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { SearchResult } from '../../shared/search/models/search-result.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; /** * Component that represents all results for mydspace page diff --git a/src/app/search-page/configuration-search-page.component.spec.ts b/src/app/search-page/configuration-search-page.component.spec.ts index 5ca593981f..4468aad9c4 100644 --- a/src/app/search-page/configuration-search-page.component.spec.ts +++ b/src/app/search-page/configuration-search-page.component.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { configureSearchComponentTestingModule } from './search.component.spec'; +import { configureSearchComponentTestingModule } from '../shared/search/search.component.spec'; import { ConfigurationSearchPageComponent } from './configuration-search-page.component'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { Component, ViewChild } from '@angular/core'; diff --git a/src/app/search-page/configuration-search-page.component.ts b/src/app/search-page/configuration-search-page.component.ts index df25febde7..6daa822e2e 100644 --- a/src/app/search-page/configuration-search-page.component.ts +++ b/src/app/search-page/configuration-search-page.component.ts @@ -1,13 +1,7 @@ import { HostWindowService } from '../shared/host-window.service'; import { SidebarService } from '../shared/sidebar/sidebar.service'; -import { SearchComponent } from './search.component'; -import { - ChangeDetectionStrategy, - Component, - Inject, - Input, - OnInit -} from '@angular/core'; +import { SearchComponent } from '../shared/search/search.component'; +import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core'; import { pushInOut } from '../shared/animations/push'; import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; @@ -21,8 +15,8 @@ import { Router } from '@angular/router'; */ @Component({ selector: 'ds-configuration-search-page', - styleUrls: ['./search.component.scss'], - templateUrl: './search.component.html', + styleUrls: ['../shared/search/search.component.scss'], + templateUrl: '../shared/search/search.component.html', changeDetection: ChangeDetectionStrategy.OnPush, animations: [pushInOut], providers: [ diff --git a/src/app/search-page/search-page.module.ts b/src/app/search-page/search-page.module.ts index d7015974d8..758eca15c0 100644 --- a/src/app/search-page/search-page.module.ts +++ b/src/app/search-page/search-page.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CoreModule } from '../core/core.module'; import { SharedModule } from '../shared/shared.module'; -import { SearchComponent } from './search.component'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { ConfigurationSearchPageGuard } from './configuration-search-page.guard'; import { SearchTrackerComponent } from './search-tracker.component'; @@ -18,7 +17,6 @@ import { SearchModule } from '../shared/search/search.module'; const components = [ SearchPageComponent, - SearchComponent, SearchTrackerComponent, ThemedSearchPageComponent ]; diff --git a/src/app/search-page/search-tracker.component.ts b/src/app/search-page/search-tracker.component.ts index e7f59a2f23..f766e6f669 100644 --- a/src/app/search-page/search-tracker.component.ts +++ b/src/app/search-page/search-tracker.component.ts @@ -1,15 +1,15 @@ import { Component, Inject, OnInit } from '@angular/core'; import { Angulartics2 } from 'angulartics2'; import { map, switchMap } from 'rxjs/operators'; -import { SearchComponent } from './search.component'; +import { SearchComponent } from '../shared/search/search.component'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { HostWindowService } from '../shared/host-window.service'; import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component'; import { RouteService } from '../core/services/route.service'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { SearchService } from '../core/shared/search/search.service'; -import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model'; -import { SearchObjects } from '../shared/search/search-objects.model'; +import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; +import { SearchObjects } from '../shared/search/models/search-objects.model'; import { Router } from '@angular/router'; import { RemoteData } from '../core/data/remote-data'; import { DSpaceObject } from '../core/shared/dspace-object.model'; 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 b6aa0b3413..176d131566 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 @@ -6,7 +6,7 @@ import { Observable } from 'rxjs'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { map } from 'rxjs/operators'; import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model'; -import { SearchResult } from '../../../search/search-result.model'; +import { SearchResult } from '../../../search/models/search-result.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; import { followLink } from '../../../utils/follow-link-config.model'; diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts index 2565b36f7d..7c28859388 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts @@ -7,7 +7,7 @@ import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model' import { ItemSearchResult } from '../../object-collection/shared/item-search-result.model'; import { Item } from '../../../core/shared/item.model'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; -import { PaginatedSearchOptions } from '../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../search/models/paginated-search-options.model'; import { hasValue } from '../../empty.util'; import { createPaginatedList } from '../../testing/utils.test'; import { NotificationsService } from '../../notifications/notifications.service'; diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts b/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts index ebd9f24b61..82ad095cd4 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts @@ -22,15 +22,15 @@ import { debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators'; import { SearchService } from '../../../core/shared/search/search.service'; import { CollectionElementLinkType } from '../../object-collection/collection-element-link.type'; -import { PaginatedSearchOptions } from '../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../search/models/paginated-search-options.model'; import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { Context } from '../../../core/shared/context.model'; import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../empty.util'; -import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated-list.model'; -import { SearchResult } from '../../search/search-result.model'; +import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { RemoteData } from '../../../core/data/remote-data'; import { NotificationsService } from '../../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 7adb9a837b..eb448a7d24 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -81,7 +81,7 @@ import { DYNAMIC_FORM_CONTROL_TYPE_CUSTOM_SWITCH } from './models/custom-switch/ import { CustomSwitchComponent } from './models/custom-switch/custom-switch.component'; import { find, map, startWith, switchMap, take } from 'rxjs/operators'; import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; -import { SearchResult } from '../../../search/search-result.model'; +import { SearchResult } from '../../../search/models/search-result.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { RelationshipService } from '../../../../core/data/relationship.service'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts index 4b4eedeffa..5d81eaf29a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; -import { NgZone, NO_ERRORS_SCHEMA, DebugElement, EventEmitter } from '@angular/core'; +import { DebugElement, NgZone, NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf, Subscription } from 'rxjs'; import { DsDynamicLookupRelationModalComponent } from './dynamic-lookup-relation-modal.component'; import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; @@ -14,7 +14,7 @@ import { ItemSearchResult } from '../../../../object-collection/shared/item-sear import { RelationshipOptions } from '../../models/relationship-options.model'; import { AddRelationshipAction, RemoveRelationshipAction } from './relationship.actions'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; -import { PaginatedSearchOptions } from '../../../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../search/models/paginated-search-options.model'; import { ExternalSource } from '../../../../../core/shared/external-source.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { createPaginatedList } from '../../../../testing/utils.test'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts index fb5a9474d5..62071a5ad2 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts @@ -9,7 +9,7 @@ import { SelectableListService } from '../../../../object-list/selectable-list/s import { SelectableListState } from '../../../../object-list/selectable-list/selectable-list.reducer'; import { ListableObject } from '../../../../object-collection/shared/listable-object.model'; import { RelationshipOptions } from '../../models/relationship-options.model'; -import { SearchResult } from '../../../../search/search-result.model'; +import { SearchResult } from '../../../../search/models/search-result.model'; import { Item } from '../../../../../core/shared/item.model'; import { AddRelationshipAction, diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts index 62c33bd967..00b0527af6 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts @@ -4,7 +4,7 @@ import { VarDirective } from '../../../../../utils/var.directive'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; -import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../../search/models/paginated-search-options.model'; import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; import { of as observableOf } from 'rxjs'; import { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts index 48b9edc82a..ca2535cb91 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts @@ -8,7 +8,7 @@ import { PaginatedList } from '../../../../../../core/data/paginated-list.model' import { ExternalSourceEntry } from '../../../../../../core/shared/external-source-entry.model'; import { ExternalSource } from '../../../../../../core/shared/external-source.model'; import { map, startWith, switchMap } from 'rxjs/operators'; -import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../../search/models/paginated-search-options.model'; import { Context } from '../../../../../../core/shared/context.model'; import { ListableObject } from '../../../../../object-collection/shared/listable-object.model'; import { fadeIn, fadeInOut } from '../../../../../animations/fade'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts index d586520b64..bf1f2f7e70 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts @@ -6,11 +6,11 @@ import { Metadata } from '../../../../../../../core/shared/metadata.utils'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../../../../../core/data/remote-data'; import { PaginatedList } from '../../../../../../../core/data/paginated-list.model'; -import { SearchResult } from '../../../../../../search/search-result.model'; +import { SearchResult } from '../../../../../../search/models/search-result.model'; import { Item } from '../../../../../../../core/shared/item.model'; import { RelationshipOptions } from '../../../../models/relationship-options.model'; import { LookupRelationService } from '../../../../../../../core/data/lookup-relation.service'; -import { PaginatedSearchOptions } from '../../../../../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../../../search/models/paginated-search-options.model'; import { CollectionElementLinkType } from '../../../../../../object-collection/collection-element-link.type'; import { Context } from '../../../../../../../core/shared/context.model'; import { SelectableListService } from '../../../../../../object-list/selectable-list/selectable-list.service'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts index f2e7efeaa5..09929f1973 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts @@ -10,7 +10,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { VarDirective } from '../../../../../utils/var.directive'; import { RelationshipOptions } from '../../../models/relationship-options.model'; import { of as observableOf } from 'rxjs'; -import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../../search/models/paginated-search-options.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../../remote-data.utils'; import { buildPaginatedList } from '../../../../../../core/data/paginated-list.model'; import { ItemSearchResult } from '../../../../../object-collection/shared/item-search-result.model'; @@ -21,11 +21,9 @@ import { PaginationService } from '../../../../../../core/pagination/pagination. import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub'; import { RelationshipService } from '../../../../../../core/data/relationship.service'; import { relatedRelationships } from '../../../../../testing/related-relationships.mock'; -import { RelationshipTypeService } from '../../../../../../core/data/relationship-type.service'; import { RelationshipType } from '../../../../../../core/shared/item-relationships/relationship-type.model'; - describe('DsDynamicLookupRelationSearchTabComponent', () => { let component: DsDynamicLookupRelationSearchTabComponent; let fixture: ComponentFixture; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts index 96af591540..23dd97601e 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angu import { SEARCH_CONFIG_SERVICE } from '../../../../../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; import { Item } from '../../../../../../core/shared/item.model'; -import { SearchResult } from '../../../../../search/search-result.model'; +import { SearchResult } from '../../../../../search/models/search-result.model'; import { PaginatedList } from '../../../../../../core/data/paginated-list.model'; import { RemoteData } from '../../../../../../core/data/remote-data'; import { Observable } from 'rxjs'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts index dbe46609a6..472a719b27 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts @@ -4,7 +4,7 @@ import { SearchConfigurationService } from '../../../../../../core/shared/search import { NO_ERRORS_SCHEMA } from '@angular/core'; import { VarDirective } from '../../../../../utils/var.directive'; import { Observable, of as observableOf } from 'rxjs'; -import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../../search/models/paginated-search-options.model'; import { ItemSearchResult } from '../../../../../object-collection/shared/item-search-result.model'; import { Item } from '../../../../../../core/shared/item.model'; import { DsDynamicLookupRelationSelectionTabComponent } from './dynamic-lookup-relation-selection-tab.component'; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts index 9ba0ee2413..a78bf8896c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts @@ -8,7 +8,7 @@ import { map, switchMap, take } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; import { buildPaginatedList, PaginatedList } from '../../../../../../core/data/paginated-list.model'; import { Router } from '@angular/router'; -import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../../../../search/models/paginated-search-options.model'; import { PageInfo } from '../../../../../../core/shared/page-info.model'; import { Context } from '../../../../../../core/shared/context.model'; import { createSuccessfulRemoteDataObject } from '../../../../../remote-data.utils'; diff --git a/src/app/shared/item/item-versions/item-versions.component.ts b/src/app/shared/item/item-versions/item-versions.component.ts index e7d65919d6..f36e8ab8fe 100644 --- a/src/app/shared/item/item-versions/item-versions.component.ts +++ b/src/app/shared/item/item-versions/item-versions.component.ts @@ -23,7 +23,7 @@ import { map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; -import { PaginatedSearchOptions } from '../../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../search/models/paginated-search-options.model'; import { AlertType } from '../../alert/aletr-type'; import { followLink } from '../../utils/follow-link-config.model'; import { hasValue, hasValueOperator } from '../../empty.util'; diff --git a/src/app/shared/object-collection/shared/claimed-approved-task-search-result.model.ts b/src/app/shared/object-collection/shared/claimed-approved-task-search-result.model.ts index 7cacd87048..71b885d249 100644 --- a/src/app/shared/object-collection/shared/claimed-approved-task-search-result.model.ts +++ b/src/app/shared/object-collection/shared/claimed-approved-task-search-result.model.ts @@ -1,5 +1,5 @@ import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; /** * Represents a search result object of an Approved ClaimedTask object diff --git a/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts b/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts index ff775be909..eaf63bf6dd 100644 --- a/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts +++ b/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts @@ -1,5 +1,5 @@ import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; /** * Represents a search result object of a Declined ClaimedTask object diff --git a/src/app/shared/object-collection/shared/claimed-task-search-result.model.ts b/src/app/shared/object-collection/shared/claimed-task-search-result.model.ts index 1148ea1526..f8d0752245 100644 --- a/src/app/shared/object-collection/shared/claimed-task-search-result.model.ts +++ b/src/app/shared/object-collection/shared/claimed-task-search-result.model.ts @@ -1,5 +1,5 @@ import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { searchResultFor } from '../../search/search-result-element-decorator'; /** diff --git a/src/app/shared/object-collection/shared/collection-search-result.model.ts b/src/app/shared/object-collection/shared/collection-search-result.model.ts index 3b32197533..615ef0809c 100644 --- a/src/app/shared/object-collection/shared/collection-search-result.model.ts +++ b/src/app/shared/object-collection/shared/collection-search-result.model.ts @@ -1,4 +1,4 @@ -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { Collection } from '../../../core/shared/collection.model'; import { searchResultFor } from '../../search/search-result-element-decorator'; diff --git a/src/app/shared/object-collection/shared/community-search-result.model.ts b/src/app/shared/object-collection/shared/community-search-result.model.ts index 9ce936b5d8..e80dfdaecf 100644 --- a/src/app/shared/object-collection/shared/community-search-result.model.ts +++ b/src/app/shared/object-collection/shared/community-search-result.model.ts @@ -1,5 +1,5 @@ import { Community } from '../../../core/shared/community.model'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { searchResultFor } from '../../search/search-result-element-decorator'; @searchResultFor(Community) diff --git a/src/app/shared/object-collection/shared/item-search-result.model.ts b/src/app/shared/object-collection/shared/item-search-result.model.ts index 4e445f0c88..66f3eb1513 100644 --- a/src/app/shared/object-collection/shared/item-search-result.model.ts +++ b/src/app/shared/object-collection/shared/item-search-result.model.ts @@ -1,4 +1,4 @@ -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { Item } from '../../../core/shared/item.model'; import { inheritEquatable } from '../../../core/utilities/equals.decorators'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; diff --git a/src/app/shared/object-collection/shared/pool-task-search-result.model.ts b/src/app/shared/object-collection/shared/pool-task-search-result.model.ts index 3cff48ba51..9ee74bcd4b 100644 --- a/src/app/shared/object-collection/shared/pool-task-search-result.model.ts +++ b/src/app/shared/object-collection/shared/pool-task-search-result.model.ts @@ -1,5 +1,5 @@ import { PoolTask } from '../../../core/tasks/models/pool-task-object.model'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { searchResultFor } from '../../search/search-result-element-decorator'; /** diff --git a/src/app/shared/object-collection/shared/workflow-item-search-result.model.ts b/src/app/shared/object-collection/shared/workflow-item-search-result.model.ts index 107b66cbee..8611f8678a 100644 --- a/src/app/shared/object-collection/shared/workflow-item-search-result.model.ts +++ b/src/app/shared/object-collection/shared/workflow-item-search-result.model.ts @@ -1,5 +1,5 @@ import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { searchResultFor } from '../../search/search-result-element-decorator'; /** diff --git a/src/app/shared/object-collection/shared/workspace-item-search-result.model.ts b/src/app/shared/object-collection/shared/workspace-item-search-result.model.ts index aa1d4988d4..fddd5dc203 100644 --- a/src/app/shared/object-collection/shared/workspace-item-search-result.model.ts +++ b/src/app/shared/object-collection/shared/workspace-item-search-result.model.ts @@ -1,6 +1,6 @@ import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model'; import { searchResultFor } from '../../search/search-result-element-decorator'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; /** * Represents a search result object of a WorkspaceItem object diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts index 5c880133bc..5faf02eac0 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts @@ -2,7 +2,7 @@ import { Component, Input } from '@angular/core'; import { Metadata } from '../../../../../core/shared/metadata.utils'; import { Item } from '../../../../../core/shared/item.model'; -import { SearchResult } from '../../../../search/search-result.model'; +import { SearchResult } from '../../../../search/models/search-result.model'; /** * This component show values for the given item metadata diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts index 92c1afcb59..778e455004 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts @@ -11,7 +11,7 @@ import { fadeInOut } from '../../../animations/fade'; import { Bitstream } from '../../../../core/shared/bitstream.model'; import { FileService } from '../../../../core/shared/file.service'; import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; -import { SearchResult } from '../../../search/search-result.model'; +import { SearchResult } from '../../../search/models/search-result.model'; /** * This component show metadata for the given item object in the detail view. diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/search-result-detail-element.component.ts index e56576a08b..5b0bd2c39b 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/search-result-detail-element.component.ts @@ -4,7 +4,7 @@ import { AbstractListableElementComponent } from '../../object-collection/shared import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { Metadata } from '../../../core/shared/metadata.utils'; import { hasValue } from '../../empty.util'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; /** * Component representing Search Results with ViewMode.DetailedElement diff --git a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts index da1f0ea11b..6e72eaa942 100644 --- a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { BitstreamDataService } from '../../../core/data/bitstream-data.service'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { Metadata } from '../../../core/shared/metadata.utils'; diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts index 6d9f325757..840960d51f 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts @@ -3,7 +3,7 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { fadeInOut } from '../../../animations/fade'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; -import { SearchResult } from '../../../search/search-result.model'; +import { SearchResult } from '../../../search/models/search-result.model'; /** * This component show metadata for the given item object in the list view. diff --git a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts index afd0363e7a..b85fb0a39f 100644 --- a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { hasValue } from '../../empty.util'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; diff --git a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts index 31ad68ec63..e6bd15df1b 100644 --- a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts +++ b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts @@ -3,7 +3,7 @@ import { VarDirective } from '../../utils/var.directive'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { TruncatableService } from '../../truncatable/truncatable.service'; import { LinkService } from '../../../core/cache/builders/link.service'; diff --git a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts index 693d6a9665..91f3ba6d88 100644 --- a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts +++ b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts @@ -1,4 +1,4 @@ -import { SearchResult } from '../../search/search-result.model'; +import { SearchResult } from '../../search/models/search-result.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { SearchResultListElementComponent } from '../search-result-list-element/search-result-list-element.component'; import { Component } from '@angular/core'; diff --git a/src/app/shared/page-size-selector/page-size-selector.component.ts b/src/app/shared/page-size-selector/page-size-selector.component.ts index ddfe1ca4d9..0dce32a4ba 100644 --- a/src/app/shared/page-size-selector/page-size-selector.component.ts +++ b/src/app/shared/page-size-selector/page-size-selector.component.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; -import { PaginatedSearchOptions } from '../search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model'; import { map, take } from 'rxjs/operators'; import { PaginationService } from '../../core/pagination/pagination.service'; diff --git a/src/app/shared/search/facet-config-response.model.ts b/src/app/shared/search/models/facet-config-response.model.ts similarity index 62% rename from src/app/shared/search/facet-config-response.model.ts rename to src/app/shared/search/models/facet-config-response.model.ts index 74190d35ba..74e6f10d62 100644 --- a/src/app/shared/search/facet-config-response.model.ts +++ b/src/app/shared/search/models/facet-config-response.model.ts @@ -1,10 +1,10 @@ -import { CacheableObject } from '../../core/cache/object-cache.reducer'; -import { typedObject } from '../../core/cache/builders/build-decorators'; -import { FACET_CONFIG_RESPONSE } from './facet-config-response.resouce-type'; -import { excludeFromEquals } from '../../core/utilities/equals.decorators'; +import { CacheableObject } from '../../../core/cache/object-cache.reducer'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { FACET_CONFIG_RESPONSE } from './types/facet-config-response.resouce-type'; +import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; import { SearchFilterConfig } from './search-filter-config.model'; import { deserialize } from 'cerialize'; -import { HALLink } from '../../core/shared/hal-link.model'; +import { HALLink } from '../../../core/shared/hal-link.model'; /** * The response from the discover/facets endpoint diff --git a/src/app/shared/search/facet-value.model.ts b/src/app/shared/search/models/facet-value.model.ts similarity index 82% rename from src/app/shared/search/facet-value.model.ts rename to src/app/shared/search/models/facet-value.model.ts index 969e531bd2..f545d982ce 100644 --- a/src/app/shared/search/facet-value.model.ts +++ b/src/app/shared/search/models/facet-value.model.ts @@ -1,6 +1,6 @@ import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; -import { HALLink } from '../../core/shared/hal-link.model'; -import { HALResource } from '../../core/shared/hal-resource.model'; +import { HALLink } from '../../../core/shared/hal-link.model'; +import { HALResource } from '../../../core/shared/hal-resource.model'; /** * Class representing possible values for a certain filter diff --git a/src/app/shared/search/facet-values.model.ts b/src/app/shared/search/models/facet-values.model.ts similarity index 72% rename from src/app/shared/search/facet-values.model.ts rename to src/app/shared/search/models/facet-values.model.ts index 31b1f560fb..09bef25876 100644 --- a/src/app/shared/search/facet-values.model.ts +++ b/src/app/shared/search/models/facet-values.model.ts @@ -1,11 +1,11 @@ -import { typedObject } from '../../core/cache/builders/build-decorators'; -import { excludeFromEquals } from '../../core/utilities/equals.decorators'; -import { FACET_VALUES } from './facet-values.resource-type'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; +import { FACET_VALUES } from './types/facet-values.resource-type'; import { FacetValue } from './facet-value.model'; import { SearchQueryResponse } from './search-query-response.model'; -import { autoserializeAs, autoserialize, inheritSerialization } from 'cerialize'; +import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; import { FilterType } from './filter-type.model'; -import { PaginatedList } from '../../core/data/paginated-list.model'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; @typedObject @inheritSerialization(PaginatedList) diff --git a/src/app/shared/search/filter-type.model.ts b/src/app/shared/search/models/filter-type.model.ts similarity index 100% rename from src/app/shared/search/filter-type.model.ts rename to src/app/shared/search/models/filter-type.model.ts diff --git a/src/app/shared/search/paginated-search-options.model.spec.ts b/src/app/shared/search/models/paginated-search-options.model.spec.ts similarity index 87% rename from src/app/shared/search/paginated-search-options.model.spec.ts rename to src/app/shared/search/models/paginated-search-options.model.spec.ts index 9881cc1149..7afee06abf 100644 --- a/src/app/shared/search/paginated-search-options.model.spec.ts +++ b/src/app/shared/search/models/paginated-search-options.model.spec.ts @@ -1,7 +1,7 @@ -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; +import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; +import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; import { PaginatedSearchOptions } from './paginated-search-options.model'; -import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; +import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; import { SearchFilter } from './search-filter.model'; describe('PaginatedSearchOptions', () => { diff --git a/src/app/shared/search/paginated-search-options.model.ts b/src/app/shared/search/models/paginated-search-options.model.ts similarity index 82% rename from src/app/shared/search/paginated-search-options.model.ts rename to src/app/shared/search/models/paginated-search-options.model.ts index 950b207c3a..925a7a3a7e 100644 --- a/src/app/shared/search/paginated-search-options.model.ts +++ b/src/app/shared/search/models/paginated-search-options.model.ts @@ -1,9 +1,9 @@ -import { SortOptions } from '../../core/cache/models/sort-options.model'; -import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; -import { isNotEmpty } from '../empty.util'; +import { SortOptions } from '../../../core/cache/models/sort-options.model'; +import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; +import { isNotEmpty } from '../../empty.util'; import { SearchOptions } from './search-options.model'; import { SearchFilter } from './search-filter.model'; -import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; +import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; /** * This model class represents all parameters needed to request information about a certain page of a search request, in a certain order diff --git a/src/app/shared/search/search-filter-config.model.ts b/src/app/shared/search/models/search-filter-config.model.ts similarity index 78% rename from src/app/shared/search/search-filter-config.model.ts rename to src/app/shared/search/models/search-filter-config.model.ts index 1a0be94d2b..129259985a 100644 --- a/src/app/shared/search/search-filter-config.model.ts +++ b/src/app/shared/search/models/search-filter-config.model.ts @@ -1,14 +1,14 @@ import { FilterType } from './filter-type.model'; import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; -import { HALLink } from '../../core/shared/hal-link.model'; -import { typedObject } from '../../core/cache/builders/build-decorators'; -import { CacheableObject } from '../../core/cache/object-cache.reducer'; -import { excludeFromEquals } from '../../core/utilities/equals.decorators'; -import { SEARCH_FILTER_CONFIG } from './search-filter-config.resource-type'; +import { HALLink } from '../../../core/shared/hal-link.model'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { CacheableObject } from '../../../core/cache/object-cache.reducer'; +import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; +import { SEARCH_FILTER_CONFIG } from './types/search-filter-config.resource-type'; - /** - * The configuration for a search filter - */ +/** + * The configuration for a search filter + */ @typedObject export class SearchFilterConfig implements CacheableObject { static type = SEARCH_FILTER_CONFIG; diff --git a/src/app/shared/search/search-filter.model.ts b/src/app/shared/search/models/search-filter.model.ts similarity index 100% rename from src/app/shared/search/search-filter.model.ts rename to src/app/shared/search/models/search-filter.model.ts diff --git a/src/app/shared/search/search-objects.model.ts b/src/app/shared/search/models/search-objects.model.ts similarity index 68% rename from src/app/shared/search/search-objects.model.ts rename to src/app/shared/search/models/search-objects.model.ts index 9974dc64ec..2afbab372b 100644 --- a/src/app/shared/search/search-objects.model.ts +++ b/src/app/shared/search/models/search-objects.model.ts @@ -1,10 +1,10 @@ import { autoserializeAs, inheritSerialization } from 'cerialize'; -import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { SearchResult } from './search-result.model'; -import { PaginatedList } from '../../core/data/paginated-list.model'; -import { typedObject } from '../../core/cache/builders/build-decorators'; -import { SEARCH_OBJECTS } from './search-objects.resource-type'; -import { excludeFromEquals } from '../../core/utilities/equals.decorators'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { SEARCH_OBJECTS } from './types/search-objects.resource-type'; +import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; import { SearchQueryResponse } from './search-query-response.model'; /** diff --git a/src/app/shared/search/search-options.model.spec.ts b/src/app/shared/search/models/search-options.model.spec.ts similarity index 90% rename from src/app/shared/search/search-options.model.spec.ts rename to src/app/shared/search/models/search-options.model.spec.ts index 8bed046736..176d321999 100644 --- a/src/app/shared/search/search-options.model.spec.ts +++ b/src/app/shared/search/models/search-options.model.spec.ts @@ -1,7 +1,6 @@ -import { PaginatedSearchOptions } from './paginated-search-options.model'; import { SearchOptions } from './search-options.model'; import { SearchFilter } from './search-filter.model'; -import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; +import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; describe('SearchOptions', () => { let options: SearchOptions; diff --git a/src/app/shared/search/search-options.model.ts b/src/app/shared/search/models/search-options.model.ts similarity index 91% rename from src/app/shared/search/search-options.model.ts rename to src/app/shared/search/models/search-options.model.ts index 591e4fcb04..470ef8a06f 100644 --- a/src/app/shared/search/search-options.model.ts +++ b/src/app/shared/search/models/search-options.model.ts @@ -1,8 +1,8 @@ -import { hasValue, isNotEmpty } from '../empty.util'; -import { URLCombiner } from '../../core/url-combiner/url-combiner'; +import { hasValue, isNotEmpty } from '../../empty.util'; +import { URLCombiner } from '../../../core/url-combiner/url-combiner'; import { SearchFilter } from './search-filter.model'; -import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; -import { ViewMode } from '../../core/shared/view-mode.model'; +import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; +import { ViewMode } from '../../../core/shared/view-mode.model'; /** * This model class represents all parameters needed to request information about a certain search request diff --git a/src/app/shared/search/search-query-response.model.ts b/src/app/shared/search/models/search-query-response.model.ts similarity index 87% rename from src/app/shared/search/search-query-response.model.ts rename to src/app/shared/search/models/search-query-response.model.ts index 77e513a918..a436a7c045 100644 --- a/src/app/shared/search/search-query-response.model.ts +++ b/src/app/shared/search/models/search-query-response.model.ts @@ -1,6 +1,6 @@ import { autoserialize } from 'cerialize'; -import { PageInfo } from '../../core/shared/page-info.model'; -import { PaginatedList } from '../../core/data/paginated-list.model'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; /** * Class representing the response returned by the server when performing a search request diff --git a/src/app/shared/search/search-result.model.ts b/src/app/shared/search/models/search-result.model.ts similarity index 59% rename from src/app/shared/search/search-result.model.ts rename to src/app/shared/search/models/search-result.model.ts index c221b2e581..6079c4af63 100644 --- a/src/app/shared/search/search-result.model.ts +++ b/src/app/shared/search/models/search-result.model.ts @@ -1,13 +1,13 @@ import { autoserialize, deserialize } from 'cerialize'; -import { typedObject } from '../../core/cache/builders/build-decorators'; -import { DSpaceObject } from '../../core/shared/dspace-object.model'; -import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { HALLink } from '../../core/shared/hal-link.model'; -import { MetadataMap } from '../../core/shared/metadata.models'; -import { excludeFromEquals, fieldsForEquals } from '../../core/utilities/equals.decorators'; -import { ListableObject } from '../object-collection/shared/listable-object.model'; -import { HALResource } from '../../core/shared/hal-resource.model'; -import { SEARCH_RESULT } from './search-result.resource-type'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { GenericConstructor } from '../../../core/shared/generic-constructor'; +import { HALLink } from '../../../core/shared/hal-link.model'; +import { MetadataMap } from '../../../core/shared/metadata.models'; +import { excludeFromEquals, fieldsForEquals } from '../../../core/utilities/equals.decorators'; +import { ListableObject } from '../../object-collection/shared/listable-object.model'; +import { HALResource } from '../../../core/shared/hal-resource.model'; +import { SEARCH_RESULT } from './types/search-result.resource-type'; /** * Represents a search result object of a certain () DSpaceObject diff --git a/src/app/shared/search/facet-config-response.resouce-type.ts b/src/app/shared/search/models/types/facet-config-response.resouce-type.ts similarity index 75% rename from src/app/shared/search/facet-config-response.resouce-type.ts rename to src/app/shared/search/models/types/facet-config-response.resouce-type.ts index 7c3bdef7ca..83f2ca0e66 100644 --- a/src/app/shared/search/facet-config-response.resouce-type.ts +++ b/src/app/shared/search/models/types/facet-config-response.resouce-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../core/shared/resource-type'; +import { ResourceType } from '../../../../core/shared/resource-type'; /** * The resource type for FacetConfigResponse diff --git a/src/app/shared/search/facet-values.resource-type.ts b/src/app/shared/search/models/types/facet-values.resource-type.ts similarity index 74% rename from src/app/shared/search/facet-values.resource-type.ts rename to src/app/shared/search/models/types/facet-values.resource-type.ts index e1042bbbc0..c9d6c69c2e 100644 --- a/src/app/shared/search/facet-values.resource-type.ts +++ b/src/app/shared/search/models/types/facet-values.resource-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../core/shared/resource-type'; +import { ResourceType } from '../../../../core/shared/resource-type'; /** * The resource type for FacetValues diff --git a/src/app/shared/search/search-filter-config.resource-type.ts b/src/app/shared/search/models/types/search-filter-config.resource-type.ts similarity index 75% rename from src/app/shared/search/search-filter-config.resource-type.ts rename to src/app/shared/search/models/types/search-filter-config.resource-type.ts index c2da308207..eb7521b8df 100644 --- a/src/app/shared/search/search-filter-config.resource-type.ts +++ b/src/app/shared/search/models/types/search-filter-config.resource-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../core/shared/resource-type'; +import { ResourceType } from '../../../../core/shared/resource-type'; /** * The resource type for SearchFilterConfig diff --git a/src/app/shared/search/search-objects.resource-type.ts b/src/app/shared/search/models/types/search-objects.resource-type.ts similarity index 74% rename from src/app/shared/search/search-objects.resource-type.ts rename to src/app/shared/search/models/types/search-objects.resource-type.ts index cf5d7c9c9e..650b02f005 100644 --- a/src/app/shared/search/search-objects.resource-type.ts +++ b/src/app/shared/search/models/types/search-objects.resource-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../core/shared/resource-type'; +import { ResourceType } from '../../../../core/shared/resource-type'; /** * The resource type for SearchObjects diff --git a/src/app/shared/search/search-result.resource-type.ts b/src/app/shared/search/models/types/search-result.resource-type.ts similarity index 73% rename from src/app/shared/search/search-result.resource-type.ts rename to src/app/shared/search/models/types/search-result.resource-type.ts index 4e18f17db6..354c1898c4 100644 --- a/src/app/shared/search/search-result.resource-type.ts +++ b/src/app/shared/search/models/types/search-result.resource-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../core/shared/resource-type'; +import { ResourceType } from '../../../../core/shared/resource-type'; /** * The resource type for SearchResult diff --git a/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts index 7a20fd5ff0..6dcb47ce9f 100644 --- a/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { FilterType } from '../../../filter-type.model'; +import { FilterType } from '../../../models/filter-type.model'; import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; import { renderFacetFor } from '../search-filter-type-decorator'; diff --git a/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.ts index 49f361e75e..c9da8500d0 100644 --- a/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.ts @@ -1,10 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { FilterType } from '../../../filter-type.model'; +import { FilterType } from '../../../models/filter-type.model'; import { renderFacetFor } from '../search-filter-type-decorator'; -import { - facetLoad, - SearchFacetFilterComponent -} from '../search-facet-filter/search-facet-filter.component'; +import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; @Component({ selector: 'ds-search-boolean-filter', 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..b0f2a51460 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 @@ -11,13 +11,11 @@ import { SearchFilterService } from '../../../../../../core/shared/search/search import { SearchService } from '../../../../../../core/shared/search/search.service'; import { RouterStub } from '../../../../../testing/router.stub'; import { SearchServiceStub } from '../../../../../testing/search-service.stub'; -import { FacetValue } from '../../../../facet-value.model'; -import { FilterType } from '../../../../filter-type.model'; -import { SearchFilterConfig } from '../../../../search-filter-config.model'; +import { FacetValue } from '../../../../models/facet-value.model'; +import { FilterType } from '../../../../models/filter-type.model'; +import { SearchFilterConfig } from '../../../../models/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'; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts index 11056a232c..cdbab61a30 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts @@ -2,8 +2,8 @@ import { combineLatest as observableCombineLatest, Observable, Subscription } fr import { map } from 'rxjs/operators'; import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { FacetValue } from '../../../../facet-value.model'; -import { SearchFilterConfig } from '../../../../search-filter-config.model'; +import { FacetValue } from '../../../../models/facet-value.model'; +import { SearchFilterConfig } from '../../../../models/search-filter-config.model'; import { SearchService } from '../../../../../../core/shared/search/search.service'; import { SearchFilterService } from '../../../../../../core/shared/search/search-filter.service'; import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; 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..e587913491 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 @@ -2,9 +2,9 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { SearchFilterConfig } from '../../../../search-filter-config.model'; -import { FilterType } from '../../../../filter-type.model'; -import { FacetValue } from '../../../../facet-value.model'; +import { SearchFilterConfig } from '../../../../models/search-filter-config.model'; +import { FilterType } from '../../../../models/filter-type.model'; +import { FacetValue } from '../../../../models/facet-value.model'; import { FormsModule } from '@angular/forms'; import { of as observableOf } from 'rxjs'; import { SearchService } from '../../../../../../core/shared/search/search.service'; @@ -20,8 +20,6 @@ import { RANGE_FILTER_MIN_SUFFIX } 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'; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts index 3d8215b210..b08a54e42b 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts @@ -2,8 +2,8 @@ import { Observable, Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { FacetValue } from '../../../../facet-value.model'; -import { SearchFilterConfig } from '../../../../search-filter-config.model'; +import { FacetValue } from '../../../../models/facet-value.model'; +import { SearchFilterConfig } from '../../../../models/search-filter-config.model'; import { SearchService } from '../../../../../../core/shared/search/search.service'; import { SearchFilterService } from '../../../../../../core/shared/search/search-filter.service'; import { 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..54c447aa61 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 @@ -10,13 +10,11 @@ import { SearchFilterService } from '../../../../../../core/shared/search/search import { SearchService } from '../../../../../../core/shared/search/search.service'; import { RouterStub } from '../../../../../testing/router.stub'; import { SearchServiceStub } from '../../../../../testing/search-service.stub'; -import { FacetValue } from '../../../../facet-value.model'; -import { FilterType } from '../../../../filter-type.model'; -import { SearchFilterConfig } from '../../../../search-filter-config.model'; +import { FacetValue } from '../../../../models/facet-value.model'; +import { FilterType } from '../../../../models/filter-type.model'; +import { SearchFilterConfig } from '../../../../models/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'; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts index d92455fdd9..4566b882eb 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts @@ -1,12 +1,12 @@ import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { SearchFilterConfig } from '../../../../search-filter-config.model'; +import { SearchFilterConfig } from '../../../../models/search-filter-config.model'; import { SearchService } from '../../../../../../core/shared/search/search.service'; import { SearchFilterService } from '../../../../../../core/shared/search/search-filter.service'; import { hasValue } from '../../../../../empty.util'; import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; -import { FacetValue } from '../../../../facet-value.model'; +import { FacetValue } from '../../../../models/facet-value.model'; import { currentPath } from '../../../../../utils/route.utils'; import { getFacetValueForType } from '../../../../search.utils'; import { PaginationService } from '../../../../../../core/pagination/pagination.service'; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts index 6674b090a7..3f83c766fe 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts @@ -1,7 +1,7 @@ import { Component, Injector, Input, OnInit } from '@angular/core'; import { renderFilterType } from '../search-filter-type-decorator'; -import { FilterType } from '../../../filter-type.model'; -import { SearchFilterConfig } from '../../../search-filter-config.model'; +import { FilterType } from '../../../models/filter-type.model'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { FILTER_CONFIG, IN_PLACE_SEARCH } from '../../../../../core/shared/search/search-filter.service'; import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts index dc58a8c853..919dfc0889 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts @@ -7,9 +7,9 @@ import { IN_PLACE_SEARCH, SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; -import { SearchFilterConfig } from '../../../search-filter-config.model'; -import { FilterType } from '../../../filter-type.model'; -import { FacetValue } from '../../../facet-value.model'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; +import { FilterType } from '../../../models/filter-type.model'; +import { FacetValue } from '../../../models/facet-value.model'; import { FormsModule } from '@angular/forms'; import { of as observableOf } from 'rxjs'; import { SearchService } from '../../../../../core/shared/search/search.service'; diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 0b28929554..9b898126f0 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -15,14 +15,18 @@ import { PaginatedList } from '../../../../../core/data/paginated-list.model'; import { RemoteData } from '../../../../../core/data/remote-data'; import { hasNoValue, hasValue, isNotEmpty } from '../../../../empty.util'; import { EmphasizePipe } from '../../../../utils/emphasize.pipe'; -import { FacetValue } from '../../../facet-value.model'; -import { SearchFilterConfig } from '../../../search-filter-config.model'; +import { FacetValue } from '../../../models/facet-value.model'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { SearchService } from '../../../../../core/shared/search/search.service'; -import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; +import { + FILTER_CONFIG, + IN_PLACE_SEARCH, + SearchFilterService +} from '../../../../../core/shared/search/search-filter.service'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; import { getFirstSucceededRemoteData } from '../../../../../core/shared/operators'; import { InputSuggestion } from '../../../../input-suggestions/input-suggestions.model'; -import { SearchOptions } from '../../../search-options.model'; +import { SearchOptions } from '../../../models/search-options.model'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; import { currentPath } from '../../../../utils/route.utils'; import { getFacetValueForType, stripOperatorFromFilterValue } from '../../../search.utils'; diff --git a/src/app/shared/search/search-filters/search-filter/search-filter-type-decorator.ts b/src/app/shared/search/search-filters/search-filter/search-filter-type-decorator.ts index 40cff30d41..7c7d721f2e 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter-type-decorator.ts +++ b/src/app/shared/search/search-filters/search-filter/search-filter-type-decorator.ts @@ -1,5 +1,4 @@ - -import { FilterType } from '../../filter-type.model'; +import { FilterType } from '../../models/filter-type.model'; /** * Contains the mapping between a facet component and a FilterType diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.actions.ts b/src/app/shared/search/search-filters/search-filter/search-filter.actions.ts index 59a1903eb8..ce1df556c9 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter.actions.ts +++ b/src/app/shared/search/search-filters/search-filter/search-filter.actions.ts @@ -1,7 +1,7 @@ import { Action } from '@ngrx/store'; import { type } from '../../../ngrx/type'; -import { SearchFilterConfig } from '../../search-filter-config.model'; +import { SearchFilterConfig } from '../../models/search-filter-config.model'; /** * For each action type in an action group, make a simple 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 ee337a503b..1be4c0f2cb 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 @@ -8,8 +8,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { SearchFilterService } from '../../../../core/shared/search/search-filter.service'; import { SearchService } from '../../../../core/shared/search/search.service'; import { SearchFilterComponent } from './search-filter.component'; -import { SearchFilterConfig } from '../../search-filter-config.model'; -import { FilterType } from '../../filter-type.model'; +import { SearchFilterConfig } from '../../models/search-filter-config.model'; +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'; diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts index 8bc7502159..1897169a2e 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts @@ -3,7 +3,7 @@ import { Component, Inject, Input, OnInit } from '@angular/core'; import { Observable, of as observableOf } from 'rxjs'; import { filter, map, startWith, switchMap, take } from 'rxjs/operators'; -import { SearchFilterConfig } from '../../search-filter-config.model'; +import { SearchFilterConfig } from '../../models/search-filter-config.model'; import { SearchFilterService } from '../../../../core/shared/search/search-filter.service'; import { slide } from '../../../animations/slide'; import { isNotEmpty } from '../../../empty.util'; diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts index 3b16f84ea5..d2f3de2dc3 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts @@ -1,10 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { FilterType } from '../../../filter-type.model'; +import { FilterType } from '../../../models/filter-type.model'; import { renderFacetFor } from '../search-filter-type-decorator'; -import { - facetLoad, - SearchFacetFilterComponent -} from '../search-facet-filter/search-facet-filter.component'; +import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; @Component({ selector: 'ds-search-hierarchy-filter', diff --git a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts index 41f0b4307d..3bdd833c51 100644 --- a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts @@ -7,9 +7,9 @@ import { IN_PLACE_SEARCH, SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; -import { SearchFilterConfig } from '../../../search-filter-config.model'; -import { FilterType } from '../../../filter-type.model'; -import { FacetValue } from '../../../facet-value.model'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; +import { FilterType } from '../../../models/filter-type.model'; +import { FacetValue } from '../../../models/facet-value.model'; import { FormsModule } from '@angular/forms'; import { of as observableOf } from 'rxjs'; import { SearchService } from '../../../../../core/shared/search/search.service'; diff --git a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts index 702854dee5..a6b33ddf88 100644 --- a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts @@ -3,11 +3,15 @@ import { map, startWith } from 'rxjs/operators'; import { isPlatformBrowser } from '@angular/common'; import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; -import { FilterType } from '../../../filter-type.model'; +import { FilterType } from '../../../models/filter-type.model'; import { renderFacetFor } from '../search-filter-type-decorator'; import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; -import { SearchFilterConfig } from '../../../search-filter-config.model'; -import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; +import { + FILTER_CONFIG, + IN_PLACE_SEARCH, + SearchFilterService +} from '../../../../../core/shared/search/search-filter.service'; import { SearchService } from '../../../../../core/shared/search/search.service'; import { Router } from '@angular/router'; import * as moment from 'moment'; diff --git a/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.ts index b2b44ce219..cfd81c3750 100644 --- a/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.ts @@ -1,13 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { FilterType } from '../../../filter-type.model'; -import { - facetLoad, - SearchFacetFilterComponent -} from '../search-facet-filter/search-facet-filter.component'; +import { FilterType } from '../../../models/filter-type.model'; +import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; import { renderFacetFor } from '../search-filter-type-decorator'; -import { - addOperatorToFilterValue, -} from '../../../search.utils'; +import { addOperatorToFilterValue, } from '../../../search.utils'; /** * This component renders a simple item page. diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index 0556da4426..8b870f6467 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -5,7 +5,7 @@ import { map, switchMap } from 'rxjs/operators'; import { SearchService } from '../../../core/shared/search/search.service'; import { RemoteData } from '../../../core/data/remote-data'; -import { SearchFilterConfig } from '../search-filter-config.model'; +import { SearchFilterConfig } from '../models/search-filter-config.model'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; import { getFirstSucceededRemoteData } from '../../../core/shared/operators'; diff --git a/src/app/shared/search/search-results/search-results.component.ts b/src/app/shared/search/search-results/search-results.component.ts index 9e466fd620..b13771656e 100644 --- a/src/app/shared/search/search-results/search-results.component.ts +++ b/src/app/shared/search/search-results/search-results.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { RemoteData } from '../../../core/data/remote-data'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { fadeIn, fadeInOut } from '../../animations/fade'; -import { SearchResult } from '../search-result.model'; +import { SearchResult } from '../models/search-result.model'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { hasNoValue, isNotEmpty } from '../../empty.util'; import { SortOptions } from '../../../core/cache/models/sort-options.model'; @@ -10,7 +10,7 @@ import { ListableObject } from '../../object-collection/shared/listable-object.m import { CollectionElementLinkType } from '../../object-collection/collection-element-link.type'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { Context } from '../../../core/shared/context.model'; -import { PaginatedSearchOptions } from '../paginated-search-options.model'; +import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; @Component({ selector: 'ds-search-results', diff --git a/src/app/shared/search/search-settings/search-settings.component.ts b/src/app/shared/search/search-settings/search-settings.component.ts index 9f6cab4a7b..c97410fa5f 100644 --- a/src/app/shared/search/search-settings/search-settings.component.ts +++ b/src/app/shared/search/search-settings/search-settings.component.ts @@ -2,7 +2,7 @@ import { Component, Inject, Input } from '@angular/core'; import { SearchService } from '../../../core/shared/search/search.service'; import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; import { ActivatedRoute, Router } from '@angular/router'; -import { PaginatedSearchOptions } from '../paginated-search-options.model'; +import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; import { PaginationService } from '../../../core/pagination/pagination.service'; diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index 7f134cacd3..1d858f579d 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model'; import { Observable } from 'rxjs'; -import { PaginatedSearchOptions } from '../paginated-search-options.model'; +import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; import { SortOptions } from '../../../core/cache/models/sort-options.model'; /** diff --git a/src/app/search-page/search.component.html b/src/app/shared/search/search.component.html similarity index 89% rename from src/app/search-page/search.component.html rename to src/app/shared/search/search.component.html index 3489cccdfb..7886d7190e 100644 --- a/src/app/search-page/search.component.html +++ b/src/app/shared/search/search.component.html @@ -5,7 +5,16 @@
- + +
+ +
+ + + + + +
@@ -26,7 +35,7 @@ [context]="context">
-
+ ; diff --git a/src/app/search-page/search.component.ts b/src/app/shared/search/search.component.ts similarity index 77% rename from src/app/search-page/search.component.ts rename to src/app/shared/search/search.component.ts index 2972175bdf..0fb7ca358a 100644 --- a/src/app/search-page/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -1,26 +1,26 @@ import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { startWith, switchMap } from 'rxjs/operators'; -import { PaginatedList } from '../core/data/paginated-list.model'; -import { RemoteData } from '../core/data/remote-data'; -import { DSpaceObject } from '../core/shared/dspace-object.model'; -import { pushInOut } from '../shared/animations/push'; -import { HostWindowService } from '../shared/host-window.service'; -import { SidebarService } from '../shared/sidebar/sidebar.service'; -import { hasValue, isEmpty } from '../shared/empty.util'; -import { getFirstCompletedRemoteData } from '../core/shared/operators'; -import { RouteService } from '../core/services/route.service'; -import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component'; -import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model'; -import { SearchResult } from '../shared/search/search-result.model'; -import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; -import { SearchService } from '../core/shared/search/search.service'; -import { currentPath } from '../shared/utils/route.utils'; +import { PaginatedList } from '../../core/data/paginated-list.model'; +import { RemoteData } from '../../core/data/remote-data'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { pushInOut } from '../animations/push'; +import { HostWindowService } from '../host-window.service'; +import { SidebarService } from '../sidebar/sidebar.service'; +import { hasValue, isEmpty } from '../empty.util'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { RouteService } from '../../core/services/route.service'; +import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; +import { PaginatedSearchOptions } from './models/paginated-search-options.model'; +import { SearchResult } from './models/search-result.model'; +import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; +import { SearchService } from '../../core/shared/search/search.service'; +import { currentPath } from '../utils/route.utils'; import { Router } from '@angular/router'; -import { Context } from '../core/shared/context.model'; -import { SortOptions } from '../core/cache/models/sort-options.model'; -import { followLink } from '../shared/utils/follow-link-config.model'; -import { Item } from '../core/shared/item.model'; +import { Context } from '../../core/shared/context.model'; +import { SortOptions } from '../../core/cache/models/sort-options.model'; +import { followLink } from '../utils/follow-link-config.model'; +import { Item } from '../../core/shared/item.model'; @Component({ selector: 'ds-search', @@ -104,6 +104,10 @@ export class SearchComponent implements OnInit { */ isSidebarCollapsed$: Observable; + /** + * A boolean representing if show search sidebar button + */ + @Input() showSidebar = true; constructor(protected service: SearchService, protected sidebarService: SidebarService, protected windowService: HostWindowService, diff --git a/src/app/shared/search/search.module.ts b/src/app/shared/search/search.module.ts index d5816d2a1b..668d260c23 100644 --- a/src/app/shared/search/search.module.ts +++ b/src/app/shared/search/search.module.ts @@ -19,16 +19,18 @@ import { SearchSidebarComponent } from './search-sidebar/search-sidebar.componen import { SearchSettingsComponent } from './search-settings/search-settings.component'; import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component'; import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component'; -import { SearchObjects } from './search-objects.model'; -import { FacetConfigResponse } from './facet-config-response.model'; -import { FacetValues } from './facet-values.model'; -import { SearchResult } from './search-result.model'; +import { SearchObjects } from './models/search-objects.model'; +import { FacetConfigResponse } from './models/facet-config-response.model'; +import { FacetValues } from './models/facet-values.model'; +import { SearchResult } from './models/search-result.model'; import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core'; import { MissingTranslationHelper } from '../translate/missing-translation.helper'; import { SharedModule } from '../shared.module'; import { SearchResultsComponent } from './search-results/search-results.component'; +import { SearchComponent } from './search.component'; const COMPONENTS = [ + SearchComponent, SearchResultsComponent, SearchSidebarComponent, SearchSettingsComponent, diff --git a/src/app/shared/search/search.utils.spec.ts b/src/app/shared/search/search.utils.spec.ts index ca4b4894b1..68ecd87e2a 100644 --- a/src/app/shared/search/search.utils.spec.ts +++ b/src/app/shared/search/search.utils.spec.ts @@ -1,5 +1,5 @@ -import { FacetValue } from './facet-value.model'; -import { SearchFilterConfig } from './search-filter-config.model'; +import { FacetValue } from './models/facet-value.model'; +import { SearchFilterConfig } from './models/search-filter-config.model'; import { addOperatorToFilterValue, escapeRegExp, diff --git a/src/app/shared/search/search.utils.ts b/src/app/shared/search/search.utils.ts index 38c5f568cb..ed81ce46ae 100644 --- a/src/app/shared/search/search.utils.ts +++ b/src/app/shared/search/search.utils.ts @@ -1,5 +1,5 @@ -import { FacetValue } from './facet-value.model'; -import { SearchFilterConfig } from './search-filter-config.model'; +import { FacetValue } from './models/facet-value.model'; +import { SearchFilterConfig } from './models/search-filter-config.model'; import { isNotEmpty } from '../empty.util'; /** diff --git a/src/app/statistics/statistics.service.spec.ts b/src/app/statistics/statistics.service.spec.ts index cdf81afdb6..e4ca6a7059 100644 --- a/src/app/statistics/statistics.service.spec.ts +++ b/src/app/statistics/statistics.service.spec.ts @@ -5,7 +5,7 @@ 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 { SearchOptions } from '../shared/search/models/search-options.model'; describe('StatisticsService', () => { let service: StatisticsService; diff --git a/src/app/statistics/statistics.service.ts b/src/app/statistics/statistics.service.ts index 9e12e627b5..81c79c4c43 100644 --- a/src/app/statistics/statistics.service.ts +++ b/src/app/statistics/statistics.service.ts @@ -6,7 +6,7 @@ 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 { SearchOptions } from '../shared/search/models/search-options.model'; /** * The statistics service diff --git a/src/app/submission/import-external/submission-import-external.component.spec.ts b/src/app/submission/import-external/submission-import-external.component.spec.ts index 20f36995a6..dc53b2e45f 100644 --- a/src/app/submission/import-external/submission-import-external.component.spec.ts +++ b/src/app/submission/import-external/submission-import-external.component.spec.ts @@ -17,7 +17,7 @@ import { createPaginatedList, createTestComponent } from '../../shared/testing/u import { RouterStub } from '../../shared/testing/router.stub'; import { VarDirective } from '../../shared/utils/var.directive'; import { routeServiceStub } from '../../shared/testing/route-service.stub'; -import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { ExternalSourceEntry } from '../../core/shared/external-source-entry.model'; diff --git a/src/themes/custom/app/search-page/configuration-search-page.component.ts b/src/themes/custom/app/search-page/configuration-search-page.component.ts index b578ed5d8c..159434c7a8 100644 --- a/src/themes/custom/app/search-page/configuration-search-page.component.ts +++ b/src/themes/custom/app/search-page/configuration-search-page.component.ts @@ -1,4 +1,4 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { pushInOut } from '../../../../app/shared/animations/push'; import { SEARCH_CONFIG_SERVICE } from '../../../../app/my-dspace-page/my-dspace-page.component'; import { SearchConfigurationService } from '../../../../app/core/shared/search/search-configuration.service'; @@ -7,9 +7,9 @@ import { ConfigurationSearchPageComponent as BaseComponent } from '../../../../a @Component({ selector: 'ds-configuration-search-page', // styleUrls: ['./configuration-search-page.component.scss'], - styleUrls: ['../../../../app/search-page/search.component.scss'], + styleUrls: ['../../../../app/shared/search/search.component.scss'], // templateUrl: './configuration-search-page.component.html' - templateUrl: '../../../../app/search-page/search.component.html', + templateUrl: '../../../../app/shared/search/search.component.html', changeDetection: ChangeDetectionStrategy.OnPush, animations: [pushInOut], providers: [ From 4c46d9db2b6000c5091929e3bb714887b1fb0a8f Mon Sep 17 00:00:00 2001 From: William Welling Date: Fri, 17 Dec 2021 08:14:15 -0600 Subject: [PATCH 042/102] refactor environment.prod.ts to environment.production.ts --- README.md | 2 +- angular.json | 4 ++-- src/config/config.server.ts | 2 +- src/config/config.util.spec.ts | 2 +- .../{environment.prod.ts => environment.production.ts} | 0 src/environments/environment.test.ts | 2 +- src/environments/environment.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename src/environments/{environment.prod.ts => environment.production.ts} (100%) diff --git a/README.md b/README.md index b4ae6c884b..829f7121b2 100644 --- a/README.md +++ b/README.md @@ -384,7 +384,7 @@ dspace-angular │ ├── backend * Folder containing a mock of the REST API, hosted by the express server │ ├── config * │ ├── environments * -│ │ ├── environment.prod.ts * Production configuration files +│ │ ├── environment.production.ts * Production configuration files │ │ ├── environment.test.ts * Test configuration files │ │ └── environment.ts * Default (development) configuration files │ ├── mirador-viewer * diff --git a/angular.json b/angular.json index b9b8cb41cc..a0a4cd8ea1 100644 --- a/angular.json +++ b/angular.json @@ -71,7 +71,7 @@ "fileReplacements": [ { "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" + "with": "src/environments/environment.production.ts" } ], "optimization": true, @@ -203,7 +203,7 @@ "fileReplacements": [ { "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" + "with": "src/environments/environment.production.ts" } ] } diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 36d7d1f9d8..ade4e90591 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -178,7 +178,7 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => { console.warn(`Unable to find dist config file at ${localConfigPath}`); } - // override with external config if specified by environment variable `APP_CONFIG_PATH` + // override with external config if specified by environment variable `DSPACE_APP_CONFIG_PATH` const externalConfigPath = ENV('APP_CONFIG_PATH', true); if (isNotEmpty(externalConfigPath)) { if (fs.existsSync(externalConfigPath)) { diff --git a/src/config/config.util.spec.ts b/src/config/config.util.spec.ts index f9989f5a83..7896b1f5c2 100644 --- a/src/config/config.util.spec.ts +++ b/src/config/config.util.spec.ts @@ -1,4 +1,4 @@ -import { environment } from '../environments/environment.prod'; +import { environment } from '../environments/environment.production'; import { extendEnvironmentWithAppConfig } from './config.util'; import { DefaultAppConfig } from './default-app-config'; import { HandleThemeConfig } from './theme.model'; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.production.ts similarity index 100% rename from src/environments/environment.prod.ts rename to src/environments/environment.production.ts diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 0af2fc8546..0cf069bd56 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -1,4 +1,4 @@ -// This configuration is only used for unit tests, end-to-end tests use environment.prod.ts +// This configuration is only used for unit tests, end-to-end tests use environment.production.ts import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 29b67704eb..90eecac900 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,5 +1,5 @@ // This file can be replaced during build by using the `fileReplacements` array. -// `ng build --configuration production` replaces `environment.ts` with `environment.prod.ts`. +// `ng build --configuration production` replaces `environment.ts` with `environment.production.ts`. // `ng test --configuration test` replaces `environment.ts` with `environment.test.ts`. // The list of file replacements can be found in `angular.json`. From ef18308893e66ca119cdaf2316f0958ed69ea062 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 17 Dec 2021 19:35:28 +0100 Subject: [PATCH 043/102] [CST-4633] Refactoring of search.component in order to have all functionality used during the different search components --- .../search/search-configuration.service.ts | 219 +++++++------- .../search-navbar/search-navbar.component.ts | 12 +- src/app/search-page/search-page.component.ts | 8 + .../search-form/search-form.component.ts | 3 - .../search-results.component.html | 6 +- .../search-results.component.ts | 7 + .../search-settings.component.html | 20 +- .../search-settings.component.spec.ts | 6 +- .../search-settings.component.ts | 8 +- .../search-sidebar.component.html | 8 +- .../search-sidebar.component.ts | 17 +- .../search-configuration-option.model.ts | 7 + ...search-switch-configuration.component.html | 2 +- ...rch-switch-configuration.component.spec.ts | 30 +- .../search-switch-configuration.component.ts | 25 +- src/app/shared/search/search.component.html | 22 +- .../shared/search/search.component.spec.ts | 60 ++-- src/app/shared/search/search.component.ts | 274 ++++++++++++------ 18 files changed, 450 insertions(+), 284 deletions(-) diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index 3b162a60b9..03e46cac65 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -3,14 +3,12 @@ import { ActivatedRoute, Params } from '@angular/router'; import { BehaviorSubject, - combineLatest, combineLatest as observableCombineLatest, merge as observableMerge, Observable, - of, Subscription } from 'rxjs'; -import { distinctUntilChanged, filter, map, startWith, switchMap, take } from 'rxjs/operators'; +import { filter, map, startWith } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SearchOptions } from '../../../shared/search/models/search-options.model'; import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; @@ -22,7 +20,7 @@ import { RouteService } from '../../services/route.service'; import { getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData } from '../operators'; import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; -import { SearchConfig } from './search-filters/search-config.model'; +import { SearchConfig, SortOption } from './search-filters/search-config.model'; import { SearchService } from './search.service'; import { PaginationService } from '../../pagination/pagination.service'; @@ -33,6 +31,14 @@ import { PaginationService } from '../../pagination/pagination.service'; export class SearchConfigurationService implements OnDestroy { public paginationID = 'spc'; + /** + * Emits the current search options + */ + public searchOptions: BehaviorSubject; + /** + * Emits the current search options including pagination and sort + */ + public paginatedSearchOptions: BehaviorSubject; /** * Default pagination settings */ @@ -41,50 +47,23 @@ export class SearchConfigurationService implements OnDestroy { pageSize: 10, currentPage: 1 }); - - /** - * Default sort settings - */ - protected defaultSort = new SortOptions('score', SortDirection.DESC); - - /** - * Default configuration parameter setting - */ - protected defaultConfiguration; - /** * Default scope setting */ protected defaultScope = ''; - /** * Default query setting */ protected defaultQuery = ''; - /** - * Emits the current default values + * A map of subscriptions to unsubscribe from on destroy */ - protected _defaults: Observable>; - - /** - * Emits the current search options - */ - public searchOptions: BehaviorSubject; - - /** - * Emits the current search options including pagination and sort - */ - public paginatedSearchOptions: BehaviorSubject; - - /** - * List of subscriptions to unsubscribe from on destroy - */ - protected subs: Subscription[] = []; + protected subs: Map = new Map(null); /** * Initialize the search options * @param {RouteService} routeService + * @param {PaginationService} paginationService * @param {ActivatedRoute} route */ constructor(protected routeService: RouteService, @@ -95,19 +74,23 @@ export class SearchConfigurationService implements OnDestroy { } /** - * Initialize the search options + * Emits the current default values */ - protected initDefaults() { - this.defaults - .pipe(getFirstSucceededRemoteData()) - .subscribe((defRD: RemoteData) => { - const defs = defRD.payload; - this.paginatedSearchOptions = new BehaviorSubject(defs); - this.searchOptions = new BehaviorSubject(defs); - this.subs.push(this.subscribeToSearchOptions(defs)); - this.subs.push(this.subscribeToPaginatedSearchOptions(defs.pagination.id, defs)); - } - ); + protected _defaults: Observable>; + + /** + * Default values for the Search Options + */ + get defaults(): Observable> { + if (hasNoValue(this._defaults)) { + const options = new PaginatedSearchOptions({ + pagination: this.defaultPagination, + scope: this.defaultScope, + query: this.defaultQuery + }); + this._defaults = createSuccessfulRemoteDataObject$(options, new Date().getTime()); + } + return this._defaults; } /** @@ -205,59 +188,82 @@ export class SearchConfigurationService implements OnDestroy { } /** - * Creates an observable of SearchConfig every time the configuration$ stream emits. - * @param configuration$ - * @param service + * Creates an observable of SearchConfig every time the configuration stream emits. + * @param configuration The search configuration + * @param service The serach service to use + * @param scope The search scope if exists */ - getConfigurationSearchConfigObservable(configuration$: Observable, service: SearchService): Observable { - return configuration$.pipe( - distinctUntilChanged(), - switchMap((configuration) => service.getSearchConfigurationFor(null, configuration)), - getAllSucceededRemoteDataPayload()); + getConfigurationSearchConfig(configuration: string, service: SearchService, scope?: string): Observable { + return service.getSearchConfigurationFor(scope, configuration).pipe( + getAllSucceededRemoteDataPayload() + ); } /** - * Every time searchConfig change (after a configuration change) it update the navigation with the default sort option - * and emit the new paginateSearchOptions value. - * @param configuration$ - * @param service + * Return the SortOptions list available for the given SearchConfig + * @param searchConfig The SearchConfig object */ - initializeSortOptionsFromConfiguration(searchConfig$: Observable) { - const subscription = searchConfig$.pipe(switchMap((searchConfig) => combineLatest([ - of(searchConfig), - this.paginatedSearchOptions.pipe(take(1)) - ]))).subscribe(([searchConfig, searchOptions]) => { - const field = searchConfig.sortOptions[0].name; - const direction = searchConfig.sortOptions[0].sortOrder.toLowerCase() === SortDirection.ASC.toLowerCase() ? SortDirection.ASC : SortDirection.DESC; - const updateValue = Object.assign(new PaginatedSearchOptions({}), searchOptions, { - sort: new SortOptions(field, direction) - }); - this.paginationService.updateRoute(this.paginationID, - { - sortDirection: updateValue.sort.direction, - sortField: updateValue.sort.field, - }); - this.paginatedSearchOptions.next(updateValue); - }); - this.subs.push(subscription); - } - - /** - * Creates an observable of available SortOptions[] every time the searchConfig$ stream emits. - * @param searchConfig$ - * @param service - */ - getConfigurationSortOptionsObservable(searchConfig$: Observable): Observable { - return searchConfig$.pipe(map((searchConfig) => { - const sortOptions = []; - searchConfig.sortOptions.forEach(sortOption => { - sortOptions.push(new SortOptions(sortOption.name, SortDirection.ASC)); - sortOptions.push(new SortOptions(sortOption.name, SortDirection.DESC)); - }); - return sortOptions; + getConfigurationSortOptions(searchConfig: SearchConfig): SortOptions[] { + return searchConfig.sortOptions.map((entry: SortOption) => ({ + field: entry.name, + direction: entry.sortOrder.toLowerCase() === SortDirection.ASC.toLowerCase() ? SortDirection.ASC : SortDirection.DESC })); } + setPaginationId(paginationId): void { + if (isNotEmpty(paginationId)) { + const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue(); + const updatedValue: PaginatedSearchOptions = Object.assign(new PaginatedSearchOptions({}), currentValue, { + pagination: Object.assign({}, currentValue.pagination, { + id: paginationId + }) + }); + // unsubscribe from subscription related to old pagination id + this.unsubscribeFromSearchOptions(this.paginationID); + + // change to the new pagination id + this.paginationID = paginationId; + this.paginatedSearchOptions.next(updatedValue); + this.setSearchSubscription(this.paginationID, this.paginatedSearchOptions.value); + } + } + + /** + * Make sure to unsubscribe from all existing subscription to prevent memory leaks + */ + ngOnDestroy(): void { + this.subs + .forEach((subs: Subscription[]) => subs + .filter((sub) => hasValue(sub)) + .forEach((sub) => sub.unsubscribe()) + ); + + this.subs = new Map(null); + } + + /** + * Initialize the search options + */ + protected initDefaults() { + this.defaults + .pipe(getFirstSucceededRemoteData()) + .subscribe((defRD: RemoteData) => { + const defs = defRD.payload; + this.paginatedSearchOptions = new BehaviorSubject(defs); + this.searchOptions = new BehaviorSubject(defs); + this.setSearchSubscription(this.paginationID, defs); + }); + } + + private setSearchSubscription(paginationID: string, defaults: PaginatedSearchOptions) { + this.unsubscribeFromSearchOptions(paginationID); + const subs = [ + this.subscribeToSearchOptions(defaults), + this.subscribeToPaginatedSearchOptions(paginationID || defaults.pagination.id, defaults) + ]; + this.subs.set(this.paginationID, subs); + } + /** * Sets up a subscription to all necessary parameters to make sure the searchOptions emits a new value every time they update * @param {SearchOptions} defaults Default values for when no parameters are available @@ -280,6 +286,7 @@ export class SearchConfigurationService implements OnDestroy { /** * Sets up a subscription to all necessary parameters to make sure the paginatedSearchOptions emits a new value every time they update + * @param {string} paginationId The pagination ID * @param {PaginatedSearchOptions} defaults Default values for when no parameters are available * @returns {Subscription} The subscription to unsubscribe from */ @@ -301,30 +308,16 @@ export class SearchConfigurationService implements OnDestroy { } /** - * Default values for the Search Options + * Unsubscribe from all subscriptions related to the given paginationID + * @param paginationId The pagination id */ - get defaults(): Observable> { - if (hasNoValue(this._defaults)) { - const options = new PaginatedSearchOptions({ - pagination: this.defaultPagination, - configuration: this.defaultConfiguration, - sort: this.defaultSort, - scope: this.defaultScope, - query: this.defaultQuery - }); - this._defaults = createSuccessfulRemoteDataObject$(options, new Date().getTime()); + private unsubscribeFromSearchOptions(paginationId: string): void { + if (this.subs.has(this.paginationID)) { + this.subs.get(this.paginationID) + .filter((sub) => hasValue(sub)) + .forEach((sub) => sub.unsubscribe()); + this.subs.delete(paginationId); } - return this._defaults; - } - - /** - * Make sure to unsubscribe from all existing subscription to prevent memory leaks - */ - ngOnDestroy(): void { - this.subs.forEach((sub) => { - sub.unsubscribe(); - }); - this.subs = []; } /** diff --git a/src/app/search-navbar/search-navbar.component.ts b/src/app/search-navbar/search-navbar.component.ts index 1e509a180b..26849adf6d 100644 --- a/src/app/search-navbar/search-navbar.component.ts +++ b/src/app/search-navbar/search-navbar.component.ts @@ -3,8 +3,6 @@ import { FormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { SearchService } from '../core/shared/search/search.service'; import { expandSearchInput } from '../shared/animations/slide'; -import { PaginationService } from '../core/pagination/pagination.service'; -import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; /** * The search box in the header that expands on focus and collapses on focus out @@ -26,9 +24,7 @@ export class SearchNavbarComponent { // Search input field @ViewChild('searchInput') searchField: ElementRef; - constructor(private formBuilder: FormBuilder, private router: Router, private searchService: SearchService, - private paginationService: PaginationService, - private searchConfig: SearchConfigurationService) { + constructor(private formBuilder: FormBuilder, private router: Router, private searchService: SearchService) { this.searchForm = this.formBuilder.group(({ query: '', })); @@ -65,8 +61,12 @@ export class SearchNavbarComponent { */ onSubmit(data: any) { this.collapse(); + const queryParams = Object.assign({}, data); const linkToNavigateTo = this.searchService.getSearchLink().split('/'); this.searchForm.reset(); - this.paginationService.updateRouteWithUrl(this.searchConfig.paginationID, linkToNavigateTo, {page: 1}, data); + this.router.navigate(linkToNavigateTo, { + queryParams: queryParams, + queryParamsHandling: 'merge' + }); } } diff --git a/src/app/search-page/search-page.component.ts b/src/app/search-page/search-page.component.ts index 393af37c27..38647b779b 100644 --- a/src/app/search-page/search-page.component.ts +++ b/src/app/search-page/search-page.component.ts @@ -1,8 +1,16 @@ import { Component } from '@angular/core'; +import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; @Component({ selector: 'ds-search-page', templateUrl: './search-page.component.html', + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useClass: SearchConfigurationService + } + ] }) /** * This component represents the whole search page diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index cb9b43dbd1..caf6a91046 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -116,14 +116,11 @@ export class SearchFormComponent implements OnInit { */ updateSearch(data: any) { const queryParams = Object.assign({}, data); - const pageParam = this.paginationService.getPageParam(this.searchConfig.paginationID); - queryParams[pageParam] = 1; this.router.navigate(this.getSearchLinkParts(), { queryParams: queryParams, queryParamsHandling: 'merge' }); - this.paginationService.updateRouteWithUrl(this.searchConfig.paginationID, this.getSearchLinkParts(), { page: 1 }, data); } /** 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 23850f50dd..c383a2fa1a 100644 --- a/src/app/shared/search/search-results/search-results.component.html +++ b/src/app/shared/search/search-results/search-results.component.html @@ -1,5 +1,5 @@

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

-
+
- + diff --git a/src/app/shared/search/search-results/search-results.component.ts b/src/app/shared/search/search-results/search-results.component.ts index b13771656e..f7c5dcf5b7 100644 --- a/src/app/shared/search/search-results/search-results.component.ts +++ b/src/app/shared/search/search-results/search-results.component.ts @@ -78,6 +78,13 @@ export class SearchResultsComponent { @Output() selectObject: EventEmitter = new EventEmitter(); + /** + * Check if search results are loading + */ + isLoading() { + return !this.showError() && (hasNoValue(this.searchResults) || hasNoValue(this.searchResults.payload) || this.searchResults.isLoading); + } + showError(): boolean { return this.searchResults?.hasFailed && (!this.searchResults?.errorMessage || this.searchResults?.statusCode !== 400); } diff --git a/src/app/shared/search/search-settings/search-settings.component.html b/src/app/shared/search/search-settings/search-settings.component.html index a31678743d..40bca8e4e8 100644 --- a/src/app/shared/search/search-settings/search-settings.component.html +++ b/src/app/shared/search/search-settings/search-settings.component.html @@ -1,16 +1,14 @@ - +

{{ 'search.sidebar.settings.title' | translate}}

- -
diff --git a/src/app/shared/search/search-settings/search-settings.component.spec.ts b/src/app/shared/search/search-settings/search-settings.component.spec.ts index 6cf8ffc618..06e506ddb0 100644 --- a/src/app/shared/search/search-settings/search-settings.component.spec.ts +++ b/src/app/shared/search/search-settings/search-settings.component.spec.ts @@ -102,14 +102,12 @@ describe('SearchSettingsComponent', () => { fixture = TestBed.createComponent(SearchSettingsComponent); comp = fixture.componentInstance; - comp.sortOptions = [ + comp.sortOptionsList = [ new SortOptions('score', SortDirection.DESC), new SortOptions('dc.title', SortDirection.ASC), new SortOptions('dc.title', SortDirection.DESC) ]; - comp.searchOptions = paginatedSearchOptions; - // SearchPageComponent test instance fixture.detectChanges(); searchServiceObject = (comp as any).service; @@ -123,7 +121,7 @@ describe('SearchSettingsComponent', () => { const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings')); expect(orderSetting).toBeDefined(); const childElements = orderSetting.queryAll(By.css('option')); - expect(childElements.length).toEqual(comp.sortOptions.length); + expect(childElements.length).toEqual(comp.sortOptionsList.length); }); it('it should show the size settings', () => { diff --git a/src/app/shared/search/search-settings/search-settings.component.ts b/src/app/shared/search/search-settings/search-settings.component.ts index c97410fa5f..0efd38b5b2 100644 --- a/src/app/shared/search/search-settings/search-settings.component.ts +++ b/src/app/shared/search/search-settings/search-settings.component.ts @@ -2,7 +2,6 @@ import { Component, Inject, Input } from '@angular/core'; import { SearchService } from '../../../core/shared/search/search.service'; import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; import { ActivatedRoute, Router } from '@angular/router'; -import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; import { PaginationService } from '../../../core/pagination/pagination.service'; @@ -17,16 +16,15 @@ import { PaginationService } from '../../../core/pagination/pagination.service'; * This component represents the part of the search sidebar that contains the general search settings. */ export class SearchSettingsComponent { - /** - * The configuration for the current paginated search results + * The current sort option used */ - @Input() searchOptions: PaginatedSearchOptions; + @Input() currentSortOption: SortOptions; /** * All sort options that are shown in the settings */ - @Input() sortOptions: SortOptions[]; + @Input() sortOptionsList: SortOptions[]; constructor(private service: SearchService, private route: ActivatedRoute, diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index 624d094d22..c19831d71d 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -10,9 +10,13 @@
diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index 1d858f579d..f9f7165401 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -22,11 +22,21 @@ import { SortOptions } from '../../../core/cache/models/sort-options.model'; */ export class SearchSidebarComponent { + /** + * The configuration to use for the search options + */ + @Input() configuration; + /** * The list of available configuration options */ @Input() configurationList: SearchConfigurationOption[]; + /** + * The current sort option used + */ + @Input() currentSortOption: SortOptions; + /** * The total amount of results */ @@ -55,7 +65,7 @@ export class SearchSidebarComponent { /** * All sort options that are shown in the settings */ - @Input() sortOptions: SortOptions[]; + @Input() sortOptionsList: SortOptions[]; /** * Emits when the search filters values may be stale, and so they must be refreshed. @@ -67,4 +77,9 @@ export class SearchSidebarComponent { */ @Output() toggleSidebar = new EventEmitter(); + /** + * Emits event when the user select a new configuration + */ + @Output() changeConfiguration: EventEmitter = new EventEmitter(); + } diff --git a/src/app/shared/search/search-switch-configuration/search-configuration-option.model.ts b/src/app/shared/search/search-switch-configuration/search-configuration-option.model.ts index 6f9a72da48..d59f7b74b1 100644 --- a/src/app/shared/search/search-switch-configuration/search-configuration-option.model.ts +++ b/src/app/shared/search/search-switch-configuration/search-configuration-option.model.ts @@ -1,6 +1,8 @@ /** * Represents a search configuration select option */ +import { Context } from '../../../core/shared/context.model'; + export interface SearchConfigurationOption { /** @@ -12,4 +14,9 @@ export interface SearchConfigurationOption { * The select option label */ label: string; + + /** + * The search context to use with the configuration + */ + context: Context; } diff --git a/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.html b/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.html index e66483b645..f63bf9fa84 100644 --- a/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.html +++ b/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.html @@ -6,7 +6,7 @@ [compareWith]="compare" [(ngModel)]="selectedOption" (change)="onSelect()"> - diff --git a/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.spec.ts b/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.spec.ts index 3c0b9d156b..94e05b64fe 100644 --- a/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.spec.ts +++ b/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.spec.ts @@ -13,6 +13,7 @@ import { SearchService } from '../../../core/shared/search/search.service'; import { MYDSPACE_ROUTE, SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; import { MyDSpaceConfigurationValueType } from '../../../my-dspace-page/my-dspace-configuration-value-type'; import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; +import { Context } from '../../../core/shared/context.model'; describe('SearchSwitchConfigurationComponent', () => { @@ -25,6 +26,18 @@ describe('SearchSwitchConfigurationComponent', () => { getSearchLink: jasmine.createSpy('getSearchLink') }); + const configurationList = [ + { + value: MyDSpaceConfigurationValueType.Workspace, + label: 'workspace', + context: Context.Workspace + }, + { + value: MyDSpaceConfigurationValueType.Workflow, + label: 'workflow', + context: Context.Workflow + }, + ]; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ @@ -52,16 +65,7 @@ describe('SearchSwitchConfigurationComponent', () => { spyOn(searchConfService, 'getCurrentConfiguration').and.returnValue(observableOf(MyDSpaceConfigurationValueType.Workspace)); - comp.configurationList = [ - { - value: MyDSpaceConfigurationValueType.Workspace, - label: 'workspace' - }, - { - value: MyDSpaceConfigurationValueType.Workflow, - label: 'workflow' - }, - ]; + comp.configurationList = configurationList; // SearchSwitchConfigurationComponent test instance fixture.detectChanges(); @@ -69,7 +73,7 @@ describe('SearchSwitchConfigurationComponent', () => { }); it('should init the current configuration name', () => { - expect(comp.selectedOption).toBe(MyDSpaceConfigurationValueType.Workspace); + expect(comp.selectedOption).toBe(configurationList[0]); }); it('should display select field properly', () => { @@ -95,7 +99,8 @@ describe('SearchSwitchConfigurationComponent', () => { it('should navigate to the route when selecting an option', () => { spyOn((comp as any), 'getSearchLinkParts').and.returnValue([MYDSPACE_ROUTE]); - comp.selectedOption = MyDSpaceConfigurationValueType.Workflow; + spyOn((comp as any).changeConfiguration, 'emit'); + comp.selectedOption = configurationList[1]; const navigationExtras: NavigationExtras = { queryParams: { configuration: MyDSpaceConfigurationValueType.Workflow }, }; @@ -105,5 +110,6 @@ describe('SearchSwitchConfigurationComponent', () => { comp.onSelect(); expect((comp as any).router.navigate).toHaveBeenCalledWith([MYDSPACE_ROUTE], navigationExtras); + expect((comp as any).changeConfiguration.emit).toHaveBeenCalled(); }); }); diff --git a/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.ts b/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.ts index 061ca96a70..1852277673 100644 --- a/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.ts +++ b/src/app/shared/search/search-switch-configuration/search-switch-configuration.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { NavigationExtras, Router } from '@angular/router'; import { Subscription } from 'rxjs'; @@ -10,6 +10,7 @@ import { MyDSpaceConfigurationValueType } from '../../../my-dspace-page/my-dspac import { SearchConfigurationOption } from './search-configuration-option.model'; import { SearchService } from '../../../core/shared/search/search.service'; import { currentPath } from '../../utils/route.utils'; +import { findIndex } from 'lodash'; @Component({ selector: 'ds-search-switch-configuration', @@ -29,17 +30,25 @@ export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit { * The list of available configuration options */ @Input() configurationList: SearchConfigurationOption[] = []; - + /** + * The default configuration to use if no defined + */ + @Input() defaultConfiguration: string; /** * The selected option */ - public selectedOption: string; + public selectedOption: SearchConfigurationOption; /** * Subscription to unsubscribe from */ private sub: Subscription; + /** + * Emits event when the user select a new configuration + */ + @Output() changeConfiguration: EventEmitter = new EventEmitter(); + constructor(private router: Router, private searchService: SearchService, @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) { @@ -49,8 +58,11 @@ export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit { * Init current configuration */ ngOnInit() { - this.searchConfigService.getCurrentConfiguration('default') - .subscribe((currentConfiguration) => this.selectedOption = currentConfiguration); + this.searchConfigService.getCurrentConfiguration(this.defaultConfiguration) + .subscribe((currentConfiguration) => { + const index = findIndex(this.configurationList, {value: currentConfiguration }); + this.selectedOption = this.configurationList[index]; + }); } /** @@ -58,9 +70,10 @@ export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit { */ onSelect() { const navigationExtras: NavigationExtras = { - queryParams: {configuration: this.selectedOption}, + queryParams: {configuration: this.selectedOption.value}, }; + this.changeConfiguration.emit(this.selectedOption); this.router.navigate(this.getSearchLinkParts(), navigationExtras); } diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index 7886d7190e..f21dbeab22 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -21,7 +21,7 @@
- +
+ [context]="(currentContext$ | async)">
+ [sortOptionsList]="(sortOptionsList$ | async)" + [currentSortOption]="(currentSortOptions$ | async)" + [inPlaceSearch]="inPlaceSearch" + (changeConfiguration)="changeContext($event.context)"> + [sortOptionsList]="(sortOptionsList$ | async)" + [currentSortOption]="(currentSortOptions$ | async)" + (toggleSidebar)="closeSidebar()" + (changeConfiguration)="changeContext($event.context)"> diff --git a/src/app/shared/search/search.component.spec.ts b/src/app/shared/search/search.component.spec.ts index 0f77e04044..bfa4931670 100644 --- a/src/app/shared/search/search.component.spec.ts +++ b/src/app/shared/search/search.component.spec.ts @@ -4,7 +4,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; -import { cold, hot } from 'jasmine-marbles'; +import { cold } from 'jasmine-marbles'; import { of as observableOf } from 'rxjs'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { CommunityDataService } from '../../core/data/community-data.service'; @@ -21,10 +21,11 @@ import { SearchFilterService } from '../../core/shared/search/search-filter.serv import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { RouteService } from '../../core/services/route.service'; -import { SearchConfigurationServiceStub } from '../testing/search-configuration-service.stub'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { PaginatedSearchOptions } from './models/paginated-search-options.model'; import { SidebarServiceStub } from '../testing/sidebar-service.stub'; +import { SearchConfig } from '../../core/shared/search/search-filters/search-config.model'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; let comp: SearchComponent; let fixture: ComponentFixture; @@ -36,6 +37,14 @@ const store: Store = jasmine.createSpyObj('store', { /* tslint:enable:no-empty */ select: observableOf(true) }); +const sortOptionsList = [ + new SortOptions('score', SortDirection.DESC), + new SortOptions('dc.title', SortDirection.ASC), + new SortOptions('dc.title', SortDirection.DESC) +]; +const searchConfig = Object.assign(new SearchConfig(), { + sortOptions: sortOptionsList +}); const pagination: PaginationComponentOptions = new PaginationComponentOptions(); pagination.id = 'search-results-pagination'; pagination.currentPage = 1; @@ -47,7 +56,7 @@ const searchServiceStub = jasmine.createSpyObj('SearchService', { search: mockResults, getSearchLink: '/search', getScopes: observableOf(['test-scope']), - getSearchConfigurationFor: createSuccessfulRemoteDataObject$({ sortOptions: [sortOption]}) + getSearchConfigurationFor: createSuccessfulRemoteDataObject$(searchConfig) }); const configurationParam = 'default'; const queryParam = 'test query'; @@ -86,6 +95,15 @@ const routeServiceStub = { } }; + +const searchConfigurationServiceStub = jasmine.createSpyObj('SearchConfigurationService', { + getConfigurationSearchConfig: jasmine.createSpy('getConfigurationSearchConfig'), + getCurrentConfiguration: jasmine.createSpy('getCurrentConfiguration'), + getCurrentScope: jasmine.createSpy('getCurrentScope'), + updateFixedFilter: jasmine.createSpy('updateFixedFilter'), + setPaginationId: jasmine.createSpy('setPaginationId') +}); + export function configureSearchComponentTestingModule(compType, additionalDeclarations: any[] = []) { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule], @@ -117,23 +135,10 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar provide: SearchFilterService, useValue: {} }, - { - provide: SearchConfigurationService, - useValue: { - paginatedSearchOptions: hot('a', { - a: paginatedSearchOptions - }), - getCurrentScope: (a) => observableOf('test-id'), - /* tslint:disable:no-empty */ - updateFixedFilter: (newFilter) => { - } - /* tslint:enable:no-empty */ - } - }, { provide: SEARCH_CONFIG_SERVICE, - useValue: new SearchConfigurationServiceStub() - }, + useValue: searchConfigurationServiceStub + } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(compType, { @@ -141,7 +146,7 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar }).compileComponents(); } -describe('SearchComponent', () => { +fdescribe('SearchComponent', () => { beforeEach(waitForAsync(() => { configureSearchComponentTestingModule(SearchComponent); })); @@ -150,9 +155,17 @@ describe('SearchComponent', () => { fixture = TestBed.createComponent(SearchComponent); comp = fixture.componentInstance; // SearchComponent test instance comp.inPlaceSearch = false; + + // searchConfigurationServiceStub.paginatedSearchOptions.and.returnValue(observableOf(paginatedSearchOptions)); + searchConfigurationServiceStub.getConfigurationSearchConfig.and.returnValue(observableOf(searchConfig)); + searchConfigurationServiceStub.getCurrentConfiguration.and.returnValue(observableOf('default')); + searchConfigurationServiceStub.getCurrentScope.and.returnValue(observableOf('test-id')); + + searchServiceObject = TestBed.inject(SearchService); + searchConfigurationServiceObject = TestBed.inject(SEARCH_CONFIG_SERVICE); + searchConfigurationServiceObject.paginatedSearchOptions = new BehaviorSubject(paginatedSearchOptions); + fixture.detectChanges(); - searchServiceObject = (comp as any).service; - searchConfigurationServiceObject = (comp as any).searchConfigService; }); afterEach(() => { @@ -163,14 +176,13 @@ describe('SearchComponent', () => { it('should get the scope and query from the route parameters', () => { - searchConfigurationServiceObject.paginatedSearchOptions.next(paginatedSearchOptions); expect(comp.searchOptions$).toBeObservable(cold('b', { b: paginatedSearchOptions })); }); - describe('when the open sidebar button is clicked in mobile view', () => { + xdescribe('when the open sidebar button is clicked in mobile view', () => { beforeEach(() => { spyOn(comp, 'openSidebar'); @@ -192,7 +204,7 @@ describe('SearchComponent', () => { it('should have initialized the sortOptions$ observable', (done) => { - comp.sortOptions$.subscribe((sortOptions) => { + comp.sortOptionsList$.subscribe((sortOptions) => { expect(sortOptions.length).toEqual(2); expect(sortOptions[0]).toEqual(new SortOptions('score', SortDirection.ASC)); diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index 0fb7ca358a..2f18507652 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -1,14 +1,17 @@ import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core'; -import { BehaviorSubject, Observable, Subscription } from 'rxjs'; -import { startWith, switchMap } from 'rxjs/operators'; +import { Router } from '@angular/router'; + +import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; +import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; +import { uniqueId } from 'lodash'; + import { PaginatedList } from '../../core/data/paginated-list.model'; import { RemoteData } from '../../core/data/remote-data'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { pushInOut } from '../animations/push'; import { HostWindowService } from '../host-window.service'; import { SidebarService } from '../sidebar/sidebar.service'; -import { hasValue, isEmpty } from '../empty.util'; -import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { hasValue } from '../empty.util'; import { RouteService } from '../../core/services/route.service'; import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { PaginatedSearchOptions } from './models/paginated-search-options.model'; @@ -16,11 +19,15 @@ import { SearchResult } from './models/search-result.model'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; import { SearchService } from '../../core/shared/search/search.service'; import { currentPath } from '../utils/route.utils'; -import { Router } from '@angular/router'; import { Context } from '../../core/shared/context.model'; import { SortOptions } from '../../core/cache/models/sort-options.model'; +import { SearchConfig } from '../../core/shared/search/search-filters/search-config.model'; +import { SearchConfigurationOption } from './search-switch-configuration/search-configuration-option.model'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; import { followLink } from '../utils/follow-link-config.model'; import { Item } from '../../core/shared/item.model'; +import { SearchObjects } from './models/search-objects.model'; +import { ViewMode } from '../../core/shared/view-mode.model'; @Component({ selector: 'ds-search', @@ -28,18 +35,82 @@ import { Item } from '../../core/shared/item.model'; templateUrl: './search.component.html', changeDetection: ChangeDetectionStrategy.OnPush, animations: [pushInOut], - providers: [ - { - provide: SEARCH_CONFIG_SERVICE, - useClass: SearchConfigurationService - } - ] }) /** * This component renders a sidebar, a search input bar and the search results. */ export class SearchComponent implements OnInit { + + /** + * The list of available configuration options + */ + @Input() configurationList: SearchConfigurationOption[] = []; + + /** + * The current context + * If empty, 'search' is used + */ + @Input() context: Context = Context.Search; + + /** + * The configuration to use for the search options + * If empty, 'default' is used + */ + @Input() configuration = 'default'; + + /** + * The actual query for the fixed filter. + * If empty, the query will be determined by the route parameter called 'filter' + */ + @Input() fixedFilterQuery: string; + + /** + * If this is true, the request will only be sent if there's + * no valid cached version. Defaults to true + */ + @Input() useCachedVersionIfAvailable = true; + + /** + * True when the search component should show results on the current page + */ + @Input() inPlaceSearch = true; + + /** + * The pagination id used in the search + */ + @Input() paginationId = 'spc'; + + /** + * Whether or not the search bar should be visible + */ + @Input() searchEnabled = true; + + /** + * The width of the sidebar (bootstrap columns) + */ + @Input() sideBarWidth = 3; + + /** + * A boolean representing if show search sidebar button + */ + @Input() showSidebar = true; + + /** + * List of available view mode + */ + @Input() viewModeList: ViewMode[]; + + /** + * The current configuration used during the search + */ + currentConfiguration$: BehaviorSubject = new BehaviorSubject(''); + + /** + * The current context used during the search + */ + currentContext$: BehaviorSubject = new BehaviorSubject(null); + /** * The current search results */ @@ -48,56 +119,17 @@ export class SearchComponent implements OnInit { /** * The current paginated search options */ - searchOptions$: Observable; + searchOptions$: BehaviorSubject = new BehaviorSubject(null); /** - * The current available sort options + * The available sort options list */ - sortOptions$: Observable; + sortOptionsList$: BehaviorSubject = new BehaviorSubject([]); /** - * Emits true if were on a small screen + * The current sort options used */ - isXsOrSm$: Observable; - - /** - * Subscription to unsubscribe from - */ - sub: Subscription; - - /** - * True when the search component should show results on the current page - */ - @Input() inPlaceSearch = true; - - /** - * Whether or not the search bar should be visible - */ - @Input() - searchEnabled = true; - - /** - * The width of the sidebar (bootstrap columns) - */ - @Input() - sideBarWidth = 3; - - /** - * The currently applied configuration (determines title of search) - */ - @Input() - configuration$: Observable; - - /** - * The current context - */ - @Input() - context: Context; - - /** - * Link to the search page - */ - searchLink: string; + currentSortOptions$: BehaviorSubject = new BehaviorSubject(null); /** * Observable for whether or not the sidebar is currently collapsed @@ -105,9 +137,20 @@ export class SearchComponent implements OnInit { isSidebarCollapsed$: Observable; /** - * A boolean representing if show search sidebar button + * Emits true if were on a small screen */ - @Input() showSidebar = true; + isXsOrSm$: Observable; + + /** + * Link to the search page + */ + searchLink: string; + + /** + * Subscription to unsubscribe from + */ + sub: Subscription; + constructor(protected service: SearchService, protected sidebarService: SidebarService, protected windowService: HostWindowService, @@ -125,35 +168,67 @@ export class SearchComponent implements OnInit { * If something changes, update the list of scopes for the dropdown */ ngOnInit(): void { - this.isSidebarCollapsed$ = this.isSidebarCollapsed(); - this.searchLink = this.getSearchLink(); - this.searchOptions$ = this.getSearchOptions(); - this.sub = this.searchOptions$.pipe( - switchMap((options) => this.service.search( - options, undefined, false, true, followLink('thumbnail', { isOptional: true }) - ).pipe(getFirstCompletedRemoteData(), startWith(undefined)) - ) - ).subscribe((results) => { - this.resultsRD$.next(results); - }); + // Create an unique pagination id related to the instance of the SearchComponent + this.paginationId = uniqueId(this.paginationId); + this.searchConfigService.setPaginationId(this.paginationId); - if (isEmpty(this.configuration$)) { - this.configuration$ = this.searchConfigService.getCurrentConfiguration('default'); + if (hasValue(this.fixedFilterQuery)) { + this.routeService.setParameter('fixedFilterQuery', this.fixedFilterQuery); } - const searchConfig$ = this.searchConfigService.getConfigurationSearchConfigObservable(this.configuration$, this.service); + this.isSidebarCollapsed$ = this.isSidebarCollapsed(); + this.searchLink = this.getSearchLink(); + this.currentContext$.next(this.context); - this.sortOptions$ = this.searchConfigService.getConfigurationSortOptionsObservable(searchConfig$); - this.searchConfigService.initializeSortOptionsFromConfiguration(searchConfig$); + // Determinate PaginatedSearchOptions and listen to any update on it + const configuration$: Observable = this.searchConfigService + .getCurrentConfiguration(this.configuration).pipe(distinctUntilChanged()); + const searchSortOptions$: Observable = configuration$.pipe( + switchMap((configuration: string) => this.searchConfigService + .getConfigurationSearchConfig(configuration, this.service)), + map((searchConfig: SearchConfig) => this.searchConfigService.getConfigurationSortOptions(searchConfig)), + distinctUntilChanged() + ); + const sortOption$: Observable = searchSortOptions$.pipe( + switchMap((searchSortOptions: SortOptions[]) => { + const defaultSort: SortOptions = searchSortOptions[0]; + return this.searchConfigService.getCurrentSort(this.paginationId, defaultSort); + }), + distinctUntilChanged() + ); + const searchOptions$: Observable = this.getSearchOptions().pipe(distinctUntilChanged()); + this.sub = combineLatest([configuration$, searchSortOptions$, searchOptions$, sortOption$]).pipe( + filter(([configuration, searchSortOptions, searchOptions, sortOption]: [string, SortOptions[], PaginatedSearchOptions, SortOptions]) => { + // filter for search options related to instanced paginated id + return searchOptions.pagination.id === this.paginationId; + }) + ).subscribe(([configuration, searchSortOptions, searchOptions, sortOption]: [string, SortOptions[], PaginatedSearchOptions, SortOptions]) => { + // Build the PaginatedSearchOptions object + const combinedOptions = Object.assign({}, searchOptions, + { + configuration: searchOptions.configuration || configuration, + sort: sortOption || searchOptions.sort + }); + const newSearchOptions = new PaginatedSearchOptions(combinedOptions); + + // Initialize variables + this.currentConfiguration$.next(configuration); + this.currentSortOptions$.next(newSearchOptions.sort); + this.sortOptionsList$.next(searchSortOptions); + this.searchOptions$.next(newSearchOptions); + + // retrieve results + this.retrieveSearchResults(newSearchOptions); + }); } /** - * Get the current paginated search options - * @returns {Observable} + * Change the current context + * @param context */ - protected getSearchOptions(): Observable { - return this.searchConfigService.paginatedSearchOptions; + public changeContext(context: Context) { + this.currentContext$.next(context); } /** @@ -170,6 +245,43 @@ export class SearchComponent implements OnInit { this.sidebarService.expand(); } + /** + * Unsubscribe from the subscription + */ + ngOnDestroy(): void { + if (hasValue(this.sub)) { + this.sub.unsubscribe(); + } + } + + /** + * Get the current paginated search options + * @returns {Observable} + */ + protected getSearchOptions(): Observable { + return this.searchConfigService.paginatedSearchOptions; + } + + /** + * Retrieve search result by the given search options + * @param searchOptions + * @private + */ + private retrieveSearchResults(searchOptions: PaginatedSearchOptions) { + this.resultsRD$.next(null); + this.service.search( + searchOptions, + undefined, + this.useCachedVersionIfAvailable, + true, + followLink('thumbnail', { isOptional: true }) + ).pipe(getFirstCompletedRemoteData()) + .subscribe((results: RemoteData>) => { + console.log('results ', results); + this.resultsRD$.next(results); + }); + } + /** * Check if the sidebar is collapsed * @returns {Observable} emits true if the sidebar is currently collapsed, false if it is expanded @@ -188,12 +300,4 @@ export class SearchComponent implements OnInit { return this.service.getSearchLink(); } - /** - * Unsubscribe from the subscription - */ - ngOnDestroy(): void { - if (hasValue(this.sub)) { - this.sub.unsubscribe(); - } - } } From 97221647057adc0d5751c5cf3705fc4773f5d352 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 17 Dec 2021 19:51:37 +0100 Subject: [PATCH 044/102] [CST-4633] use search.component.ts for the mydspace page --- .../my-dspace-configuration.service.ts | 10 +- .../my-dspace-page.component.html | 62 +-------- .../my-dspace-page.component.ts | 128 ++---------------- .../my-dspace-page/my-dspace-page.module.ts | 2 - .../my-dspace-results.component.html | 14 -- .../my-dspace-results.component.spec.ts | 68 ---------- .../my-dspace-results.component.ts | 69 ---------- src/assets/i18n/en.json5 | 4 + 8 files changed, 30 insertions(+), 327 deletions(-) delete mode 100644 src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.html delete mode 100644 src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts delete mode 100644 src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.ts b/src/app/my-dspace-page/my-dspace-configuration.service.ts index 82f76eb776..7dde8eff77 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.ts @@ -12,6 +12,13 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; import { RouteService } from '../core/services/route.service'; import { PaginationService } from '../core/pagination/pagination.service'; +import { Context } from '../core/shared/context.model'; + +export const MyDSpaceConfigurationToContextMap = new Map([ + [MyDSpaceConfigurationValueType.Workspace, Context.Workspace], + [MyDSpaceConfigurationValueType.Workflow, Context.Workflow] +]); + /** * Service that performs all actions that have to do with the current mydspace configuration @@ -110,7 +117,8 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { availableConfigurationTypes.forEach((type) => { const value = type; const label = `mydspace.show.${value}`; - configurationOptions.push({ value, label }); + const context = MyDSpaceConfigurationToContextMap.get(type); + configurationOptions.push({ value, label, context }); }); return configurationOptions; }) diff --git a/src/app/my-dspace-page/my-dspace-page.component.html b/src/app/my-dspace-page/my-dspace-page.component.html index 5a15abcc08..2b0dc7108a 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.html +++ b/src/app/my-dspace-page/my-dspace-page.component.html @@ -1,56 +1,6 @@ -
- -
- -
- - - -
-
- - -
-
- - -
- -
-
-
-
-
-
+ diff --git a/src/app/my-dspace-page/my-dspace-page.component.ts b/src/app/my-dspace-page/my-dspace-page.component.ts index bac1713230..4fbebcd6a7 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.ts +++ b/src/app/my-dspace-page/my-dspace-page.component.ts @@ -1,29 +1,18 @@ -import { ChangeDetectionStrategy, Component, Inject, InjectionToken, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, InjectionToken, OnInit } from '@angular/core'; -import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs'; -import { map, switchMap, tap } from 'rxjs/operators'; - -import { PaginatedList } from '../core/data/paginated-list.model'; -import { RemoteData } from '../core/data/remote-data'; -import { DSpaceObject } from '../core/shared/dspace-object.model'; +import { Observable, Subject } from 'rxjs'; +import { take } from 'rxjs/operators'; import { pushInOut } from '../shared/animations/push'; import { HostWindowService } from '../shared/host-window.service'; -import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; import { SearchService } from '../core/shared/search/search.service'; import { SidebarService } from '../shared/sidebar/sidebar.service'; -import { hasValue } from '../shared/empty.util'; -import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service'; import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model'; -import { RoleType } from '../core/roles/role-types'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; import { ViewMode } from '../core/shared/view-mode.model'; import { MyDSpaceRequest } from '../core/data/request.models'; -import { SearchResult } from '../shared/search/models/search-result.model'; import { Context } from '../core/shared/context.model'; -import { SortOptions } from '../core/cache/models/sort-options.model'; -import { SearchObjects } from '../shared/search/models/search-objects.model'; export const MYDSPACE_ROUTE = '/mydspace'; export const SEARCH_CONFIG_SERVICE: InjectionToken = new InjectionToken('searchConfigurationService'); @@ -46,55 +35,26 @@ export const SEARCH_CONFIG_SERVICE: InjectionToken = }) export class MyDSpacePageComponent implements OnInit { - /** - * True when the search component should show results on the current page - */ - @Input() inPlaceSearch = true; - /** * The list of available configuration options */ configurationList$: Observable; /** - * The current search results + * The start context to use in the search: workspace or workflow */ - resultsRD$: BehaviorSubject>>> = new BehaviorSubject(null); + context: Context; /** - * The current paginated search options + * The start configuration to use in the search: workspace or workflow */ - searchOptions$: Observable; - - /** - * The current available sort options - */ - sortOptions$: Observable; - - /** - * Emits true if were on a small screen - */ - isXsOrSm$: Observable; - - /** - * Subscription to unsubscribe from - */ - sub: Subscription; - - /** - * Variable for enumeration RoleType - */ - roleTypeEnum = RoleType; + configuration: string; /** * List of available view mode */ viewModeList = [ViewMode.ListElement, ViewMode.DetailedListElement]; - /** - * The current context of this page: workspace or workflow - */ - context$: Observable; /** * Emit an event every time search sidebars must refresh their contents. @@ -105,7 +65,6 @@ export class MyDSpacePageComponent implements OnInit { private sidebarService: SidebarService, private windowService: HostWindowService, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) { - this.isXsOrSm$ = this.windowService.isXsOrSm(); this.service.setServiceOptions(MyDSpaceResponseParsingService, MyDSpaceRequest); } @@ -123,77 +82,12 @@ export class MyDSpacePageComponent implements OnInit { */ ngOnInit(): void { this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions(); - this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; - this.sub = this.searchOptions$.pipe( - tap(() => this.resultsRD$.next(null)), - switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getFirstCompletedRemoteData()))) - .subscribe((results: RemoteData>) => { - this.resultsRD$.next(results); - }); - this.context$ = this.searchConfigService.getCurrentConfiguration('workspace') - .pipe( - map((configuration: string) => { - if (configuration === 'workspace') { - return Context.Workspace; - } else { - return Context.Workflow; - } - }) - ); - - const configuration$ = this.searchConfigService.getCurrentConfiguration('workspace'); - const searchConfig$ = this.searchConfigService.getConfigurationSearchConfigObservable(configuration$, this.service); - - this.sortOptions$ = this.searchConfigService.getConfigurationSortOptionsObservable(searchConfig$); - this.searchConfigService.initializeSortOptionsFromConfiguration(searchConfig$); + this.configurationList$.pipe(take(1)).subscribe((configurationList: SearchConfigurationOption[]) => { + this.configuration = configurationList[0].value; + this.context = configurationList[0].context; + }); } - /** - * Handle the contentChange event from within the my dspace content. - * Notify search sidebars to refresh their content. - */ - onResultsContentChange() { - this.refreshFilters.next(); - } - - /** - * Set the sidebar to a collapsed state - */ - public closeSidebar(): void { - this.sidebarService.collapse(); - } - - /** - * Set the sidebar to an expanded state - */ - public openSidebar(): void { - this.sidebarService.expand(); - } - - /** - * Check if the sidebar is collapsed - * @returns {Observable} emits true if the sidebar is currently collapsed, false if it is expanded - */ - public isSidebarCollapsed(): Observable { - return this.sidebarService.isCollapsed; - } - - /** - * @returns {string} The base path to the search page - */ - public getSearchLink(): string { - return this.service.getSearchLink(); - } - - /** - * Unsubscribe from the subscription - */ - ngOnDestroy(): void { - if (hasValue(this.sub)) { - this.sub.unsubscribe(); - } - this.refreshFilters.complete(); - } } diff --git a/src/app/my-dspace-page/my-dspace-page.module.ts b/src/app/my-dspace-page/my-dspace-page.module.ts index 183edf2728..2ccddd87f7 100644 --- a/src/app/my-dspace-page/my-dspace-page.module.ts +++ b/src/app/my-dspace-page/my-dspace-page.module.ts @@ -5,7 +5,6 @@ import { SharedModule } from '../shared/shared.module'; import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module'; import { MyDSpacePageComponent } from './my-dspace-page.component'; -import { MyDSpaceResultsComponent } from './my-dspace-results/my-dspace-results.component'; import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission/my-dspace-new-submission.component'; import { MyDSpaceGuard } from './my-dspace.guard'; import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; @@ -19,7 +18,6 @@ import { SearchModule } from '../shared/search/search.module'; const DECLARATIONS = [ MyDSpacePageComponent, ThemedMyDSpacePageComponent, - MyDSpaceResultsComponent, MyDSpaceNewSubmissionComponent, CollectionSelectorComponent, MyDSpaceNewSubmissionDropdownComponent, diff --git a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.html b/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.html deleted file mode 100644 index 67b13cc49c..0000000000 --- a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - -
- - -

{{'mydspace.results.no-results' | translate}}

diff --git a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts b/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts deleted file mode 100644 index 5b069f4ddb..0000000000 --- a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; - -import { TranslateModule } from '@ngx-translate/core'; -import { QueryParamsDirectiveStub } from '../../shared/testing/query-params-directive.stub'; -import { MyDSpaceResultsComponent } from './my-dspace-results.component'; - -describe('MyDSpaceResultsComponent', () => { - let comp: MyDSpaceResultsComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), NoopAnimationsModule], - declarations: [ - MyDSpaceResultsComponent, - QueryParamsDirectiveStub], - schemas: [NO_ERRORS_SCHEMA] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(MyDSpaceResultsComponent); - comp = fixture.componentInstance; // MyDSpaceResultsComponent test instance - }); - - it('should display results when results are not empty', () => { - (comp as any).searchResults = { hasSucceeded: true, isLoading: false, payload: { page: { length: 2 } } }; - (comp as any).searchConfig = {}; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('ds-viewable-collection'))).not.toBeNull(); - }); - - it('should not display link when results are not empty', () => { - (comp as any).searchResults = { hasSucceeded: true, isLoading: false, payload: { page: { length: 2 } } }; - (comp as any).searchConfig = {}; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('a'))).toBeNull(); - }); - - it('should display error message if error is 500', () => { - (comp as any).searchResults = { hasFailed: true, statusCode: 500 }; - fixture.detectChanges(); - expect(comp.showError()).toBeTrue(); - expect(comp.errorMessageLabel()).toBe('error.search-results'); - expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull(); - }); - - it('should display error message if error is 422', () => { - (comp as any).searchResults = { hasFailed: true, statusCode: 422 }; - fixture.detectChanges(); - expect(comp.showError()).toBeTrue(); - expect(comp.errorMessageLabel()).toBe('error.invalid-search-query'); - expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull(); - }); - - it('should display a message if search result is empty', () => { - (comp as any).searchResults = { payload: { page: { length: 0 } } }; - (comp as any).searchConfig = { query: 'foobar' }; - fixture.detectChanges(); - - const linkDes = fixture.debugElement.queryAll(By.css('text-muted')); - - expect(linkDes).toBeDefined(); - }); -}); diff --git a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts b/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts deleted file mode 100644 index cc8f7c8fc3..0000000000 --- a/src/app/my-dspace-page/my-dspace-results/my-dspace-results.component.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { RemoteData } from '../../core/data/remote-data'; -import { DSpaceObject } from '../../core/shared/dspace-object.model'; -import { fadeIn, fadeInOut } from '../../shared/animations/fade'; -import { PaginatedList } from '../../core/data/paginated-list.model'; -import { ViewMode } from '../../core/shared/view-mode.model'; -import { isEmpty } from '../../shared/empty.util'; -import { Context } from '../../core/shared/context.model'; -import { SearchResult } from '../../shared/search/models/search-result.model'; -import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; - -/** - * Component that represents all results for mydspace page - */ -@Component({ - selector: 'ds-my-dspace-results', - templateUrl: './my-dspace-results.component.html', - animations: [ - fadeIn, - fadeInOut - ] -}) -export class MyDSpaceResultsComponent { - - /** - * The actual search result objects - */ - @Input() searchResults: RemoteData>>; - - /** - * The current configuration of the search - */ - @Input() searchConfig: PaginatedSearchOptions; - - /** - * The current view mode for the search results - */ - @Input() viewMode: ViewMode; - - /** - * The current context for the search results - */ - @Input() context: Context; - - /** - * Emit when one of the results has changed. - */ - @Output() contentChange = new EventEmitter(); - - /** - * A boolean representing if search results entry are separated by a line - */ - hasBorder = true; - - /** - * Check if mydspace search results are loading - */ - isLoading() { - return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading; - } - - showError(): boolean { - return this.searchResults?.hasFailed && (!this.searchResults?.errorMessage || this.searchResults?.statusCode !== 400); - } - - errorMessageLabel(): string { - return (this.searchResults?.statusCode === 422) ? 'error.invalid-search-query' : 'error.search-results'; - } -} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 8edc01b278..1ab27fdb0e 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4019,8 +4019,12 @@ + "workspace.search.results.head": "Your submissions", + "workflowAdmin.search.results.head": "Administer Workflow", + "workflow.search.results.head": "All tasks", + "workflow-item.edit.breadcrumbs": "Edit workflowitem", From f43317c2ee8c810604f75e6b03aad3fa90daa578 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 17 Dec 2021 19:51:50 +0100 Subject: [PATCH 045/102] [CST-4633] fix build --- .../related-entities-search.component.html | 1 - .../related-entities-search.component.ts | 5 ----- .../configuration-search-page.component.ts | 13 +------------ .../themed-configuration-search-page.component.ts | 2 +- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html index 75f3b7aaad..2a08efeb2c 100644 --- a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html +++ b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html @@ -1,7 +1,6 @@ diff --git a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts index 781e72795b..a3f76c12d7 100644 --- a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts +++ b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts @@ -1,5 +1,4 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Observable, of } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { isNotEmpty } from '../../../../shared/empty.util'; import { getFilterByRelation } from '../../../../shared/utils/relation-query.utils'; @@ -44,15 +43,11 @@ export class RelatedEntitiesSearchComponent implements OnInit { @Input() sideBarWidth = 4; fixedFilter: string; - configuration$: Observable; ngOnInit(): void { if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) { this.fixedFilter = getFilterByRelation(this.relationType, this.item.id); } - if (isNotEmpty(this.configuration)) { - this.configuration$ = of(this.configuration); - } } } diff --git a/src/app/search-page/configuration-search-page.component.ts b/src/app/search-page/configuration-search-page.component.ts index 6daa822e2e..18466452f1 100644 --- a/src/app/search-page/configuration-search-page.component.ts +++ b/src/app/search-page/configuration-search-page.component.ts @@ -1,7 +1,7 @@ import { HostWindowService } from '../shared/host-window.service'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { SearchComponent } from '../shared/search/search.component'; -import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core'; import { pushInOut } from '../shared/animations/push'; import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; @@ -28,17 +28,6 @@ import { Router } from '@angular/router'; }) export class ConfigurationSearchPageComponent extends SearchComponent implements OnInit { - /** - * The configuration to use for the search options - * If empty, the configuration will be determined by the route parameter called 'configuration' - */ - @Input() configuration: string; - - /** - * The actual query for the fixed filter. - * If empty, the query will be determined by the route parameter called 'filter' - */ - @Input() fixedFilterQuery: string; constructor(protected service: SearchService, protected sidebarService: SidebarService, diff --git a/src/app/search-page/themed-configuration-search-page.component.ts b/src/app/search-page/themed-configuration-search-page.component.ts index 1419d18a88..e4d6e93402 100644 --- a/src/app/search-page/themed-configuration-search-page.component.ts +++ b/src/app/search-page/themed-configuration-search-page.component.ts @@ -55,7 +55,7 @@ export class ThemedConfigurationSearchPageComponent extends ThemedComponent Date: Sun, 19 Dec 2021 17:36:03 +0100 Subject: [PATCH 046/102] [CST-4633] fix tests --- .../search/search-configuration.service.ts | 4 +- .../search-filters/search-config.model.ts | 4 +- .../related-entities-search.component.spec.ts | 6 - .../my-dspace-configuration.service.spec.ts | 7 +- .../my-dspace-page.component.spec.ts | 215 ++++-------------- .../my-dspace-page.component.ts | 14 +- .../search-navbar.component.spec.ts | 25 +- .../search-navbar/search-navbar.component.ts | 3 +- .../shared/search/search.component.spec.ts | 115 ++++++---- src/app/shared/search/search.component.ts | 3 +- 10 files changed, 140 insertions(+), 256 deletions(-) diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index 03e46cac65..40bb8aebcd 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -20,7 +20,7 @@ import { RouteService } from '../../services/route.service'; import { getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData } from '../operators'; import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; -import { SearchConfig, SortOption } from './search-filters/search-config.model'; +import { SearchConfig, SortConfig } from './search-filters/search-config.model'; import { SearchService } from './search.service'; import { PaginationService } from '../../pagination/pagination.service'; @@ -204,7 +204,7 @@ export class SearchConfigurationService implements OnDestroy { * @param searchConfig The SearchConfig object */ getConfigurationSortOptions(searchConfig: SearchConfig): SortOptions[] { - return searchConfig.sortOptions.map((entry: SortOption) => ({ + return searchConfig.sortOptions.map((entry: SortConfig) => ({ field: entry.name, direction: entry.sortOrder.toLowerCase() === SortDirection.ASC.toLowerCase() ? SortDirection.ASC : SortDirection.DESC })); diff --git a/src/app/core/shared/search/search-filters/search-config.model.ts b/src/app/core/shared/search/search-filters/search-config.model.ts index 725761fe7b..e789de0f80 100644 --- a/src/app/core/shared/search/search-filters/search-config.model.ts +++ b/src/app/core/shared/search/search-filters/search-config.model.ts @@ -29,7 +29,7 @@ export class SearchConfig implements CacheableObject { * The configured sort options. */ @autoserialize - sortOptions: SortOption[]; + sortOptions: SortConfig[]; /** * The object type. @@ -63,7 +63,7 @@ export interface FilterConfig { /** * Interface to model sort option's configuration. */ -export interface SortOption { +export interface SortConfig { name: string; sortOrder: string; } diff --git a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts index a75a0feae2..a9fd11b9ee 100644 --- a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts +++ b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts @@ -38,10 +38,4 @@ describe('RelatedEntitiesSearchComponent', () => { expect(comp.fixedFilter).toEqual(mockFilter); }); - it('should create a configuration$', () => { - comp.configuration$.subscribe((configuration) => { - expect(configuration).toEqual(mockConfiguration); - }); - }); - }); diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts index b4926d7b92..dc971bc3af 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts @@ -10,6 +10,7 @@ import { RoleServiceMock } from '../shared/mocks/role-service.mock'; import { cold, hot } from 'jasmine-marbles'; import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; +import { Context } from '../core/shared/context.model'; describe('MyDSpaceConfigurationService', () => { let service: MyDSpaceConfigurationService; @@ -241,11 +242,13 @@ describe('MyDSpaceConfigurationService', () => { b: [ { value: MyDSpaceConfigurationValueType.Workspace, - label: `mydspace.show.${MyDSpaceConfigurationValueType.Workspace}` + label: `mydspace.show.${MyDSpaceConfigurationValueType.Workspace}`, + context: Context.Workspace }, { value: MyDSpaceConfigurationValueType.Workflow, - label: `mydspace.show.${MyDSpaceConfigurationValueType.Workflow}` + label: `mydspace.show.${MyDSpaceConfigurationValueType.Workflow}`, + context: Context.Workflow } ] })); 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 6d8bfc274c..34bf20d462 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 @@ -1,213 +1,88 @@ -import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ActivatedRoute } from '@angular/router'; -import { By } from '@angular/platform-browser'; import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, flush, TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { Store } from '@ngrx/store'; +import { of as observableOf } from 'rxjs'; import { TranslateModule } from '@ngx-translate/core'; import { cold } from 'jasmine-marbles'; -import { of as observableOf } from 'rxjs'; -import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; -import { CommunityDataService } from '../core/data/community-data.service'; -import { HostWindowService } from '../shared/host-window.service'; -import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from './my-dspace-page.component'; -import { RouteService } from '../core/services/route.service'; -import { routeServiceStub } from '../shared/testing/route-service.stub'; -import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service.stub'; import { SearchService } from '../core/shared/search/search.service'; -import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; -import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; -import { SidebarService } from '../shared/sidebar/sidebar.service'; -import { SearchFilterService } from '../core/shared/search/search-filter.service'; -import { RoleDirective } from '../shared/roles/role.directive'; -import { RoleService } from '../core/roles/role.service'; -import { RoleServiceMock } from '../shared/mocks/role-service.mock'; -import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; -import { SidebarServiceStub } from '../shared/testing/sidebar-service.stub'; +import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; +import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; +import { Context } from '../core/shared/context.model'; +import SpyObj = jasmine.SpyObj; describe('MyDSpacePageComponent', () => { let comp: MyDSpacePageComponent; let fixture: ComponentFixture; - let searchServiceObject: SearchService; - let searchConfigurationServiceObject: SearchConfigurationService; - const store: Store = jasmine.createSpyObj('store', { - /* tslint:disable:no-empty */ - dispatch: {}, - /* tslint:enable:no-empty */ - select: observableOf(true) + + const searchServiceStub: SpyObj = jasmine.createSpyObj('SearchService', { + setServiceOptions: jasmine.createSpy('setServiceOptions') }); - const pagination: PaginationComponentOptions = new PaginationComponentOptions(); - pagination.id = 'mydspace-results-pagination'; - pagination.currentPage = 1; - pagination.pageSize = 10; - const sortOption = { name: 'score', sortOrder: 'DESC', metadata: null }; - const sort: SortOptions = new SortOptions('score', SortDirection.DESC); - const mockResults = createSuccessfulRemoteDataObject$(['test', 'data']); - const searchServiceStub = jasmine.createSpyObj('SearchService', { - search: mockResults, - getEndpoint: observableOf('discover/search/objects'), - getSearchLink: '/mydspace', - getScopes: observableOf(['test-scope']), - setServiceOptions: {}, - getSearchConfigurationFor: createSuccessfulRemoteDataObject$({ sortOptions: [sortOption]}) + + const myDSpaceConfigurationServiceStub: SpyObj = jasmine.createSpyObj('MyDSpaceConfigurationService', { + getAvailableConfigurationOptions: jasmine.createSpy('getAvailableConfigurationOptions') }); - const configurationParam = 'default'; - const queryParam = 'test query'; - const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; - const paginatedSearchOptions = new PaginatedSearchOptions({ - configuration: configurationParam, - query: queryParam, - scope: scopeParam, - pagination, - sort - }); - const activatedRouteStub = { - snapshot: { - queryParamMap: new Map([ - ['query', queryParam], - ['scope', scopeParam] - ]) + + const configurationList = [ + { + value: MyDSpaceConfigurationValueType.Workspace, + label: `mydspace.show.${MyDSpaceConfigurationValueType.Workspace}`, + context: Context.Workspace }, - queryParams: observableOf({ - query: queryParam, - scope: scopeParam - }) - }; + { + value: MyDSpaceConfigurationValueType.Workflow, + label: `mydspace.show.${MyDSpaceConfigurationValueType.Workflow}`, + context: Context.Workflow + } + ]; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule], - declarations: [MyDSpacePageComponent, RoleDirective], + declarations: [MyDSpacePageComponent], providers: [ { provide: SearchService, useValue: searchServiceStub }, - { - provide: CommunityDataService, - useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) - }, - { provide: ActivatedRoute, useValue: activatedRouteStub }, - { provide: RouteService, useValue: routeServiceStub }, - { - provide: Store, useValue: store - }, - { - provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService', - { - isXs: observableOf(true), - isSm: observableOf(false), - isXsOrSm: observableOf(true) - }) - }, - { - provide: SidebarService, - useValue: SidebarServiceStub - }, - { - provide: SearchFilterService, - useValue: {} - }, { - provide: SEARCH_CONFIG_SERVICE, - useValue: new SearchConfigurationServiceStub() - }, - { - provide: RoleService, - useValue: new RoleServiceMock() - }, + { provide: MyDSpaceConfigurationService, useValue: myDSpaceConfigurationServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(MyDSpacePageComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } + set: { + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useValue: myDSpaceConfigurationServiceStub + } + ] + } }).compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(MyDSpacePageComponent); comp = fixture.componentInstance; // SearchPageComponent test instance + myDSpaceConfigurationServiceStub.getAvailableConfigurationOptions.and.returnValue(observableOf(configurationList)); + fixture.detectChanges(); - searchServiceObject = (comp as any).service; - searchConfigurationServiceObject = (comp as any).searchConfigService; + }); afterEach(() => { comp = null; - searchServiceObject = null; - searchConfigurationServiceObject = null; }); - it('should get the scope and query from the route parameters', () => { + it('should init properly context and configuration', fakeAsync(() => { - searchConfigurationServiceObject.paginatedSearchOptions.next(paginatedSearchOptions); - expect(comp.searchOptions$).toBeObservable(cold('b', { - b: paginatedSearchOptions + expect(comp.configurationList$).toBeObservable(cold('(a|)', { + a: configurationList })); - }); + flush(); + expect(comp.configuration).toBe(MyDSpaceConfigurationValueType.Workspace); + expect(comp.context).toBe(Context.Workspace); + })); - describe('when the open sidebar button is clicked in mobile view', () => { - - beforeEach(() => { - spyOn(comp, 'openSidebar'); - const openSidebarButton = fixture.debugElement.query(By.css('.open-sidebar')); - openSidebarButton.triggerEventHandler('click', null); - }); - - it('should trigger the openSidebar function', () => { - expect(comp.openSidebar).toHaveBeenCalled(); - }); - - }); - - describe('when sidebarCollapsed is true in mobile view', () => { - let menu: HTMLElement; - - beforeEach(() => { - menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement; - comp.isSidebarCollapsed = () => observableOf(true); - fixture.detectChanges(); - }); - - it('should close the sidebar', () => { - expect(menu.classList).not.toContain('active'); - }); - - }); - - describe('when sidebarCollapsed is false in mobile view', () => { - let menu: HTMLElement; - - beforeEach(() => { - menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement; - comp.isSidebarCollapsed = () => observableOf(false); - fixture.detectChanges(); - }); - - it('should open the menu', () => { - expect(menu.classList).toContain('active'); - }); - - }); - - describe('when stable', () => { - - beforeEach(() => { - fixture.detectChanges(); - }); - - it('should have initialized the sortOptions$ observable', (done) => { - - comp.sortOptions$.subscribe((sortOptions) => { - - expect(sortOptions.length).toEqual(2); - expect(sortOptions[0]).toEqual(new SortOptions('score', SortDirection.ASC)); - expect(sortOptions[1]).toEqual(new SortOptions('score', SortDirection.DESC)); - done(); - }); - - }); - - }); }); diff --git a/src/app/my-dspace-page/my-dspace-page.component.ts b/src/app/my-dspace-page/my-dspace-page.component.ts index 4fbebcd6a7..121d417bf1 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.ts +++ b/src/app/my-dspace-page/my-dspace-page.component.ts @@ -1,11 +1,8 @@ import { ChangeDetectionStrategy, Component, Inject, InjectionToken, OnInit } from '@angular/core'; -import { Observable, Subject } from 'rxjs'; +import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; -import { pushInOut } from '../shared/animations/push'; -import { HostWindowService } from '../shared/host-window.service'; import { SearchService } from '../core/shared/search/search.service'; -import { SidebarService } from '../shared/sidebar/sidebar.service'; import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service'; import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; @@ -25,7 +22,6 @@ export const SEARCH_CONFIG_SERVICE: InjectionToken = styleUrls: ['./my-dspace-page.component.scss'], templateUrl: './my-dspace-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - animations: [pushInOut], providers: [ { provide: SEARCH_CONFIG_SERVICE, @@ -55,15 +51,7 @@ export class MyDSpacePageComponent implements OnInit { */ viewModeList = [ViewMode.ListElement, ViewMode.DetailedListElement]; - - /** - * Emit an event every time search sidebars must refresh their contents. - */ - refreshFilters: Subject = new Subject(); - constructor(private service: SearchService, - private sidebarService: SidebarService, - private windowService: HostWindowService, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) { this.service.setServiceOptions(MyDSpaceResponseParsingService, MyDSpaceRequest); } diff --git a/src/app/search-navbar/search-navbar.component.spec.ts b/src/app/search-navbar/search-navbar.component.spec.ts index ba08c7ca75..fbdcdc951c 100644 --- a/src/app/search-navbar/search-navbar.component.spec.ts +++ b/src/app/search-navbar/search-navbar.component.spec.ts @@ -2,18 +2,14 @@ import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angul import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { Router } from '@angular/router'; +import { NavigationExtras, Router } from '@angular/router'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { SearchService } from '../core/shared/search/search.service'; import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; import { SearchNavbarComponent } from './search-navbar.component'; -import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; -import { of as observableOf } from 'rxjs'; -import { PaginationService } from '../core/pagination/pagination.service'; -import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; +import { RouterTestingModule } from '@angular/router/testing'; describe('SearchNavbarComponent', () => { let component: SearchNavbarComponent; @@ -41,6 +37,7 @@ describe('SearchNavbarComponent', () => { FormsModule, ReactiveFormsModule, BrowserAnimationsModule, + RouterTestingModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, @@ -49,10 +46,7 @@ describe('SearchNavbarComponent', () => { })], declarations: [SearchNavbarComponent], providers: [ - { provide: SearchService, useValue: mockSearchService }, - { provide: PaginationService, useValue: paginationService }, - { provide: Router, useValue: routerStub }, - { provide: SearchConfigurationService, useValue: {paginationID: 'page-id'} } + { provide: SearchService, useValue: mockSearchService } ] }) .compileComponents(); @@ -61,8 +55,8 @@ describe('SearchNavbarComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(SearchNavbarComponent); component = fixture.componentInstance; + router = TestBed.inject(Router); fixture.detectChanges(); - router = (component as any).router; }); it('should create', () => { @@ -73,7 +67,7 @@ describe('SearchNavbarComponent', () => { beforeEach(fakeAsync(() => { spyOn(component, 'expand').and.callThrough(); spyOn(component, 'onSubmit').and.callThrough(); - spyOn(router, 'navigate').and.callThrough(); + spyOn(router, 'navigate'); const searchIcon = fixture.debugElement.query(By.css('#search-navbar-container form .submit-icon')); searchIcon.triggerEventHandler('click', { preventDefault: () => {/**/ @@ -99,8 +93,9 @@ describe('SearchNavbarComponent', () => { fixture.detectChanges(); })); it('to search page with empty query', () => { + const extras: NavigationExtras = {queryParams: { query: '' }, queryParamsHandling: 'merge'}; expect(component.onSubmit).toHaveBeenCalledWith({ query: '' }); - expect(paginationService.updateRouteWithUrl).toHaveBeenCalled(); + expect(router.navigate).toHaveBeenCalledWith(['search'], extras); }); }); }); @@ -123,8 +118,10 @@ describe('SearchNavbarComponent', () => { fixture.detectChanges(); })); it('to search page with query', async () => { + const extras: NavigationExtras = { queryParams: { query: 'test' }, queryParamsHandling: 'merge'}; expect(component.onSubmit).toHaveBeenCalledWith({ query: 'test' }); - expect(paginationService.updateRouteWithUrl).toHaveBeenCalled(); + + expect(router.navigate).toHaveBeenCalledWith(['search'], extras); }); }); }); diff --git a/src/app/search-navbar/search-navbar.component.ts b/src/app/search-navbar/search-navbar.component.ts index 26849adf6d..ccdaa27861 100644 --- a/src/app/search-navbar/search-navbar.component.ts +++ b/src/app/search-navbar/search-navbar.component.ts @@ -62,8 +62,9 @@ export class SearchNavbarComponent { onSubmit(data: any) { this.collapse(); const queryParams = Object.assign({}, data); - const linkToNavigateTo = this.searchService.getSearchLink().split('/'); + const linkToNavigateTo = [this.searchService.getSearchLink().replace('/', '')]; this.searchForm.reset(); + this.router.navigate(linkToNavigateTo, { queryParams: queryParams, queryParamsHandling: 'merge' diff --git a/src/app/shared/search/search.component.spec.ts b/src/app/shared/search/search.component.spec.ts index bfa4931670..25b0814579 100644 --- a/src/app/shared/search/search.component.spec.ts +++ b/src/app/shared/search/search.component.spec.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, flush, TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; import { cold } from 'jasmine-marbles'; -import { of as observableOf } from 'rxjs'; +import { BehaviorSubject, of as observableOf } from 'rxjs'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { CommunityDataService } from '../../core/data/community-data.service'; import { HostWindowService } from '../host-window.service'; @@ -21,11 +21,10 @@ import { SearchFilterService } from '../../core/shared/search/search-filter.serv import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { RouteService } from '../../core/services/route.service'; -import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { PaginatedSearchOptions } from './models/paginated-search-options.model'; import { SidebarServiceStub } from '../testing/sidebar-service.stub'; -import { SearchConfig } from '../../core/shared/search/search-filters/search-config.model'; -import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { SearchConfig, SortConfig } from '../../core/shared/search/search-filters/search-config.model'; let comp: SearchComponent; let fixture: ComponentFixture; @@ -37,23 +36,29 @@ const store: Store = jasmine.createSpyObj('store', { /* tslint:enable:no-empty */ select: observableOf(true) }); -const sortOptionsList = [ +const sortConfigList: SortConfig[] = [ + { name: 'score', sortOrder: SortDirection.DESC }, + { name: 'dc.title', sortOrder: SortDirection.ASC }, + { name: 'dc.title', sortOrder: SortDirection.DESC } +]; +const sortOptionsList: SortOptions[] = [ new SortOptions('score', SortDirection.DESC), new SortOptions('dc.title', SortDirection.ASC), new SortOptions('dc.title', SortDirection.DESC) ]; const searchConfig = Object.assign(new SearchConfig(), { - sortOptions: sortOptionsList + sortOptions: sortConfigList }); +const paginationId = 'search-test-page-id'; const pagination: PaginationComponentOptions = new PaginationComponentOptions(); -pagination.id = 'search-results-pagination'; +pagination.id = paginationId; pagination.currentPage = 1; pagination.pageSize = 10; -const sortOption = { name: 'score', sortOrder: 'DESC', metadata: null }; + const sort: SortOptions = new SortOptions('score', SortDirection.DESC); -const mockResults = createSuccessfulRemoteDataObject$(['test', 'data']); +const mockResults$ = createSuccessfulRemoteDataObject$(['test', 'data']); const searchServiceStub = jasmine.createSpyObj('SearchService', { - search: mockResults, + search: mockResults$, getSearchLink: '/search', getScopes: observableOf(['test-scope']), getSearchConfigurationFor: createSuccessfulRemoteDataObject$(searchConfig) @@ -62,6 +67,11 @@ const configurationParam = 'default'; const queryParam = 'test query'; const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; const fixedFilter = 'fixed filter'; + +const defaultSearchOptions = new PaginatedSearchOptions({ pagination }); + +const paginatedSearchOptions$ = new BehaviorSubject(defaultSearchOptions); + const paginatedSearchOptions = new PaginatedSearchOptions({ configuration: configurationParam, query: queryParam, @@ -97,12 +107,14 @@ const routeServiceStub = { const searchConfigurationServiceStub = jasmine.createSpyObj('SearchConfigurationService', { + getConfigurationSortOptions: jasmine.createSpy('getConfigurationSortOptions'), getConfigurationSearchConfig: jasmine.createSpy('getConfigurationSearchConfig'), getCurrentConfiguration: jasmine.createSpy('getCurrentConfiguration'), getCurrentScope: jasmine.createSpy('getCurrentScope'), + getCurrentSort: jasmine.createSpy('getCurrentSort'), updateFixedFilter: jasmine.createSpy('updateFixedFilter'), setPaginationId: jasmine.createSpy('setPaginationId') -}); +}, ['paginatedSearchOptions']); export function configureSearchComponentTestingModule(compType, additionalDeclarations: any[] = []) { TestBed.configureTestingModule({ @@ -146,7 +158,7 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar }).compileComponents(); } -fdescribe('SearchComponent', () => { +describe('SearchComponent', () => { beforeEach(waitForAsync(() => { configureSearchComponentTestingModule(SearchComponent); })); @@ -155,17 +167,25 @@ fdescribe('SearchComponent', () => { fixture = TestBed.createComponent(SearchComponent); comp = fixture.componentInstance; // SearchComponent test instance comp.inPlaceSearch = false; + comp.paginationId = paginationId; - // searchConfigurationServiceStub.paginatedSearchOptions.and.returnValue(observableOf(paginatedSearchOptions)); searchConfigurationServiceStub.getConfigurationSearchConfig.and.returnValue(observableOf(searchConfig)); + searchConfigurationServiceStub.getConfigurationSortOptions.and.returnValue(sortOptionsList); searchConfigurationServiceStub.getCurrentConfiguration.and.returnValue(observableOf('default')); searchConfigurationServiceStub.getCurrentScope.and.returnValue(observableOf('test-id')); + searchConfigurationServiceStub.getCurrentSort.and.returnValue(observableOf(sortOptionsList[0])); + searchConfigurationServiceStub.setPaginationId.and.callFake((pageId) => { + paginatedSearchOptions$.next(Object.assign(paginatedSearchOptions$.value, { + pagination: Object.assign(new PaginationComponentOptions(), { + id: pageId + }) + })); + }); + spyOn((comp as any), 'getSearchOptions').and.returnValue(paginatedSearchOptions$.asObservable()); - searchServiceObject = TestBed.inject(SearchService); + searchServiceObject = TestBed.inject(SearchService); searchConfigurationServiceObject = TestBed.inject(SEARCH_CONFIG_SERVICE); - searchConfigurationServiceObject.paginatedSearchOptions = new BehaviorSubject(paginatedSearchOptions); - fixture.detectChanges(); }); afterEach(() => { @@ -174,45 +194,52 @@ fdescribe('SearchComponent', () => { searchConfigurationServiceObject = null; }); - it('should get the scope and query from the route parameters', () => { + it('should init search parameters properly and call retrieveSearchResults', fakeAsync(() => { + spyOn((comp as any), 'retrieveSearchResults').and.callThrough(); + fixture.detectChanges(); + flush(); - expect(comp.searchOptions$).toBeObservable(cold('b', { - b: paginatedSearchOptions + const expectedSearchOptions = Object.assign(paginatedSearchOptions$.value, { + configuration: 'default', + sort: sortOptionsList[0] + }); + expect(comp.currentConfiguration$).toBeObservable(cold('b', { + b: 'default' })); + expect(comp.currentSortOptions$).toBeObservable(cold('b', { + b: sortOptionsList[0] + })); + expect(comp.sortOptionsList$).toBeObservable(cold('b', { + b: sortOptionsList + })); + expect(comp.searchOptions$).toBeObservable(cold('b', { + b: expectedSearchOptions + })); + expect((comp as any).retrieveSearchResults).toHaveBeenCalledWith(expectedSearchOptions); + })); - }); + it('should retrieve SearchResults', fakeAsync(() => { + fixture.detectChanges(); + flush(); + const expectedResults = createSuccessfulRemoteDataObject(['test', 'data']); + expect(comp.resultsRD$).toBeObservable(cold('b', { + b: expectedResults + })); + })); - xdescribe('when the open sidebar button is clicked in mobile view', () => { + describe('when the open sidebar button is clicked in mobile view', () => { - beforeEach(() => { + beforeEach(fakeAsync(() => { spyOn(comp, 'openSidebar'); + fixture.detectChanges(); + flush(); const openSidebarButton = fixture.debugElement.query(By.css('.open-sidebar')); openSidebarButton.triggerEventHandler('click', null); - }); + })); it('should trigger the openSidebar function', () => { expect(comp.openSidebar).toHaveBeenCalled(); }); }); - - describe('when stable', () => { - - beforeEach(() => { - fixture.detectChanges(); - }); - - it('should have initialized the sortOptions$ observable', (done) => { - - comp.sortOptionsList$.subscribe((sortOptions) => { - - expect(sortOptions.length).toEqual(2); - expect(sortOptions[0]).toEqual(new SortOptions('score', SortDirection.ASC)); - expect(sortOptions[1]).toEqual(new SortOptions('score', SortDirection.DESC)); - done(); - }); - - }); - - }); }); diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index 2f18507652..a27f9ff0da 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -204,6 +204,7 @@ export class SearchComponent implements OnInit { return searchOptions.pagination.id === this.paginationId; }) ).subscribe(([configuration, searchSortOptions, searchOptions, sortOption]: [string, SortOptions[], PaginatedSearchOptions, SortOptions]) => { + // Build the PaginatedSearchOptions object const combinedOptions = Object.assign({}, searchOptions, { @@ -211,7 +212,6 @@ export class SearchComponent implements OnInit { sort: sortOption || searchOptions.sort }); const newSearchOptions = new PaginatedSearchOptions(combinedOptions); - // Initialize variables this.currentConfiguration$.next(configuration); this.currentSortOptions$.next(newSearchOptions.sort); @@ -277,7 +277,6 @@ export class SearchComponent implements OnInit { followLink('thumbnail', { isOptional: true }) ).pipe(getFirstCompletedRemoteData()) .subscribe((results: RemoteData>) => { - console.log('results ', results); this.resultsRD$.next(results); }); } From 0fe6d4536c26122d1b14beecf0fbb996717c15e0 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 20 Dec 2021 12:22:52 -0600 Subject: [PATCH 047/102] update readme --- README.md | 5 +++-- docs/Configuration.md | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 829f7121b2..1982fac452 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ All other settings can be set using the following convention for naming the envi 1. replace all `.` with `_` 2. convert all characters to upper case +3. prefix with `DSPACE_` e.g. @@ -161,11 +162,11 @@ export DSPACE_HOST=api7.dspace.org export DSPACE_UI_PORT=4200 ``` -The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`** +The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `DSPACE_APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`** These configuration sources are collected **at run time**, and written to `dist/browser/assets/config.json` for production and `src/app/assets/config.json` for development. -The configuration file can be externalized by using environment variable `APP_CONFIG_PATH`. +The configuration file can be externalized by using environment variable `DSPACE_APP_CONFIG_PATH`. #### Using environment variables in code To use environment variables in a UI component, use: diff --git a/docs/Configuration.md b/docs/Configuration.md index 193ca0f57d..66d8d492be 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -72,8 +72,8 @@ Settings can be set using the following convention for naming the environment va e.g. ``` -cache.msToLive.default => CACHE_MSTOLIVE_DEFAULT -auth.ui.timeUntilIdle => AUTH_UI_TIMEUNTILIDLE +cache.msToLive.default => DSPACE_CACHE_MSTOLIVE_DEFAULT +auth.ui.timeUntilIdle => DSPACE_AUTH_UI_TIMEUNTILIDLE ``` ## Supporting analytics services other than Google Analytics From d59e8becc31a4eeb75686254aac843d9d788d306 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 20 Dec 2021 15:28:19 -0600 Subject: [PATCH 048/102] specify NODE_ENV for production scripts --- package.json | 3 ++- yarn.lock | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 002ee3bd5c..3d652c2246 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "start:prod": "yarn run build:prod && yarn run serve:ssr", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod", "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", - "serve:ssr": "node dist/server/main", + "serve:ssr": "cross-env NODE_ENV=production node dist/server/main", "analyze": "webpack-bundle-analyzer dist/browser/stats.json", "build": "ng build", "build:stats": "ng build --stats-json", @@ -144,6 +144,7 @@ "codelyzer": "^6.0.0", "compression-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^6.4.1", + "cross-env": "^7.0.3", "css-loader": "3.4.0", "cssnano": "^4.1.10", "cypress": "8.6.0", diff --git a/yarn.lock b/yarn.lock index ff0d9c6ff5..0f8ef0a6e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4469,6 +4469,13 @@ critters@0.0.7: parse5-htmlparser2-tree-adapter "^6.0.1" pretty-bytes "^5.3.0" +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -4480,7 +4487,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== From f75854b77b15f89b017895e229b4d4f0f57c2040 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 20 Dec 2021 15:29:04 -0600 Subject: [PATCH 049/102] default to production --- src/config/config.server.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/config.server.ts b/src/config/config.server.ts index ade4e90591..80e7b72261 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -31,7 +31,8 @@ const getNumberFromString = (variable: string): number => { }; const getEnvironment = (): Environment => { - let environment: Environment = 'development'; + // default to production + let environment: Environment = 'production'; if (isNotEmpty(ENV('NODE_ENV'))) { switch (ENV('NODE_ENV')) { case 'prod': @@ -43,6 +44,7 @@ const getEnvironment = (): Environment => { break; case 'dev': case 'development': + environment = 'development'; break; default: console.warn(`Unknown NODE_ENV ${ENV('NODE_ENV')}. Defaulting to development`); From f9328da82691aad702e3974c2d49f75733918c12 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 20 Dec 2021 15:29:21 -0600 Subject: [PATCH 050/102] add theme head tags to example --- config/config.example.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/config/config.example.yml b/config/config.example.yml index 165d94b7bf..7ff8d1d23c 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -222,6 +222,25 @@ themes: # - name: BASE_THEME_NAME # - name: dspace + headTags: + - tagName: link + attributes: + rel: icon + href: assets/dspace/images/favicons/favicon.ico + sizes: any + - tagName: link + attributes: + rel: icon + href: assets/dspace/images/favicons/favicon.svg + type: image/svg+xml + - tagName: link + attributes: + rel: apple-touch-icon + href: assets/dspace/images/favicons/apple-touch-icon.png + - tagName: link + attributes: + rel: manifest + href: assets/dspace/images/favicons/manifest.webmanifest # Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). # For images, this enables a gallery viewer where you can zoom or page through images. From 66555c9fc135fceca7589306901f175819476657 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 20 Dec 2021 15:36:36 -0600 Subject: [PATCH 051/102] move NODE_ENV production to start:prod --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3d652c2246..a500058e41 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,10 @@ "test:rest": "ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", "start": "yarn run start:prod", "start:dev": "nodemon --exec \"yarn run serve\"", - "start:prod": "yarn run build:prod && yarn run serve:ssr", + "start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod", "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", - "serve:ssr": "cross-env NODE_ENV=production node dist/server/main", + "serve:ssr": "node dist/server/main", "analyze": "webpack-bundle-analyzer dist/browser/stats.json", "build": "ng build", "build:stats": "ng build --stats-json", From 21e78e33e525b95a6ea64040f5b9636abadbf87c Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Tue, 14 Sep 2021 10:20:29 +0200 Subject: [PATCH 052/102] [CST-5065] Admin is unable to reset other user password (cherry pick from DSC-215) --- .../eperson-form/eperson-form.component.html | 4 ++++ .../eperson-form/eperson-form.component.ts | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html index 45326c1abc..5d80f24990 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html @@ -36,9 +36,13 @@ + +
{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}
+ + = observableOf(false); + canReset$: Observable; /** * Observable whether or not the admin is allowed to delete the EPerson @@ -310,6 +310,11 @@ export class EPersonFormComponent implements OnInit, OnDestroy { this.canDelete$ = activeEPerson$.pipe( switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined)) ); + this.canReset$ = activeEPerson$.pipe( + switchMap((eperson) => { + return observableOf(true); + }) + ); }); } From 6a1bbc8afc9551728ba662303e4018ecb7408f9a Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 21 Dec 2021 12:43:13 +0100 Subject: [PATCH 053/102] [CST-5065] Admin is unable to reset other user password (function implementation) --- .../eperson-form/eperson-form.component.html | 4 +- .../eperson-form.component.spec.ts | 28 ++++++++++- .../eperson-form/eperson-form.component.ts | 47 ++++++++++++++----- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html index 5d80f24990..41ae67423c 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html @@ -19,7 +19,7 @@ class="btn btn-outline-secondary"> {{messagePrefix + '.return' | translate}}
-
@@ -42,7 +42,7 @@
{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}
- + { @@ -42,6 +43,7 @@ describe('EPersonFormComponent', () => { let authService: AuthServiceStub; let authorizationService: AuthorizationDataService; let groupsDataService: GroupDataService; + let epersonRegistrationService: EpersonRegistrationService; let paginationService; @@ -205,6 +207,10 @@ describe('EPersonFormComponent', () => { }).compileComponents(); })); + epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { + registerEmail: createSuccessfulRemoteDataObject$(null) + }); + beforeEach(() => { fixture = TestBed.createComponent(EPersonFormComponent); component = fixture.componentInstance; @@ -514,4 +520,24 @@ describe('EPersonFormComponent', () => { expect(component.epersonService.deleteEPerson).toHaveBeenCalledWith(eperson); }); }); + + + describe('Reset Password', () => { + let ePersonId; + let ePersonEmail; + + beforeEach(() => { + ePersonId = 'testEPersonId'; + ePersonEmail = 'person.email@4science.it'; + component.epersonInitial = Object.assign(new EPerson(), { + id: ePersonId, + email: ePersonEmail + }); + component.resetPassword(); + }); + + it('should call epersonRegistrationService.registerEmail', () => { + expect(epersonRegistrationService.registerEmail).toHaveBeenCalledWith(ePersonEmail); + }); + }); }); diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts index 8e248a17d8..0d6d9feaec 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts @@ -34,6 +34,8 @@ import { NoContent } from '../../../core/shared/NoContent.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { followLink } from '../../../shared/utils/follow-link-config.model'; import { ValidateEmailNotTaken } from './validators/email-taken.validator'; +import { Registration } from '../../../core/shared/registration.model'; +import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; @Component({ selector: 'ds-eperson-form', @@ -167,17 +169,20 @@ export class EPersonFormComponent implements OnInit, OnDestroy { */ emailValueChangeSubscribe: Subscription; - constructor(protected changeDetectorRef: ChangeDetectorRef, - public epersonService: EPersonDataService, - public groupsDataService: GroupDataService, - private formBuilderService: FormBuilderService, - private translateService: TranslateService, - private notificationsService: NotificationsService, - private authService: AuthService, - private authorizationService: AuthorizationDataService, - private modalService: NgbModal, - private paginationService: PaginationService, - public requestService: RequestService) { + constructor( + protected changeDetectorRef: ChangeDetectorRef, + public epersonService: EPersonDataService, + public groupsDataService: GroupDataService, + private formBuilderService: FormBuilderService, + private translateService: TranslateService, + private notificationsService: NotificationsService, + private authService: AuthService, + private authorizationService: AuthorizationDataService, + private modalService: NgbModal, + private paginationService: PaginationService, + public requestService: RequestService, + private epersonRegistrationService: EpersonRegistrationService, + ) { this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => { this.epersonInitial = eperson; if (hasValue(eperson)) { @@ -484,6 +489,26 @@ export class EPersonFormComponent implements OnInit, OnDestroy { this.isImpersonated = false; } + /** + * Sends an email to current eperson address with the information + * to reset password + */ + resetPassword() { + if (hasValue(this.epersonInitial.email)) { + this.epersonRegistrationService.registerEmail(this.epersonInitial.email).pipe(getFirstCompletedRemoteData()) + .subscribe((response: RemoteData) => { + if (response.hasSucceeded) { + this.notificationsService.success(this.translateService.get('admin.access-control.epeople.actions.reset'), + this.translateService.get('forgot-email.form.success.content', {email: this.epersonInitial.email})); + } else { + this.notificationsService.error(this.translateService.get('forgot-email.form.error.head'), + this.translateService.get('forgot-email.form.error.content', {email: this.epersonInitial.email})); + } + } + ); + } + } + /** * Cancel the current edit when component is destroyed & unsub all subscriptions */ From f46767be890f04af8204c6afe030aa81c893eb71 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 21 Dec 2021 13:06:16 +0100 Subject: [PATCH 054/102] [CST-5065] Tests fixed --- .../eperson-form/eperson-form.component.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 5593c77523..644b893265 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 @@ -2,7 +2,7 @@ import { Observable, of as observableOf } from 'rxjs'; import { CommonModule } from '@angular/common'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { BrowserModule, By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -14,6 +14,7 @@ import { EPerson } from '../../../core/eperson/models/eperson.model'; import { PageInfo } from '../../../core/shared/page-info.model'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { EPeopleRegistryComponent } from '../epeople-registry.component'; import { EPersonFormComponent } from './eperson-form.component'; import { EPersonMock, EPersonMock2 } from '../../../shared/testing/eperson.mock'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; @@ -28,11 +29,9 @@ 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 { FormControl, FormGroup, Validators } from '@angular/forms'; import { ValidateEmailNotTaken } from './validators/email-taken.validator'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; - describe('EPersonFormComponent', () => { let component: EPersonFormComponent; let fixture: ComponentFixture; @@ -201,7 +200,9 @@ describe('EPersonFormComponent', () => { { provide: AuthService, useValue: authService }, { provide: AuthorizationDataService, useValue: authorizationService }, { provide: PaginationService, useValue: paginationService }, - { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) } + { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring'])}, + { provide: EpersonRegistrationService, useValue: epersonRegistrationService }, + EPeopleRegistryComponent ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -521,7 +522,6 @@ describe('EPersonFormComponent', () => { }); }); - describe('Reset Password', () => { let ePersonId; let ePersonEmail; From 8f2ef71e3c223196326d21f52364a9c66905214d Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Tue, 21 Dec 2021 13:53:17 +0100 Subject: [PATCH 055/102] [CST-5065] Fix --- .../epeople-registry/eperson-form/eperson-form.component.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts index 0d6d9feaec..05fc3189d0 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts @@ -315,11 +315,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { this.canDelete$ = activeEPerson$.pipe( switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined)) ); - this.canReset$ = activeEPerson$.pipe( - switchMap((eperson) => { - return observableOf(true); - }) - ); + this.canReset$ = observableOf(true); }); } From 15dfa3cd82304f4611b866501c18ed9b22abac3c Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 21 Dec 2021 15:38:50 +0100 Subject: [PATCH 056/102] 85993: comcol/switcher/guard browse by changes + test updates --- .../browse-by-date-page.component.ts | 15 ++--- src/app/browse-by/browse-by-guard.spec.ts | 26 ++++++--- src/app/browse-by/browse-by-guard.ts | 57 +++++++++++-------- .../browse-by-metadata-page.component.ts | 4 +- .../browse-by-decorator.spec.ts | 8 +-- .../browse-by-switcher/browse-by-decorator.ts | 8 +-- .../browse-by-switcher.component.spec.ts | 50 ++++++++++++---- .../browse-by-switcher.component.ts | 12 ++-- .../browse-by-title-page.component.ts | 4 +- .../browse/browse-definition-data.service.ts | 15 +++++ .../core/shared/browse-definition.model.ts | 4 ++ src/app/navbar/navbar.component.spec.ts | 38 ++++++++++++- .../comcol-page-browse-by.component.ts | 36 +++++------- src/config/browse-by-config.interface.ts | 6 -- src/config/browse-by-type-config.interface.ts | 23 -------- src/config/global-config.interface.ts | 2 +- src/environments/environment.common.ts | 27 --------- src/environments/mock-environment.ts | 28 +-------- 18 files changed, 184 insertions(+), 179 deletions(-) delete mode 100644 src/config/browse-by-type-config.interface.ts diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index 3158c3d7cc..d0b63a00f9 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -12,12 +12,13 @@ import { ActivatedRoute, Params, Router } from '@angular/router'; import { BrowseService } from '../../core/browse/browse.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; -import { BrowseByType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { environment } from '../../../environments/environment'; import { PaginationService } from '../../core/pagination/pagination.service'; import { map } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; +import { BrowseDefinition } from '../../core/shared/browse-definition.model'; @Component({ selector: 'ds-browse-by-date-page', @@ -29,13 +30,13 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options * A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields. * An example would be 'dateissued' for 'dc.date.issued' */ -@rendersBrowseBy(BrowseByType.Date) +@rendersBrowseBy(BrowseByDataType.Date) export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { /** * The default metadata-field to use for determining the lower limit of the StartsWith dropdown options */ - defaultMetadataField = 'dc.date.issued'; + defaultBrowseDefinition = Object.assign(new BrowseDefinition(), {metadataKeys: ['dc.date.issued']}); public constructor(protected route: ActivatedRoute, protected browseService: BrowseService, @@ -59,13 +60,13 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { return [Object.assign({}, routeParams, queryParams, data), currentPage, currentSort]; }) ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { - const metadataField = params.metadataField || this.defaultMetadataField; + const browseDefinition = params.browseDefinition || this.defaultBrowseDefinition; this.browseId = params.id || this.defaultBrowseId; this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); this.updatePageWithItems(searchOptions, this.value); this.updateParent(params.scope); - this.updateStartsWithOptions(this.browseId, metadataField, params.scope); + this.updateStartsWithOptions(this.browseId, browseDefinition, params.scope); })); } @@ -79,12 +80,12 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { * @param metadataField The metadata field to fetch the earliest date from (expects a date field) * @param scope The scope under which to fetch the earliest item for */ - updateStartsWithOptions(definition: string, metadataField: string, scope?: string) { + updateStartsWithOptions(definition: string, browseDefinition: BrowseDefinition, scope?: string) { this.subs.push( this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData) => { let lowerLimit = environment.browseBy.defaultLowerLimit; if (hasValue(firstItemRD.payload)) { - const date = firstItemRD.payload.firstMetadataValue(metadataField); + const date = firstItemRD.payload.firstMetadataValue(browseDefinition.metadataKeys); if (hasValue(date)) { const dateObj = new Date(date); // TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC. diff --git a/src/app/browse-by/browse-by-guard.spec.ts b/src/app/browse-by/browse-by-guard.spec.ts index 4592f47175..fc483d87e2 100644 --- a/src/app/browse-by/browse-by-guard.spec.ts +++ b/src/app/browse-by/browse-by-guard.spec.ts @@ -1,20 +1,25 @@ import { first } from 'rxjs/operators'; import { BrowseByGuard } from './browse-by-guard'; import { of as observableOf } from 'rxjs'; +import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { BrowseDefinition } from '../core/shared/browse-definition.model'; +import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator'; describe('BrowseByGuard', () => { describe('canActivate', () => { let guard: BrowseByGuard; let dsoService: any; let translateService: any; + let browseDefinitionService: any; const name = 'An interesting DSO'; const title = 'Author'; const field = 'Author'; const id = 'author'; - const metadataField = 'dc.contributor'; const scope = '1234-65487-12354-1235'; const value = 'Filter'; + const browseDefinition = Object.assign(new BrowseDefinition(), { type: BrowseByDataType.Metadata, metadataKeys: ['dc.contributor'] }); beforeEach(() => { dsoService = { @@ -24,14 +29,19 @@ describe('BrowseByGuard', () => { translateService = { instant: () => field }; - guard = new BrowseByGuard(dsoService, translateService); + + browseDefinitionService = { + findById: () => createSuccessfulRemoteDataObject$(browseDefinition) + }; + + guard = new BrowseByGuard(dsoService, translateService, browseDefinitionService); }); it('should return true, and sets up the data correctly, with a scope and value', () => { const scopedRoute = { data: { title: field, - metadataField, + browseDefinition, }, params: { id, @@ -48,7 +58,7 @@ describe('BrowseByGuard', () => { const result = { title, id, - metadataField, + browseDefinition, collection: name, field, value: '"' + value + '"' @@ -63,7 +73,7 @@ describe('BrowseByGuard', () => { const scopedNoValueRoute = { data: { title: field, - metadataField, + browseDefinition, }, params: { id, @@ -80,7 +90,7 @@ describe('BrowseByGuard', () => { const result = { title, id, - metadataField, + browseDefinition, collection: name, field, value: '' @@ -95,7 +105,7 @@ describe('BrowseByGuard', () => { const route = { data: { title: field, - metadataField, + browseDefinition, }, params: { id, @@ -111,7 +121,7 @@ describe('BrowseByGuard', () => { const result = { title, id, - metadataField, + browseDefinition, collection: '', field, value: '"' + value + '"' diff --git a/src/app/browse-by/browse-by-guard.ts b/src/app/browse-by/browse-by-guard.ts index 8ac77bbd64..0a46d59613 100644 --- a/src/app/browse-by/browse-by-guard.ts +++ b/src/app/browse-by/browse-by-guard.ts @@ -2,11 +2,14 @@ import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angul import { Injectable } from '@angular/core'; import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service'; import { hasNoValue, hasValue } from '../shared/empty.util'; -import { map } from 'rxjs/operators'; -import { getFirstSucceededRemoteData } from '../core/shared/operators'; +import { map, switchMap } from 'rxjs/operators'; +import { getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { TranslateService } from '@ngx-translate/core'; -import { of as observableOf } from 'rxjs'; +import { Observable, of as observableOf } from 'rxjs'; import { environment } from '../../environments/environment'; +import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service'; +import { RemoteData } from '../core/data/remote-data'; +import { BrowseDefinition } from '../core/shared/browse-definition.model'; @Injectable() /** @@ -15,42 +18,46 @@ import { environment } from '../../environments/environment'; export class BrowseByGuard implements CanActivate { constructor(protected dsoService: DSpaceObjectDataService, - protected translate: TranslateService) { + protected translate: TranslateService, + protected browseDefinitionService: BrowseDefinitionDataService) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { const title = route.data.title; const id = route.params.id || route.queryParams.id || route.data.id; - let metadataField = route.data.metadataField; - if (hasNoValue(metadataField) && hasValue(id)) { - const config = environment.browseBy.types.find((conf) => conf.id === id); - if (hasValue(config) && hasValue(config.metadataField)) { - metadataField = config.metadataField; - } + let browseDefinition$: Observable; + if (hasNoValue(route.data.browseDefinition) && hasValue(id)) { + browseDefinition$ = this.browseDefinitionService.findById(id).pipe(getFirstSucceededRemoteDataPayload()); + } else { + browseDefinition$ = observableOf(route.data.browseDefinition); } const scope = route.queryParams.scope; const value = route.queryParams.value; const metadataTranslated = this.translate.instant('browse.metadata.' + id); - if (hasValue(scope)) { - const dsoAndMetadata$ = this.dsoService.findById(scope).pipe(getFirstSucceededRemoteData()); - return dsoAndMetadata$.pipe( - map((dsoRD) => { - const name = dsoRD.payload.name; - route.data = this.createData(title, id, metadataField, name, metadataTranslated, value, route); - return true; - }) - ); - } else { - route.data = this.createData(title, id, metadataField, '', metadataTranslated, value, route); - return observableOf(true); - } + return browseDefinition$.pipe( + switchMap((browseDefinition) => { + if (hasValue(scope)) { + const dsoAndMetadata$ = this.dsoService.findById(scope).pipe(getFirstSucceededRemoteData()); + return dsoAndMetadata$.pipe( + map((dsoRD) => { + const name = dsoRD.payload.name; + route.data = this.createData(title, id, browseDefinition, name, metadataTranslated, value, route); + return true; + }) + ); + } else { + route.data = this.createData(title, id, browseDefinition, '', metadataTranslated, value, route); + return observableOf(true); + } + }) + ) } - private createData(title, id, metadataField, collection, field, value, route) { + private createData(title, id, browseDefinition, collection, field, value, route) { return Object.assign({}, route.data, { title: title, id: id, - metadataField: metadataField, + browseDefinition: browseDefinition, collection: collection, field: field, value: hasValue(value) ? `"${value}"` : '' diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 3573ffb264..6655f98392 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -14,7 +14,7 @@ import { getFirstSucceededRemoteData } from '../../core/shared/operators'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; -import { BrowseByType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { PaginationService } from '../../core/pagination/pagination.service'; import { map } from 'rxjs/operators'; @@ -28,7 +28,7 @@ import { map } from 'rxjs/operators'; * A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields. * An example would be 'author' for 'dc.contributor.*' */ -@rendersBrowseBy(BrowseByType.Metadata) +@rendersBrowseBy(BrowseByDataType.Metadata) export class BrowseByMetadataPageComponent implements OnInit { /** diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts index f54efb9378..19a6277151 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts @@ -1,9 +1,9 @@ -import { BrowseByType, rendersBrowseBy } from './browse-by-decorator'; +import { BrowseByDataType, rendersBrowseBy } from './browse-by-decorator'; describe('BrowseByDecorator', () => { - const titleDecorator = rendersBrowseBy(BrowseByType.Title); - const dateDecorator = rendersBrowseBy(BrowseByType.Date); - const metadataDecorator = rendersBrowseBy(BrowseByType.Metadata); + const titleDecorator = rendersBrowseBy(BrowseByDataType.Title); + const dateDecorator = rendersBrowseBy(BrowseByDataType.Date); + const metadataDecorator = rendersBrowseBy(BrowseByDataType.Metadata); it('should have a decorator for all types', () => { expect(titleDecorator.length).not.toEqual(0); expect(dateDecorator.length).not.toEqual(0); diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts index efb4a4a9f4..1ebaa7face 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts @@ -2,13 +2,13 @@ import { hasNoValue } from '../../shared/empty.util'; import { InjectionToken } from '@angular/core'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -export enum BrowseByType { +export enum BrowseByDataType { Title = 'title', - Metadata = 'metadata', + Metadata = 'text', Date = 'date' } -export const DEFAULT_BROWSE_BY_TYPE = BrowseByType.Metadata; +export const DEFAULT_BROWSE_BY_TYPE = BrowseByDataType.Metadata; export const BROWSE_BY_COMPONENT_FACTORY = new InjectionToken<(browseByType) => GenericConstructor>('getComponentByBrowseByType', { providedIn: 'root', @@ -21,7 +21,7 @@ const map = new Map(); * Decorator used for rendering Browse-By pages by type * @param browseByType The type of page */ -export function rendersBrowseBy(browseByType: BrowseByType) { +export function rendersBrowseBy(browseByType: BrowseByDataType) { return function decorator(component: any) { if (hasNoValue(map.get(browseByType))) { map.set(browseByType, component); diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts index f340237e26..cb82ddb7c4 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts @@ -2,20 +2,46 @@ import { BrowseBySwitcherComponent } from './browse-by-switcher.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { BehaviorSubject } from 'rxjs'; -import { environment } from '../../../environments/environment'; -import { BROWSE_BY_COMPONENT_FACTORY } from './browse-by-decorator'; +import { BROWSE_BY_COMPONENT_FACTORY, BrowseByDataType } from './browse-by-decorator'; +import { BrowseDefinition } from '../../core/shared/browse-definition.model'; +import { BehaviorSubject, of as observableOf } from 'rxjs'; describe('BrowseBySwitcherComponent', () => { let comp: BrowseBySwitcherComponent; let fixture: ComponentFixture; - const types = environment.browseBy.types; + const types = [ + Object.assign( + new BrowseDefinition(), { + id: 'title', + dataType: BrowseByDataType.Title, + } + ), + Object.assign( + new BrowseDefinition(), { + id: 'dateissued', + dataType: BrowseByDataType.Date, + metadataKeys: ['dc.date.issued'] + } + ), + Object.assign( + new BrowseDefinition(), { + id: 'author', + dataType: BrowseByDataType.Metadata, + } + ), + Object.assign( + new BrowseDefinition(), { + id: 'subject', + dataType: BrowseByDataType.Metadata, + } + ), + ]; - const params = new BehaviorSubject(createParamsWithId('initialValue')); + const data = new BehaviorSubject(createDataWithBrowseDefinition(new BrowseDefinition())); const activatedRouteStub = { - params: params + data }; beforeEach(waitForAsync(() => { @@ -34,20 +60,20 @@ describe('BrowseBySwitcherComponent', () => { comp = fixture.componentInstance; })); - types.forEach((type) => { + types.forEach((type: BrowseDefinition) => { describe(`when switching to a browse-by page for "${type.id}"`, () => { beforeEach(() => { - params.next(createParamsWithId(type.id)); + data.next(createDataWithBrowseDefinition(type)); fixture.detectChanges(); }); - it(`should call getComponentByBrowseByType with type "${type.type}"`, () => { - expect((comp as any).getComponentByBrowseByType).toHaveBeenCalledWith(type.type); + it(`should call getComponentByBrowseByType with type "${type.dataType}"`, () => { + expect((comp as any).getComponentByBrowseByType).toHaveBeenCalledWith(type.dataType); }); }); }); }); -export function createParamsWithId(id) { - return { id: id }; +export function createDataWithBrowseDefinition(browseDefinition) { + return { browseDefinition: browseDefinition }; } diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts index 043a4ce90a..55fcebf477 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts @@ -1,11 +1,11 @@ import { Component, Inject, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; -import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; import { map } from 'rxjs/operators'; import { BROWSE_BY_COMPONENT_FACTORY } from './browse-by-decorator'; import { environment } from '../../../environments/environment'; import { GenericConstructor } from '../../core/shared/generic-constructor'; +import { BrowseDefinition } from '../../core/shared/browse-definition.model'; @Component({ selector: 'ds-browse-by-switcher', @@ -26,15 +26,11 @@ export class BrowseBySwitcherComponent implements OnInit { } /** - * Fetch the correct browse-by component by using the relevant config from environment.js + * Fetch the correct browse-by component by using the relevant config from the route data */ ngOnInit(): void { - this.browseByComponent = this.route.params.pipe( - map((params) => { - const id = params.id; - return environment.browseBy.types.find((config: BrowseByTypeConfig) => config.id === id); - }), - map((config: BrowseByTypeConfig) => this.getComponentByBrowseByType(config.type)) + this.browseByComponent = this.route.data.pipe( + map((data: { browseDefinition: BrowseDefinition }) => this.getComponentByBrowseByType(data.browseDefinition.dataType)) ); } diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index b3a2ceed00..b4a8331458 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -10,7 +10,7 @@ import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search- import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { BrowseService } from '../../core/browse/browse.service'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { BrowseByType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { PaginationService } from '../../core/pagination/pagination.service'; import { map } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; @@ -23,7 +23,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c /** * Component for browsing items by title (dc.title) */ -@rendersBrowseBy(BrowseByType.Title) +@rendersBrowseBy(BrowseByDataType.Title) export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { public constructor(protected route: ActivatedRoute, diff --git a/src/app/core/browse/browse-definition-data.service.ts b/src/app/core/browse/browse-definition-data.service.ts index 31338417ca..dd66d8fa53 100644 --- a/src/app/core/browse/browse-definition-data.service.ts +++ b/src/app/core/browse/browse-definition-data.service.ts @@ -106,6 +106,21 @@ export class BrowseDefinitionDataService { findAllByHref(href: string, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { return this.dataService.findAllByHref(href, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } + + /** + * Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of + * {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object + * @param id ID of object we want to retrieve + * @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 + */ + findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable> { + return this.dataService.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } } /* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/shared/browse-definition.model.ts b/src/app/core/shared/browse-definition.model.ts index 2c08417b6d..b5dc47cd3e 100644 --- a/src/app/core/shared/browse-definition.model.ts +++ b/src/app/core/shared/browse-definition.model.ts @@ -6,6 +6,7 @@ import { BROWSE_DEFINITION } from './browse-definition.resource-type'; import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; import { SortOption } from './sort-option.model'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; @typedObject export class BrowseDefinition extends CacheableObject { @@ -33,6 +34,9 @@ export class BrowseDefinition extends CacheableObject { @autoserializeAs('metadata') metadataKeys: string[]; + @autoserializeAs('dataType') + dataType: BrowseByDataType; + get self(): string { return this._links.self.href; } diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts index cbe6738241..5aa2bf1786 100644 --- a/src/app/navbar/navbar.component.spec.ts +++ b/src/app/navbar/navbar.component.spec.ts @@ -13,15 +13,48 @@ import { MenuService } from '../shared/menu/menu.service'; import { MenuServiceStub } from '../shared/testing/menu-service.stub'; import { ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; +import { BrowseService } from '../core/browse/browse.service'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { buildPaginatedList } from '../core/data/paginated-list.model'; +import { BrowseDefinition } from '../core/shared/browse-definition.model'; +import { BrowseByDataType } from '../browse-by/browse-by-switcher/browse-by-decorator'; let comp: NavbarComponent; let fixture: ComponentFixture; describe('NavbarComponent', () => { const menuService = new MenuServiceStub(); - + let browseDefinitions; // waitForAsync beforeEach beforeEach(waitForAsync(() => { + browseDefinitions = [ + Object.assign( + new BrowseDefinition(), { + id: 'title', + dataType: BrowseByDataType.Title, + } + ), + Object.assign( + new BrowseDefinition(), { + id: 'dateissued', + dataType: BrowseByDataType.Date, + metadataKeys: ['dc.date.issued'] + } + ), + Object.assign( + new BrowseDefinition(), { + id: 'author', + dataType: BrowseByDataType.Metadata, + } + ), + Object.assign( + new BrowseDefinition(), { + id: 'subject', + dataType: BrowseByDataType.Metadata, + } + ), + ]; + TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), @@ -33,7 +66,8 @@ describe('NavbarComponent', () => { Injector, { provide: MenuService, useValue: menuService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, - { provide: ActivatedRoute, useValue: {} } + { provide: ActivatedRoute, useValue: {} }, + { provide: BrowseService, useValue: { getBrowseDefinitions: createSuccessfulRemoteDataObject$(buildPaginatedList(undefined, browseDefinitions)) } } ], schemas: [NO_ERRORS_SCHEMA] }) diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts index 5dec1e27f8..08f7ec67ee 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -2,8 +2,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; -import { environment } from '../../../environments/environment'; import { getCommunityPageRoute } from '../../community-page/community-page-routing-paths'; import { getCollectionPageRoute } from '../../collection-page/collection-page-routing-paths'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; @@ -34,10 +32,6 @@ export class ComcolPageBrowseByComponent implements OnInit { */ @Input() id: string; @Input() contentType: string; - /** - * List of currently active browse configurations - */ - types: BrowseByTypeConfig[]; allOptions: ComColPageNavOption[]; @@ -46,7 +40,7 @@ export class ComcolPageBrowseByComponent implements OnInit { constructor( private route: ActivatedRoute, private router: Router, - public browseService: BrowseService + private browseService: BrowseService ) { } @@ -62,23 +56,23 @@ export class ComcolPageBrowseByComponent implements OnInit { routerLink: `/browse/${config.id}`, params: { scope: this.id } })); + + if (this.contentType === 'collection') { + this.allOptions = [{ + id: this.id, + label: 'collection.page.browse.recent.head', + routerLink: getCollectionPageRoute(this.id) + }, ...this.allOptions]; + } else if (this.contentType === 'community') { + this.allOptions = [{ + id: this.id, + label: 'community.all-lists.head', + routerLink: getCommunityPageRoute(this.id) + }, ...this.allOptions]; + } } }); - if (this.contentType === 'collection') { - this.allOptions = [{ - id: this.id, - label: 'collection.page.browse.recent.head', - routerLink: getCollectionPageRoute(this.id) - }, ...this.allOptions]; - } else if (this.contentType === 'community') { - this.allOptions = [{ - id: this.id, - label: 'community.all-lists.head', - routerLink: getCommunityPageRoute(this.id) - }, ...this.allOptions]; - } - this.currentOptionId$ = this.route.params.pipe( map((params: Params) => params.id) ); diff --git a/src/config/browse-by-config.interface.ts b/src/config/browse-by-config.interface.ts index 719e127b4b..6adba66b92 100644 --- a/src/config/browse-by-config.interface.ts +++ b/src/config/browse-by-config.interface.ts @@ -1,5 +1,4 @@ import { Config } from './config.interface'; -import { BrowseByTypeConfig } from './browse-by-type-config.interface'; /** * Config that determines how the dropdown list of years are created for browse-by-date components @@ -19,9 +18,4 @@ export interface BrowseByConfig extends Config { * The absolute lowest year to display in the dropdown when no lowest date can be found for all items */ defaultLowerLimit: number; - - /** - * A list of all the active Browse-By pages - */ - types: BrowseByTypeConfig[]; } diff --git a/src/config/browse-by-type-config.interface.ts b/src/config/browse-by-type-config.interface.ts deleted file mode 100644 index f15846c210..0000000000 --- a/src/config/browse-by-type-config.interface.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Config } from './config.interface'; -import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; - -/** - * Config used for rendering Browse-By pages and links - */ -export interface BrowseByTypeConfig extends Config { - /** - * The browse id used for fetching browse data from the rest api - * e.g. author - */ - id: string; - - /** - * The type of Browse-By page to render - */ - type: BrowseByType | string; - - /** - * The metadata field to use for rendering starts-with options (only necessary when type is set to BrowseByType.Date) - */ - metadataField?: string; -} diff --git a/src/config/global-config.interface.ts b/src/config/global-config.interface.ts index d46822eb61..f8c226bdb1 100644 --- a/src/config/global-config.interface.ts +++ b/src/config/global-config.interface.ts @@ -6,13 +6,13 @@ import { INotificationBoardOptions } from './notifications-config.interfaces'; import { SubmissionConfig } from './submission-config.interface'; import { FormConfig } from './form-config.interfaces'; import { LangConfig } from './lang-config.interface'; -import { BrowseByConfig } from './browse-by-config.interface'; import { ItemPageConfig } from './item-page-config.interface'; import { CollectionPageConfig } from './collection-page-config.interface'; import { ThemeConfig } from './theme.model'; import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; +import { BrowseByConfig } from './browse-by-config.interface'; export interface GlobalConfig extends Config { ui: UIServerConfig; diff --git a/src/environments/environment.common.ts b/src/environments/environment.common.ts index b1cbd699a3..1441accf04 100644 --- a/src/environments/environment.common.ts +++ b/src/environments/environment.common.ts @@ -1,6 +1,5 @@ import { GlobalConfig } from '../config/global-config.interface'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; -import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; export const environment: GlobalConfig = { @@ -207,32 +206,6 @@ export const environment: GlobalConfig = { fiveYearLimit: 30, // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) defaultLowerLimit: 1900, - // List of all the active Browse-By types - // Adding a type will activate their Browse-By page and add them to the global navigation menu, - // as well as community and collection pages - // Allowed fields and their purpose: - // id: The browse id to use for fetching info from the rest api - // type: The type of Browse-By page to display - // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') - types: [ - { - id: 'title', - type: BrowseByType.Title, - }, - { - id: 'dateissued', - type: BrowseByType.Date, - metadataField: 'dc.date.issued' - }, - { - id: 'author', - type: BrowseByType.Metadata - }, - { - id: 'subject', - type: BrowseByType.Metadata - } - ] }, item: { edit: { diff --git a/src/environments/mock-environment.ts b/src/environments/mock-environment.ts index 824c8c8a83..f21de7fae3 100644 --- a/src/environments/mock-environment.ts +++ b/src/environments/mock-environment.ts @@ -1,5 +1,5 @@ // This configuration is only used for unit tests, end-to-end tests use environment.prod.ts -import { BrowseByType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { GlobalConfig } from '../config/global-config.interface'; @@ -169,32 +169,6 @@ export const environment: Partial = { fiveYearLimit: 30, // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) defaultLowerLimit: 1900, - // List of all the active Browse-By types - // Adding a type will activate their Browse-By page and add them to the global navigation menu, - // as well as community and collection pages - // Allowed fields and their purpose: - // id: The browse id to use for fetching info from the rest api - // type: The type of Browse-By page to display - // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') - types: [ - { - id: 'title', - type: BrowseByType.Title, - }, - { - id: 'dateissued', - type: BrowseByType.Date, - metadataField: 'dc.date.issued' - }, - { - id: 'author', - type: BrowseByType.Metadata - }, - { - id: 'subject', - type: BrowseByType.Metadata - } - ] }, item: { edit: { From 0e6c3a3a9dd99b976671b372e5bac8ae6e3286fe Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 21 Dec 2021 16:48:31 +0100 Subject: [PATCH 057/102] 85993: small changes and lint fixes --- .../browse-by-date-page.component.ts | 20 +++++++++---------- src/app/browse-by/browse-by-guard.ts | 2 +- .../core/shared/browse-definition.model.ts | 2 +- src/app/navbar/navbar.component.ts | 4 ++-- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index d0b63a00f9..e977b52ad6 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -18,7 +18,6 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { map } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { BrowseDefinition } from '../../core/shared/browse-definition.model'; @Component({ selector: 'ds-browse-by-date-page', @@ -34,9 +33,9 @@ import { BrowseDefinition } from '../../core/shared/browse-definition.model'; export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { /** - * The default metadata-field to use for determining the lower limit of the StartsWith dropdown options + * The default metadata keys to use for determining the lower limit of the StartsWith dropdown options */ - defaultBrowseDefinition = Object.assign(new BrowseDefinition(), {metadataKeys: ['dc.date.issued']}); + defaultMetadataKeys = ['dc.date.issued']; public constructor(protected route: ActivatedRoute, protected browseService: BrowseService, @@ -60,13 +59,13 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { return [Object.assign({}, routeParams, queryParams, data), currentPage, currentSort]; }) ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { - const browseDefinition = params.browseDefinition || this.defaultBrowseDefinition; - this.browseId = params.id || this.defaultBrowseId; - this.startsWith = +params.startsWith || params.startsWith; + const metadataKeys = params.browseDefinition ? params.browseDefinition.metadataKeys : this.defaultMetadataKeys; + this.browseId = params.id || this.defaultBrowseId; + this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); this.updatePageWithItems(searchOptions, this.value); this.updateParent(params.scope); - this.updateStartsWithOptions(this.browseId, browseDefinition, params.scope); + this.updateStartsWithOptions(this.browseId, metadataKeys, params.scope); })); } @@ -77,15 +76,15 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { * extremely long lists with a one-year difference. * To determine the change in years, the config found under GlobalConfig.BrowseBy is used for this. * @param definition The metadata definition to fetch the first item for - * @param metadataField The metadata field to fetch the earliest date from (expects a date field) + * @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field) * @param scope The scope under which to fetch the earliest item for */ - updateStartsWithOptions(definition: string, browseDefinition: BrowseDefinition, scope?: string) { + updateStartsWithOptions(definition: string, metadataKeys: string[], scope?: string) { this.subs.push( this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData) => { let lowerLimit = environment.browseBy.defaultLowerLimit; if (hasValue(firstItemRD.payload)) { - const date = firstItemRD.payload.firstMetadataValue(browseDefinition.metadataKeys); + const date = firstItemRD.payload.firstMetadataValue(metadataKeys); if (hasValue(date)) { const dateObj = new Date(date); // TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC. @@ -121,5 +120,4 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { }) ); } - } diff --git a/src/app/browse-by/browse-by-guard.ts b/src/app/browse-by/browse-by-guard.ts index 0a46d59613..689d33f0ce 100644 --- a/src/app/browse-by/browse-by-guard.ts +++ b/src/app/browse-by/browse-by-guard.ts @@ -50,7 +50,7 @@ export class BrowseByGuard implements CanActivate { return observableOf(true); } }) - ) + ); } private createData(title, id, browseDefinition, collection, field, value, route) { diff --git a/src/app/core/shared/browse-definition.model.ts b/src/app/core/shared/browse-definition.model.ts index b5dc47cd3e..68406f3f7d 100644 --- a/src/app/core/shared/browse-definition.model.ts +++ b/src/app/core/shared/browse-definition.model.ts @@ -34,7 +34,7 @@ export class BrowseDefinition extends CacheableObject { @autoserializeAs('metadata') metadataKeys: string[]; - @autoserializeAs('dataType') + @autoserialize dataType: BrowseByDataType; get self(): string { diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index 29541c77a0..c3b34d12ee 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -88,13 +88,13 @@ export class NavbarComponent extends MenuComponent { type: MenuItemType.TEXT, text: 'menu.section.browse_global' } as TextMenuItemModel, - },) + } + ); } menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, { shouldPersistOnRouteChange: true }))); }); - } } From e2c1319011ecb51b079339f7e0be49ece5ba94f6 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 21 Dec 2021 23:53:01 -0600 Subject: [PATCH 058/102] Update Configuration.md --- docs/Configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Configuration.md b/docs/Configuration.md index 66d8d492be..62fa444cc0 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -68,6 +68,7 @@ Settings can be set using the following convention for naming the environment va 1. replace all `.` with `_` 2. convert all characters to upper case +3. prefix with `DSPACE_` e.g. From f52dd92fbcadc36e275a54f57b4fdfe2c095911f Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 21 Dec 2021 23:57:38 -0600 Subject: [PATCH 059/102] Update config.server.ts --- src/config/config.server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 80e7b72261..3c0bea30da 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -47,7 +47,7 @@ const getEnvironment = (): Environment => { environment = 'development'; break; default: - console.warn(`Unknown NODE_ENV ${ENV('NODE_ENV')}. Defaulting to development`); + console.warn(`Unknown NODE_ENV ${ENV('NODE_ENV')}. Defaulting to production.`); } } From 39e0f1a65b71b7bd82a52b5da427009909206cdd Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 22 Dec 2021 07:58:13 +0100 Subject: [PATCH 060/102] Added test and fixed lgtm errors --- src/app/browse-by/browse-by-guard.ts | 2 -- .../browse-by-switcher/browse-by-switcher.component.ts | 1 - .../core/browse/browse-definition-data.service.spec.ts | 8 ++++++++ src/environments/mock-environment.ts | 1 - 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/browse-by/browse-by-guard.ts b/src/app/browse-by/browse-by-guard.ts index 689d33f0ce..e4582cb77a 100644 --- a/src/app/browse-by/browse-by-guard.ts +++ b/src/app/browse-by/browse-by-guard.ts @@ -6,9 +6,7 @@ import { map, switchMap } from 'rxjs/operators'; import { getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { TranslateService } from '@ngx-translate/core'; import { Observable, of as observableOf } from 'rxjs'; -import { environment } from '../../environments/environment'; import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service'; -import { RemoteData } from '../core/data/remote-data'; import { BrowseDefinition } from '../core/shared/browse-definition.model'; @Injectable() diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts index 55fcebf477..cf4c1d9856 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts @@ -3,7 +3,6 @@ import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { BROWSE_BY_COMPONENT_FACTORY } from './browse-by-decorator'; -import { environment } from '../../../environments/environment'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; 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..d6770f80c0 100644 --- a/src/app/core/browse/browse-definition-data.service.spec.ts +++ b/src/app/core/browse/browse-definition-data.service.spec.ts @@ -9,9 +9,11 @@ describe(`BrowseDefinitionDataService`, () => { findAll: EMPTY, findByHref: EMPTY, findAllByHref: EMPTY, + findById: EMPTY, }); const hrefAll = 'https://rest.api/server/api/discover/browses'; const hrefSingle = 'https://rest.api/server/api/discover/browses/author'; + const id = 'author'; const options = new FindListOptions(); const linksToFollow = [ followLink('entries'), @@ -44,4 +46,10 @@ describe(`BrowseDefinitionDataService`, () => { }); }); + describe(`findById`, () => { + it(`should call findById on DataServiceImpl`, () => { + service.findAllByHref(id, options, true, false, ...linksToFollow); + expect(dataServiceImplSpy.findAllByHref).toHaveBeenCalledWith(id, options, true, false, ...linksToFollow); + }); + }); }); diff --git a/src/environments/mock-environment.ts b/src/environments/mock-environment.ts index f21de7fae3..02c12d9992 100644 --- a/src/environments/mock-environment.ts +++ b/src/environments/mock-environment.ts @@ -1,5 +1,4 @@ // This configuration is only used for unit tests, end-to-end tests use environment.prod.ts -import { BrowseByDataType } from '../app/browse-by/browse-by-switcher/browse-by-decorator'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { GlobalConfig } from '../config/global-config.interface'; From 3476fe3f0f777a066aaa471b7751fdd7e5bee85f Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 22 Dec 2021 09:20:15 -0600 Subject: [PATCH 061/102] remove browse by types from config example --- config/config.example.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 7ff8d1d23c..1e035889a5 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -154,23 +154,6 @@ browseBy: fiveYearLimit: 30 # The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) defaultLowerLimit: 1900 - # List of all the active Browse-By types - # Adding a type will activate their Browse-By page and add them to the global navigation menu, - # as well as community and collection pages - # Allowed fields and their purpose: - # id: The browse id to use for fetching info from the rest api - # type: The type of Browse-By page to display - # metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') - types: - - id: title - type: title - - id: dateissued - type: date - metadataField: dc.date.issued - - id: author - type: metadata - - id: subject - type: metadata # Item Page Config item: From c43fdb9754f9af01d1e6fe22c99dc3efc47f92a8 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 22 Dec 2021 10:50:06 -0600 Subject: [PATCH 062/102] add missing break in switch --- src/config/config.server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 3c0bea30da..986fc2a092 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -124,6 +124,7 @@ const overrideWithEnvironment = (config: Config, key: string = '') => { break; case 'string': config[property] = value; + break; default: console.warn(`Unsupported environment variable type ${typeof innerConfig} ${DSPACE(variable)}`); } From f055d1676e7eb0b3b3a7fd35c2290b275efc547b Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 22 Dec 2021 10:51:26 -0600 Subject: [PATCH 063/102] add NODE_ENV development for yarn start:dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a500058e41..403973ef98 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "config:watch": "nodemon", "test:rest": "ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", "start": "yarn run start:prod", - "start:dev": "nodemon --exec \"yarn run serve\"", + "start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"", "start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod", "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", From e594cabe4a1b4aa26c0f3aa71d90512c34acb6d1 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Wed, 22 Dec 2021 18:01:37 +0100 Subject: [PATCH 064/102] [CST4981] finished task, working on unit testing --- .../admin-sidebar/admin-sidebar.component.ts | 18 ++-- .../collection-page-routing.module.ts | 1 + .../collection-page.component.ts | 16 +-- .../community-page-routing.module.ts | 1 + .../statistics-administrator.guard.ts | 28 +++++ .../data/feature-authorization/feature-id.ts | 1 + src/app/home-page/home-page-routing.module.ts | 1 + src/app/item-page/item-page-routing.module.ts | 1 + .../navbar-section.component.html | 5 +- src/app/navbar/navbar.component.html | 13 +-- src/app/navbar/navbar.component.ts | 13 ++- src/app/shared/menu/menu.component.spec.ts | 66 +++++++++++- src/app/shared/menu/menu.component.ts | 70 ++++++++++-- src/app/shared/menu/menu.effects.ts | 8 +- .../statistics-page-routing.module.ts | 101 +++++++++--------- src/test.ts | 2 +- .../dspace/app/navbar/navbar.component.html | 14 +-- 17 files changed, 256 insertions(+), 103 deletions(-) create mode 100644 src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index f0d583744c..c81b2e6e93 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -21,6 +21,7 @@ import { MenuService } from '../../shared/menu/menu.service'; import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { Router, ActivatedRoute } from '@angular/router'; /** * Component representing the admin sidebar @@ -63,14 +64,15 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { inFocus$: BehaviorSubject; constructor(protected menuService: MenuService, - protected injector: Injector, - private variableService: CSSVariableService, - private authService: AuthService, - private modalService: NgbModal, - private authorizationService: AuthorizationDataService, - private scriptDataService: ScriptDataService, + protected injector: Injector, + private variableService: CSSVariableService, + private authService: AuthService, + private modalService: NgbModal, + public authorizationService: AuthorizationDataService, + private scriptDataService: ScriptDataService, + public route: ActivatedRoute ) { - super(menuService, injector); + super(menuService, injector, authorizationService, route); this.inFocus$ = new BehaviorSubject(false); } @@ -144,7 +146,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { type: MenuItemType.TEXT, text: 'menu.section.new' } as TextMenuItemModel, - icon: 'plus', + icon: 'plus', index: 0 }, { diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index 5879e523af..b92c5bf414 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -72,6 +72,7 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen id: 'statistics_collection_:id', active: true, visible: true, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index 366e1da7b1..62d072c249 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -103,20 +103,20 @@ export class CollectionPageComponent implements OnInit { const currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, this.sortConfig); this.itemRD$ = observableCombineLatest([currentPagination$, currentSort$]).pipe( - switchMap(([currentPagination, currentSort ]) => this.collectionRD$.pipe( + switchMap(([currentPagination, currentSort]) => this.collectionRD$.pipe( getFirstSucceededRemoteData(), map((rd) => rd.payload.id), switchMap((id: string) => { return this.searchService.search( - new PaginatedSearchOptions({ - scope: id, - pagination: currentPagination, - sort: currentSort, - dsoTypes: [DSpaceObjectType.ITEM] - })).pipe(toDSpaceObjectListRD()) as Observable>>; + new PaginatedSearchOptions({ + scope: id, + pagination: currentPagination, + sort: currentSort, + dsoTypes: [DSpaceObjectType.ITEM] + })).pipe(toDSpaceObjectListRD()) as Observable>>; }), startWith(undefined) // Make sure switching pages shows loading component - ) + ) ) ); diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index ad1b1fd2f2..1be5472010 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -55,6 +55,7 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' id: 'statistics_community_:id', active: true, visible: true, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts new file mode 100644 index 0000000000..41ea8550a7 --- /dev/null +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { AuthorizationDataService } from '../authorization-data.service'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { AuthService } from '../../../auth/auth.service'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../feature-id'; +import { tap } from 'rxjs/operators'; + +/** + * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group + * management rights + */ +@Injectable({ + providedIn: 'root' +}) +export class StatisticsAdministratorGuard extends SingleFeatureAuthorizationGuard { + constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { + super(authorizationService, router, authService); + } + + /** + * Check group management rights + */ + getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf(FeatureID.CanViewUsageStatistics); + } +} diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 15eba0e5db..b32292559e 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -25,4 +25,5 @@ export enum FeatureID { CanEditVersion = 'canEditVersion', CanDeleteVersion = 'canDeleteVersion', CanCreateVersion = 'canCreateVersion', + CanViewUsageStatistics = 'canViewUsageStatistics', } diff --git a/src/app/home-page/home-page-routing.module.ts b/src/app/home-page/home-page-routing.module.ts index 2a41c079da..2356170d4b 100644 --- a/src/app/home-page/home-page-routing.module.ts +++ b/src/app/home-page/home-page-routing.module.ts @@ -21,6 +21,7 @@ import { ThemedHomePageComponent } from './themed-home-page.component'; active: true, visible: true, index: 2, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 7d7912bb42..b0412e5a0b 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -58,6 +58,7 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; id: 'statistics_item_:id', active: true, visible: true, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/navbar/navbar-section/navbar-section.component.html b/src/app/navbar/navbar-section/navbar-section.component.html index b5f6848050..d77b57515e 100644 --- a/src/app/navbar/navbar-section/navbar-section.component.html +++ b/src/app/navbar/navbar-section/navbar-section.component.html @@ -1,4 +1,3 @@ + + \ No newline at end of file diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 3fa7598e74..fc5d1a2ef3 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -1,18 +1,15 @@ - \ No newline at end of file diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index e741cea285..df4dd72477 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -7,6 +7,11 @@ import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; import { environment } from '../../environments/environment'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { Router, ActivatedRoute } from '@angular/router'; +import { map, take } from 'rxjs/operators'; +import { RemoteData } from '../core/data/remote-data'; +import { Collection } from 'src/app/core/shared/collection.model'; /** * Component representing the public navbar @@ -25,10 +30,12 @@ export class NavbarComponent extends MenuComponent { menuID = MenuID.PUBLIC; constructor(protected menuService: MenuService, - protected injector: Injector, - public windowService: HostWindowService + protected injector: Injector, + public windowService: HostWindowService, + public authorizationService: AuthorizationDataService, + public route: ActivatedRoute ) { - super(menuService, injector); + super(menuService, injector, authorizationService, route); } ngOnInit(): void { diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts index 883969137b..14e28591f3 100644 --- a/src/app/shared/menu/menu.component.spec.ts +++ b/src/app/shared/menu/menu.component.spec.ts @@ -7,9 +7,12 @@ import { MenuComponent } from './menu.component'; import { MenuServiceStub } from '../testing/menu-service.stub'; import { of as observableOf } from 'rxjs'; import { MenuSection } from './menu.reducer'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { MenuID } from './initial-menus-state'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils'; +import { Item } from '../../core/shared/item.model'; describe('MenuComponent', () => { let comp: MenuComponent; @@ -19,13 +22,38 @@ describe('MenuComponent', () => { const mockMenuID = 'mock-menuID' as MenuID; + const authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + + const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } + }); + + const routeStub = { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(mockItem) + }), + children: [] + }; + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule], declarations: [MenuComponent], providers: [ Injector, - { provide: MenuService, useClass: MenuServiceStub } + { provide: MenuService, useClass: MenuServiceStub }, + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: ActivatedRoute, useValue: routeStub }, ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(MenuComponent, { @@ -95,4 +123,38 @@ describe('MenuComponent', () => { expect(menuService.collapseMenuPreview).toHaveBeenCalledWith(comp.menuID); })); }); + + describe('when unauthorized statistics', () => { + + beforeEach(() => { + comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); + authorizationService.isAuthorized().and.returnValue(observableOf(false)); + fixture.detectChanges(); + }); + + it('when authorized statistics', (done => { + comp.sections.subscribe((sections) => { + expect(sections.length).toEqual(2); + done(); + }); + })); + }); + + describe('get authorized statistics', () => { + + beforeEach(() => { + comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); + fixture.detectChanges(); + }); + + it('get authorized statistics', (done => { + comp.sections.subscribe((sections) => { + expect(sections.length).toEqual(3); + done(); + }); + })); + }); + + + }); diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 32fd938f4e..1070521704 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -1,14 +1,17 @@ import { ChangeDetectionStrategy, Component, Injector, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, of as observableOf } from 'rxjs'; import { MenuService } from './menu.service'; import { MenuID } from './initial-menus-state'; import { MenuSection } from './menu.reducer'; -import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; +import { distinctUntilChanged, map, switchMap, mergeMap, tap, isEmpty } from 'rxjs/operators'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { hasValue } from '../empty.util'; +import { hasValue, isNotEmpty, hasValueOperator, isNotEmptyOperator } from '../empty.util'; import { MenuSectionComponent } from './menu-section/menu-section.component'; import { getComponentForMenu } from './menu-section.decorator'; import { compareArraysUsingIds } from '../../item-page/simple/item-types/shared/item-relationships-utils'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; +import { Router, ActivatedRoute } from '@angular/router'; /** * A basic implementation of a MenuComponent @@ -67,27 +70,39 @@ export class MenuComponent implements OnInit, OnDestroy { */ subs: Subscription[] = []; - constructor(protected menuService: MenuService, protected injector: Injector) { + private activatedRouteLastChild: ActivatedRoute; + + constructor(protected menuService: MenuService, protected injector: Injector, public authorizationService: AuthorizationDataService, public route: ActivatedRoute) { } /** * Sets all instance variables to their initial values */ ngOnInit(): void { + this.activatedRouteLastChild = this.getActivatedRoute(this.route); this.menuCollapsed = this.menuService.isMenuCollapsed(this.menuID); this.menuPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID); this.menuVisible = this.menuService.isMenuVisible(this.menuID); this.sections = this.menuService.getMenuTopSections(this.menuID).pipe(distinctUntilChanged(compareArraysUsingIds())); + this.subs.push( this.sections.pipe( + tap(t => console.log(t)), // if you return an array from a switchMap it will emit each element as a separate event. // So this switchMap is equivalent to a subscribe with a forEach inside switchMap((sections: MenuSection[]) => sections), + switchMap((section: MenuSection) => { + if (section.id.includes('statistics')) { + return this.getAuthorizedStatistics(section); + } + return observableOf(section); + }), + isNotEmptyOperator(), switchMap((section: MenuSection) => this.getSectionComponent(section).pipe( - map((component: GenericConstructor) => ({ section, component })) + map((component: GenericConstructor) => ({ section, component })) )), - distinctUntilChanged((x,y) => x.section.id === y.section.id) - ).subscribe(({ section, component}) => { + distinctUntilChanged((x, y) => x.section.id === y.section.id) + ).subscribe(({ section, component }) => { const nextMap = this.sectionMap$.getValue(); nextMap.set(section.id, { injector: this.getSectionDataInjector(section), @@ -98,6 +113,43 @@ export class MenuComponent implements OnInit, OnDestroy { ); } + /** + * Get activated route of the deepest activated route + */ + getActivatedRoute(route) { + if (route.children.length > 0) { + return this.getActivatedRoute(route.firstChild); + } else { + return route; + } + } + + /** + * Get section of statistics after checking authorization + */ + getAuthorizedStatistics(section) { + return this.activatedRouteLastChild.data.pipe( + switchMap((data) => { + return this.authorizationService.isAuthorized(FeatureID.CanViewUsageStatistics, this.getObjectUrl(data)).pipe( + map((canViewUsageStatistics: boolean) => { + if (!canViewUsageStatistics) { + return {}; + } else { + return section; + } + })); + }) + ); + } + + /** + * Get statistics route dso data + */ + getObjectUrl(data) { + const object = data.site ? data.site : data.dso.payload; + return object._links.self.href; + } + /** * Collapse this menu when it's currently expanded, expand it when its currently collapsed * @param {Event} event The user event that triggered this method @@ -164,8 +216,8 @@ export class MenuComponent implements OnInit, OnDestroy { private getSectionComponent(section: MenuSection): Observable> { return this.menuService.hasSubSections(this.menuID, section.id).pipe( map((expandable: boolean) => { - return getComponentForMenu(this.menuID, expandable); - } + return getComponentForMenu(this.menuID, expandable); + } ), ); } diff --git a/src/app/shared/menu/menu.effects.ts b/src/app/shared/menu/menu.effects.ts index 47cff90209..5eafad7a98 100644 --- a/src/app/shared/menu/menu.effects.ts +++ b/src/app/shared/menu/menu.effects.ts @@ -19,7 +19,7 @@ export class MenuEffects { /** * On route change, build menu sections for every menu type depending on the current route data */ - @Effect({dispatch: false}) + @Effect({ dispatch: false }) public buildRouteMenuSections$: Observable = this.actions$ .pipe( ofType(ROUTER_NAVIGATED), @@ -31,8 +31,8 @@ export class MenuEffects { ); constructor(private actions$: Actions, - private menuService: MenuService, - private route: ActivatedRoute) { + private menuService: MenuService, + private route: ActivatedRoute) { } /** @@ -72,7 +72,6 @@ export class MenuEffects { const last: boolean = hasNoValue(route.firstChild); if (hasValue(data) && hasValue(data.menu) && hasValue(data.menu[menuID])) { - let menuSections: MenuSection[] | MenuSection = data.menu[menuID]; menuSections = this.resolveSubstitutions(menuSections, params); @@ -120,4 +119,5 @@ export class MenuEffects { } return resolved; } + } diff --git a/src/app/statistics-page/statistics-page-routing.module.ts b/src/app/statistics-page/statistics-page-routing.module.ts index 3c88e096e7..6047a82f1e 100644 --- a/src/app/statistics-page/statistics-page-routing.module.ts +++ b/src/app/statistics-page/statistics-page-routing.module.ts @@ -10,64 +10,69 @@ import { ThemedCommunityStatisticsPageComponent } from './community-statistics-p import { ThemedItemStatisticsPageComponent } from './item-statistics-page/themed-item-statistics-page.component'; import { ThemedSiteStatisticsPageComponent } from './site-statistics-page/themed-site-statistics-page.component'; import { ItemResolver } from '../item-page/item.resolver'; +import { StatisticsAdministratorGuard } from 'src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; @NgModule({ imports: [ StatisticsPageModule, RouterModule.forChild([ - { - path: '', - resolve: { - breadcrumb: I18nBreadcrumbResolver - }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - children: [ - { - path: '', - component: ThemedSiteStatisticsPageComponent, - }, - ] + { + path: '', + resolve: { + breadcrumb: I18nBreadcrumbResolver }, - { - path: `items/:id`, - resolve: { - scope: ItemResolver, - breadcrumb: I18nBreadcrumbResolver - }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - component: ThemedItemStatisticsPageComponent, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' }, - { - path: `collections/:id`, - resolve: { - scope: CollectionPageResolver, - breadcrumb: I18nBreadcrumbResolver + children: [ + { + path: '', + component: ThemedSiteStatisticsPageComponent, }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - component: ThemedCollectionStatisticsPageComponent, + ], + canActivate: [StatisticsAdministratorGuard] + }, + { + path: `items/:id`, + resolve: { + scope: ItemResolver, + breadcrumb: I18nBreadcrumbResolver }, - { - path: `communities/:id`, - resolve: { - scope: CommunityPageResolver, - breadcrumb: I18nBreadcrumbResolver - }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - component: ThemedCommunityStatisticsPageComponent, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' }, - ] + component: ThemedItemStatisticsPageComponent, + canActivate: [StatisticsAdministratorGuard] + }, + { + path: `collections/:id`, + resolve: { + scope: CollectionPageResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' + }, + component: ThemedCollectionStatisticsPageComponent, + canActivate: [StatisticsAdministratorGuard] + }, + { + path: `communities/:id`, + resolve: { + scope: CommunityPageResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' + }, + component: ThemedCommunityStatisticsPageComponent, + canActivate: [StatisticsAdministratorGuard] + }, + ] ) ], providers: [ diff --git a/src/test.ts b/src/test.ts index 16317897b1..c77b4bb2f0 100644 --- a/src/test.ts +++ b/src/test.ts @@ -15,6 +15,6 @@ getTestBed().initTestEnvironment( platformBrowserDynamicTesting() ); // Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); +const context = require.context('./app/shared/menu', true, /\.spec\.ts$/); // And load the modules. context.keys().map(context); diff --git a/src/themes/dspace/app/navbar/navbar.component.html b/src/themes/dspace/app/navbar/navbar.component.html index 5af30db632..f061c7cb3b 100644 --- a/src/themes/dspace/app/navbar/navbar.component.html +++ b/src/themes/dspace/app/navbar/navbar.component.html @@ -1,17 +1,14 @@ - \ No newline at end of file From 9646088931d206e517f3df9aa8145838bce460dc Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 22 Dec 2021 14:29:37 -0600 Subject: [PATCH 065/102] minor readme configuration corrections --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 065d313005..1beb1bf30c 100644 --- a/README.md +++ b/README.md @@ -132,19 +132,19 @@ e.g. ```bash # The host name of the REST application -dspace.rest.host => DSPACE_REST_HOST +rest.host => DSPACE_REST_HOST # The port number of the REST application -dspace.rest.port => DSPACE_REST_PORT +rest.port => DSPACE_REST_PORT # The namespace of the REST application -dspace.rest.nameSpace => DSPACE_REST_NAMESPACE +rest.nameSpace => DSPACE_REST_NAMESPACE # Whether the angular REST uses SSL [true/false] -dspace.rest.ssl => DSPACE_REST_SSL +rest.ssl => DSPACE_REST_SSL -cache.msToLive.default => CACHE_MSTOLIVE_DEFAULT -auth.ui.timeUntilIdle => AUTH_UI_TIMEUNTILIDLE +cache.msToLive.default => DSPACE_CACHE_MSTOLIVE_DEFAULT +auth.ui.timeUntilIdle => DSPACE_AUTH_UI_TIMEUNTILIDLE ``` The equavelant to the non-conventional legacy settings: From 2fe5587e0257a9f7587a01cefcf11a144aea582f Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Thu, 16 Dec 2021 14:17:28 +0100 Subject: [PATCH 066/102] move the correlation id to the ngrx store --- src/app/app.module.ts | 15 ----- src/app/app.reducer.ts | 3 + src/app/core/log/log.interceptor.ts | 9 ++- .../correlation-id/correlation-id.actions.ts | 15 +++++ .../correlation-id/correlation-id.reducer.ts | 21 ++++++ .../correlation-id/correlation-id.service.ts | 64 +++++++++++++++++++ src/modules/app/browser-app.module.ts | 17 ++--- src/modules/app/server-app.module.ts | 16 +++-- 8 files changed, 125 insertions(+), 35 deletions(-) create mode 100644 src/app/correlation-id/correlation-id.actions.ts create mode 100644 src/app/correlation-id/correlation-id.reducer.ts create mode 100644 src/app/correlation-id/correlation-id.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 98b2d9fe92..4a404e3200 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -156,21 +156,6 @@ const PROVIDERS = [ useClass: LogInterceptor, multi: true }, - // insert the unique id of the user that is using the application utilizing cookies - { - provide: APP_INITIALIZER, - useFactory: (cookieService: CookieService, uuidService: UUIDService) => { - const correlationId = cookieService.get('CORRELATION-ID'); - - // Check if cookie exists, if don't, set it with unique id - if (!correlationId) { - cookieService.set('CORRELATION-ID', uuidService.generate()); - } - return () => true; - }, - multi: true, - deps: [CookieService, UUIDService] - }, { provide: DYNAMIC_ERROR_MESSAGES_MATCHER, useValue: ValidateEmailErrorStateMatcher diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index a02095d834..5bd4f745d9 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -49,6 +49,7 @@ import { import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer'; import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer'; import { ThemeState, themeReducer } from './shared/theme-support/theme.reducer'; +import { correlationIdReducer } from './correlation-id/correlation-id.reducer'; export interface AppState { router: fromRouter.RouterReducerState; @@ -69,6 +70,7 @@ export interface AppState { communityList: CommunityListState; epeopleRegistry: EPeopleRegistryState; groupRegistry: GroupRegistryState; + correlationId: string; } export const appReducers: ActionReducerMap = { @@ -90,6 +92,7 @@ export const appReducers: ActionReducerMap = { communityList: CommunityListReducer, epeopleRegistry: ePeopleRegistryReducer, groupRegistry: groupRegistryReducer, + correlationId: correlationIdReducer }; export const routerStateSelector = (state: AppState) => state.router; diff --git a/src/app/core/log/log.interceptor.ts b/src/app/core/log/log.interceptor.ts index bf843f1da8..aff1a24963 100644 --- a/src/app/core/log/log.interceptor.ts +++ b/src/app/core/log/log.interceptor.ts @@ -3,9 +3,8 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/c import { Router } from '@angular/router'; import { Observable } from 'rxjs'; - -import { CookieService } from '../services/cookie.service'; import { hasValue } from '../../shared/empty.util'; +import { CorrelationIdService } from '../../correlation-id/correlation-id.service'; /** * Log Interceptor intercepting Http Requests & Responses to @@ -15,12 +14,12 @@ import { hasValue } from '../../shared/empty.util'; @Injectable() export class LogInterceptor implements HttpInterceptor { - constructor(private cookieService: CookieService, private router: Router) {} + constructor(private cidService: CorrelationIdService, private router: Router) {} intercept(request: HttpRequest, next: HttpHandler): Observable> { - // Get Unique id of the user from the cookies - const correlationId = this.cookieService.get('CORRELATION-ID'); + // Get the correlation id for the user from the store + const correlationId = this.cidService.getCorrelationId(); // Add headers from the intercepted request let headers = request.headers; diff --git a/src/app/correlation-id/correlation-id.actions.ts b/src/app/correlation-id/correlation-id.actions.ts new file mode 100644 index 0000000000..0901974a4c --- /dev/null +++ b/src/app/correlation-id/correlation-id.actions.ts @@ -0,0 +1,15 @@ +import { type } from '../shared/ngrx/type'; +import { Action } from '@ngrx/store'; + +export const CorrelationIDActionTypes = { + SET: type('dspace/core/correlationId/SET') +}; + +export class SetCorrelationIdAction implements Action { + type = CorrelationIDActionTypes.SET; + + constructor(public payload: string) { + } +} + +export type CorrelationIdAction = SetCorrelationIdAction; diff --git a/src/app/correlation-id/correlation-id.reducer.ts b/src/app/correlation-id/correlation-id.reducer.ts new file mode 100644 index 0000000000..e8071a3108 --- /dev/null +++ b/src/app/correlation-id/correlation-id.reducer.ts @@ -0,0 +1,21 @@ +import { + CorrelationIdAction, + CorrelationIDActionTypes, + SetCorrelationIdAction +} from './correlation-id.actions'; +import { AppState } from '../app.reducer'; + +const initialState = null; + +export const correlationIdSelector = (state: AppState) => state.correlationId; + +export const correlationIdReducer = (state = initialState, action: CorrelationIdAction): string => { + switch (action.type) { + case CorrelationIDActionTypes.SET: { + return (action as SetCorrelationIdAction).payload; + } + default: { + return state; + } + } +}; diff --git a/src/app/correlation-id/correlation-id.service.ts b/src/app/correlation-id/correlation-id.service.ts new file mode 100644 index 0000000000..6f4b2a5341 --- /dev/null +++ b/src/app/correlation-id/correlation-id.service.ts @@ -0,0 +1,64 @@ +import { CookieService } from '../core/services/cookie.service'; +import { UUIDService } from '../core/shared/uuid.service'; +import { Store, select } from '@ngrx/store'; +import { AppState } from '../app.reducer'; +import { isEmpty } from '../shared/empty.util'; +import { correlationIdSelector } from './correlation-id.reducer'; +import { take } from 'rxjs/operators'; +import { SetCorrelationIdAction } from './correlation-id.actions'; +import { Injectable } from '@angular/core'; + +/** + * Service to manage the correlation id, an id used to give context to server side logs + */ +@Injectable({ + providedIn: 'root' +}) +export class CorrelationIdService { + + constructor( + protected cookieService: CookieService, + protected uuidService: UUIDService, + protected store: Store, + ) { + } + + /** + * Initialize the correlation id based on the cookie or the ngrx store + */ + initCorrelationId(): void { + // first see of there's a cookie with a correlation-id + let correlationId = this.cookieService.get('CORRELATION-ID'); + + // if there isn't see if there's an ID in the store + if (isEmpty(correlationId)) { + correlationId = this.getCorrelationId(); + } + + // if no id was found, create a new id + if (isEmpty(correlationId)) { + correlationId = this.uuidService.generate(); + } + + // Store the correct id both in the store and as a cookie to ensure they're in sync + this.store.dispatch(new SetCorrelationIdAction(correlationId)); + this.cookieService.set('CORRELATION-ID', correlationId); + } + + /** + * Get the correlation id from the store + */ + getCorrelationId(): string { + let correlationId; + + this.store.pipe( + select(correlationIdSelector), + take(1) + ).subscribe((storeId: string) => { + // we can do this because ngrx selects are synchronous + correlationId = storeId; + }); + + return correlationId; + } +} diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index 86497d4599..88a59eb157 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -36,6 +36,7 @@ import { BrowserAuthRequestService } from '../../app/core/auth/browser-auth-requ import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface'; import { DefaultAppConfig } from '../../config/default-app-config'; import { extendEnvironmentWithAppConfig } from '../../config/config.util'; +import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { environment } from '../../environments/environment'; @@ -81,16 +82,21 @@ export function getRequest(transferState: TransferState): any { providers: [ { provide: APP_INITIALIZER, - useFactory: (transferState: TransferState) => { + useFactory: ( + transferState: TransferState, + dspaceTransferState: DSpaceTransferState, + correlationIdService: CorrelationIdService + ) => { if (transferState.hasKey(APP_CONFIG_STATE)) { const appConfig = transferState.get(APP_CONFIG_STATE, new DefaultAppConfig()); // extend environment with app config for browser extendEnvironmentWithAppConfig(environment, appConfig); } - + dspaceTransferState.transfer(); + correlationIdService.initCorrelationId(); return () => true; }, - deps: [TransferState], + deps: [TransferState, DSpaceTransferState, CorrelationIdService], multi: true }, { @@ -137,9 +143,4 @@ export function getRequest(transferState: TransferState): any { ] }) export class BrowserAppModule { - constructor( - private transferState: DSpaceTransferState, - ) { - this.transferState.transfer(); - } } diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index 8c49554de9..01a5548948 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -32,6 +32,7 @@ import { ServerHardRedirectService } from '../../app/core/services/server-hard-r import { Angulartics2Mock } from '../../app/shared/mocks/angulartics2.service.mock'; import { AuthRequestService } from '../../app/core/auth/auth-request.service'; import { ServerAuthRequestService } from '../../app/core/auth/server-auth-request.service'; +import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface'; import { environment } from '../../environments/environment'; @@ -65,11 +66,17 @@ export function createTranslateLoader() { // Initialize app config and extend environment { provide: APP_INITIALIZER, - useFactory: (transferState: TransferState) => { + useFactory: ( + transferState: TransferState, + dspaceTransferState: DSpaceTransferState, + correlationIdService: CorrelationIdService, + ) => { transferState.set(APP_CONFIG_STATE, environment as AppConfig); + dspaceTransferState.transfer(); + correlationIdService.initCorrelationId(); return () => true; }, - deps: [TransferState], + deps: [TransferState, DSpaceTransferState, CorrelationIdService], multi: true }, { @@ -117,9 +124,4 @@ export function createTranslateLoader() { ] }) export class ServerAppModule { - constructor( - private transferState: DSpaceTransferState, - ) { - this.transferState.transfer(); - } } From 3c8c425843985d2bd461690aed876dfe0d850279 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Fri, 17 Dec 2021 17:05:53 +0100 Subject: [PATCH 067/102] 86016: Add unit tests for correlationId reducer & service --- .../correlation-id.reducer.spec.ts | 23 +++++ .../correlation-id.service.spec.ts | 83 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/app/correlation-id/correlation-id.reducer.spec.ts create mode 100644 src/app/correlation-id/correlation-id.service.spec.ts diff --git a/src/app/correlation-id/correlation-id.reducer.spec.ts b/src/app/correlation-id/correlation-id.reducer.spec.ts new file mode 100644 index 0000000000..c784def1d9 --- /dev/null +++ b/src/app/correlation-id/correlation-id.reducer.spec.ts @@ -0,0 +1,23 @@ +import { correlationIdReducer } from './correlation-id.reducer'; +import { SetCorrelationIdAction } from './correlation-id.actions'; + +describe('correlationIdReducer', () => { + it('should set the correlatinId with SET action', () => { + const initialState = null; + const currentState = correlationIdReducer(initialState, new SetCorrelationIdAction('new ID')); + + expect(currentState).toBe('new ID'); + }); + + it('should leave correlatinId unchanged otherwise', () => { + const initialState = null; + + let currentState = correlationIdReducer(initialState, { type: 'unknown' } as any); + expect(currentState).toBe(null); + + currentState = correlationIdReducer(currentState, new SetCorrelationIdAction('new ID')); + currentState = correlationIdReducer(currentState, { type: 'unknown' } as any); + + expect(currentState).toBe('new ID'); + }); +}); diff --git a/src/app/correlation-id/correlation-id.service.spec.ts b/src/app/correlation-id/correlation-id.service.spec.ts new file mode 100644 index 0000000000..003924c123 --- /dev/null +++ b/src/app/correlation-id/correlation-id.service.spec.ts @@ -0,0 +1,83 @@ +import { CorrelationIdService } from './correlation-id.service'; +import { CookieServiceMock } from '../shared/mocks/cookie.service.mock'; +import { UUIDService } from '../core/shared/uuid.service'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { TestBed } from '@angular/core/testing'; +import { Store, StoreModule } from '@ngrx/store'; +import { appReducers, AppState, storeModuleConfig } from '../app.reducer'; +import { SetCorrelationIdAction } from './correlation-id.actions'; + +fdescribe('CorrelationIdService', () => { + let service: CorrelationIdService; + + let cookieService; + let uuidService; + let store; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot(appReducers, storeModuleConfig), + ], + }).compileComponents(); + }); + + beforeEach(() => { + cookieService = new CookieServiceMock(); + uuidService = new UUIDService(); + store = TestBed.inject(Store) as MockStore; + service = new CorrelationIdService(cookieService, uuidService, store); + }); + + describe('getCorrelationId', () => { + it('should get from from store', () => { + expect(service.getCorrelationId()).toBe(null); + store.dispatch(new SetCorrelationIdAction('some value')); + expect(service.getCorrelationId()).toBe('some value'); + }); + }); + + + describe('initCorrelationId', () => { + const cookieCID = 'cookie CID'; + const storeCID = 'store CID'; + + it('should set cookie and store values to a newly generated value if neither ex', () => { + service.initCorrelationId(); + + expect(cookieService.get('CORRELATION-ID')).toBeTruthy(); + expect(service.getCorrelationId()).toBeTruthy(); + expect(cookieService.get('CORRELATION-ID')).toEqual(service.getCorrelationId()); + }); + + it('should set store value to cookie value if present', () => { + expect(service.getCorrelationId()).toBe(null); + + cookieService.set('CORRELATION-ID', cookieCID); + + service.initCorrelationId(); + + expect(cookieService.get('CORRELATION-ID')).toBe(cookieCID); + expect(service.getCorrelationId()).toBe(cookieCID); + }); + + it('should set cookie value to store value if present', () => { + store.dispatch(new SetCorrelationIdAction(storeCID)); + + service.initCorrelationId(); + + expect(cookieService.get('CORRELATION-ID')).toBe(storeCID); + expect(service.getCorrelationId()).toBe(storeCID); + }); + + it('should set store value to cookie value if both are present', () => { + cookieService.set('CORRELATION-ID', cookieCID); + store.dispatch(new SetCorrelationIdAction(storeCID)); + + service.initCorrelationId(); + + expect(cookieService.get('CORRELATION-ID')).toBe(cookieCID); + expect(service.getCorrelationId()).toBe(cookieCID); + }); + }); +}); From 50d8719c41d1ecf7d069b84403353aa6c34c170f Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Mon, 20 Dec 2021 16:09:08 +0100 Subject: [PATCH 068/102] Remove stray fdescribe --- src/app/correlation-id/correlation-id.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/correlation-id/correlation-id.service.spec.ts b/src/app/correlation-id/correlation-id.service.spec.ts index 003924c123..64a4d1068a 100644 --- a/src/app/correlation-id/correlation-id.service.spec.ts +++ b/src/app/correlation-id/correlation-id.service.spec.ts @@ -7,7 +7,7 @@ import { Store, StoreModule } from '@ngrx/store'; import { appReducers, AppState, storeModuleConfig } from '../app.reducer'; import { SetCorrelationIdAction } from './correlation-id.actions'; -fdescribe('CorrelationIdService', () => { +describe('CorrelationIdService', () => { let service: CorrelationIdService; let cookieService; From a2515c11e1623ab30d99505977f903f231c441ff Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Tue, 21 Dec 2021 09:51:56 +0100 Subject: [PATCH 069/102] 86016: Update log.interceptor.spec.ts --- src/app/core/log/log.interceptor.spec.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/app/core/log/log.interceptor.spec.ts b/src/app/core/log/log.interceptor.spec.ts index 9bda4b7934..cae9c32202 100644 --- a/src/app/core/log/log.interceptor.spec.ts +++ b/src/app/core/log/log.interceptor.spec.ts @@ -9,12 +9,17 @@ import { RestRequestMethod } from '../data/rest-request-method'; import { CookieService } from '../services/cookie.service'; import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; import { RouterStub } from '../../shared/testing/router.stub'; +import { CorrelationIdService } from '../../correlation-id/correlation-id.service'; +import { UUIDService } from '../shared/uuid.service'; +import { StoreModule } from '@ngrx/store'; +import { appReducers, storeModuleConfig } from '../../app.reducer'; describe('LogInterceptor', () => { let service: DspaceRestService; let httpMock: HttpTestingController; let cookieService: CookieService; + let correlationIdService: CorrelationIdService; const router = Object.assign(new RouterStub(),{url : '/statistics'}); // Mock payload/statuses are dummy content as we are not testing the results @@ -28,7 +33,10 @@ describe('LogInterceptor', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], + imports: [ + HttpClientTestingModule, + StoreModule.forRoot(appReducers, storeModuleConfig), + ], providers: [ DspaceRestService, // LogInterceptor, @@ -39,14 +47,18 @@ describe('LogInterceptor', () => { }, { provide: CookieService, useValue: new CookieServiceMock() }, { provide: Router, useValue: router }, + { provide: CorrelationIdService, useClass: CorrelationIdService }, + { provide: UUIDService, useClass: UUIDService }, ], }); - service = TestBed.get(DspaceRestService); - httpMock = TestBed.get(HttpTestingController); - cookieService = TestBed.get(CookieService); + service = TestBed.inject(DspaceRestService); + httpMock = TestBed.inject(HttpTestingController); + cookieService = TestBed.inject(CookieService); + correlationIdService = TestBed.inject(CorrelationIdService); cookieService.set('CORRELATION-ID','123455'); + correlationIdService.initCorrelationId(); }); From c1e8bbbeaeb00e0d160f6f8b8189dbb89a1b0f79 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Thu, 23 Dec 2021 16:42:14 +0100 Subject: [PATCH 070/102] 86016: Add typedocs --- src/app/correlation-id/correlation-id.actions.ts | 6 ++++++ src/app/correlation-id/correlation-id.reducer.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/app/correlation-id/correlation-id.actions.ts b/src/app/correlation-id/correlation-id.actions.ts index 0901974a4c..d57d41a637 100644 --- a/src/app/correlation-id/correlation-id.actions.ts +++ b/src/app/correlation-id/correlation-id.actions.ts @@ -5,6 +5,9 @@ export const CorrelationIDActionTypes = { SET: type('dspace/core/correlationId/SET') }; +/** + * Action for setting a new correlation ID + */ export class SetCorrelationIdAction implements Action { type = CorrelationIDActionTypes.SET; @@ -12,4 +15,7 @@ export class SetCorrelationIdAction implements Action { } } +/** + * Type alias for all correlation ID actions + */ export type CorrelationIdAction = SetCorrelationIdAction; diff --git a/src/app/correlation-id/correlation-id.reducer.ts b/src/app/correlation-id/correlation-id.reducer.ts index e8071a3108..b7525b0b1c 100644 --- a/src/app/correlation-id/correlation-id.reducer.ts +++ b/src/app/correlation-id/correlation-id.reducer.ts @@ -9,6 +9,12 @@ const initialState = null; export const correlationIdSelector = (state: AppState) => state.correlationId; +/** + * Reducer that handles actions to update the correlation ID + * @param {string} state the previous correlation ID (null if unset) + * @param {CorrelationIdAction} action the action to perform + * @return {string} the new correlation ID + */ export const correlationIdReducer = (state = initialState, action: CorrelationIdAction): string => { switch (action.type) { case CorrelationIDActionTypes.SET: { From fc059520a007cc11482b95c34281cfc5a8430a07 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Thu, 23 Dec 2021 17:09:44 +0100 Subject: [PATCH 071/102] Fix LGTM issues --- src/app/app.module.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4a404e3200..32c3c78348 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -55,9 +55,6 @@ import { ThemedBreadcrumbsComponent } from './breadcrumbs/themed-breadcrumbs.com import { ThemedHeaderNavbarWrapperComponent } from './header-nav-wrapper/themed-header-navbar-wrapper.component'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; -import { UUIDService } from './core/shared/uuid.service'; -import { CookieService } from './core/services/cookie.service'; - import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; export function getConfig() { From 13db7c8c19bfbf11d38fbef113454dd6143a79f6 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 23 Dec 2021 10:27:49 -0600 Subject: [PATCH 072/102] Update docker-compose for runtime configs. Remove all config files & replace with env variables --- README.md | 4 +--- docker/README.md | 4 ---- docker/cli.yml | 13 +++++++++++-- docker/docker-compose-ci.yml | 14 +++++++++++++- docker/docker-compose-rest.yml | 23 ++++++++++++++++++++++- docker/docker-compose.yml | 14 ++++++++------ docker/environment.dev.ts | 18 ------------------ docker/local.cfg | 6 ------ 8 files changed, 55 insertions(+), 41 deletions(-) delete mode 100644 docker/environment.dev.ts delete mode 100644 docker/local.cfg diff --git a/README.md b/README.md index 065d313005..201e488373 100644 --- a/README.md +++ b/README.md @@ -359,7 +359,7 @@ dspace-angular │ ├── plugins * Folder for Cypress plugins (if any) │ ├── support * Folder for global e2e test actions/commands (run for all tests) │ └── tsconfig.json * TypeScript configuration file for e2e tests -├── docker * +├── docker * See docker/README.md for details │ ├── cli.assetstore.yml * │ ├── cli.ingest.yml * │ ├── cli.yml * @@ -367,8 +367,6 @@ dspace-angular │ ├── docker-compose-ci.yml * │ ├── docker-compose-rest.yml * │ ├── docker-compose.yml * -│ ├── environment.dev.ts * -│ ├── local.cfg * │ └── README.md * ├── docs * Folder for documentation │ └── Configuration.md * Configuration documentation diff --git a/docker/README.md b/docker/README.md index b0943562af..a2f4ef3362 100644 --- a/docker/README.md +++ b/docker/README.md @@ -29,10 +29,6 @@ docker push dspace/dspace-angular:dspace-7_x - Docker compose file that provides a DSpace CLI container to work with a running DSpace REST container. - cli.assetstore.yml - Docker compose file that will download and install data into a DSpace REST assetstore. This script points to a default dataset that will be utilized for CI testing. -- environment.dev.ts - - Environment file for running DSpace Angular in Docker -- local.cfg - - Environment file for running the DSpace 7 REST API in Docker. ## To refresh / pull DSpace images from Dockerhub diff --git a/docker/cli.yml b/docker/cli.yml index 36f63b2cff..54b83d4503 100644 --- a/docker/cli.yml +++ b/docker/cli.yml @@ -18,10 +18,19 @@ services: dspace-cli: image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" container_name: dspace-cli - #environment: + environment: + # Below syntax may look odd, but it is how to override dspace.cfg settings via env variables. + # See https://github.com/DSpace/DSpace/blob/main/dspace/config/config-definition.xml + # __P__ => "." (e.g. dspace__P__dir => dspace.dir) + # __D__ => "-" (e.g. google__D__metadata => google-metadata) + # dspace.dir + dspace__P__dir: /dspace + # db.url: Ensure we are using the 'dspacedb' image for our database + db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace' + # solr.server: Ensure we are using the 'dspacesolr' image for Solr + solr__P__server: http://dspacesolr:8983/solr volumes: - "assetstore:/dspace/assetstore" - - "./local.cfg:/dspace/config/local.cfg" entrypoint: /dspace/bin/dspace command: help networks: diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml index 18fa152c9d..a895314a17 100644 --- a/docker/docker-compose-ci.yml +++ b/docker/docker-compose-ci.yml @@ -17,6 +17,19 @@ services: # DSpace (backend) webapp container dspace: container_name: dspace + environment: + # Below syntax may look odd, but it is how to override dspace.cfg settings via env variables. + # See https://github.com/DSpace/DSpace/blob/main/dspace/config/config-definition.xml + # __P__ => "." (e.g. dspace__P__dir => dspace.dir) + # __D__ => "-" (e.g. google__D__metadata => google-metadata) + # dspace.dir, dspace.server.url and dspace.ui.url + dspace__P__dir: /dspace + dspace__P__server__P__url: http://localhost:8080/server + dspace__P__ui__P__url: http://localhost:4000 + # db.url: Ensure we are using the 'dspacedb' image for our database + db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace' + # solr.server: Ensure we are using the 'dspacesolr' image for Solr + solr__P__server: http://dspacesolr:8983/solr depends_on: - dspacedb image: dspace/dspace:dspace-7_x-test @@ -29,7 +42,6 @@ services: tty: true volumes: - assetstore:/dspace/assetstore - - "./local.cfg:/dspace/config/local.cfg" # Mount DSpace's solr configs to a volume, so that we can share to 'dspacesolr' container (see below) - solr_configs:/dspace/solr # Ensure that the database is ready BEFORE starting tomcat diff --git a/docker/docker-compose-rest.yml b/docker/docker-compose-rest.yml index 3534682afc..b73f1b7a39 100644 --- a/docker/docker-compose-rest.yml +++ b/docker/docker-compose-rest.yml @@ -13,10 +13,32 @@ version: '3.7' networks: dspacenet: + ipam: + config: + # Define a custom subnet for our DSpace network, so that we can easily trust requests from host to container. + # If you customize this value, be sure to customize the 'proxies.trusted.ipranges' env variable below. + - subnet: 172.23.0.0/16 services: # DSpace (backend) webapp container dspace: container_name: dspace + environment: + # Below syntax may look odd, but it is how to override dspace.cfg settings via env variables. + # See https://github.com/DSpace/DSpace/blob/main/dspace/config/config-definition.xml + # __P__ => "." (e.g. dspace__P__dir => dspace.dir) + # __D__ => "-" (e.g. google__D__metadata => google-metadata) + # dspace.dir, dspace.server.url, dspace.ui.url and dspace.name + dspace__P__dir: /dspace + dspace__P__server__P__url: http://localhost:8080/server + dspace__P__ui__P__url: http://localhost:4000 + dspace__P__name: 'DSpace Started with Docker Compose' + # db.url: Ensure we are using the 'dspacedb' image for our database + db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace' + # solr.server: Ensure we are using the 'dspacesolr' image for Solr + solr__P__server: http://dspacesolr:8983/solr + # proxies.trusted.ipranges: This setting is required for a REST API running in Docker to trust requests + # from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above. + proxies__P__trusted__P__ipranges: '172.23.0' image: dspace/dspace:dspace-7_x-test depends_on: - dspacedb @@ -29,7 +51,6 @@ services: tty: true volumes: - assetstore:/dspace/assetstore - - "./local.cfg:/dspace/config/local.cfg" # Mount DSpace's solr configs to a volume, so that we can share to 'dspacesolr' container (see below) - solr_configs:/dspace/solr # Ensure that the database is ready BEFORE starting tomcat diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index e518dc99d2..adeb61dfc6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -16,10 +16,14 @@ services: dspace-angular: container_name: dspace-angular environment: - DSPACE_HOST: dspace-angular - DSPACE_NAMESPACE: / - DSPACE_PORT: '4000' - DSPACE_SSL: "false" + DSPACE_UI_SSL: false + DSPACE_UI_HOST: dspace-angular + DSPACE_UI_PORT: '4000' + DSPACE_UI_NAMESPACE: / + DSPACE_REST_SSL: false + DSPACE_REST_HOST: localhost + DSPACE_REST_PORT: 8080 + DSPACE_REST_NAMESPACE: /server image: dspace/dspace-angular:dspace-7_x build: context: .. @@ -33,5 +37,3 @@ services: target: 9876 stdin_open: true tty: true - volumes: - - ./environment.dev.ts:/app/src/environments/environment.dev.ts diff --git a/docker/environment.dev.ts b/docker/environment.dev.ts deleted file mode 100644 index 0e603ef11d..0000000000 --- a/docker/environment.dev.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -// This file is based on environment.template.ts provided by Angular UI -export const environment = { - // Default to using the local REST API (running in Docker) - rest: { - ssl: false, - host: 'localhost', - port: 8080, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/server' - } -}; diff --git a/docker/local.cfg b/docker/local.cfg deleted file mode 100644 index a511c25789..0000000000 --- a/docker/local.cfg +++ /dev/null @@ -1,6 +0,0 @@ -dspace.dir=/dspace -db.url=jdbc:postgresql://dspacedb:5432/dspace -dspace.server.url=http://localhost:8080/server -dspace.ui.url=http://localhost:4000 -dspace.name=DSpace Started with Docker Compose -solr.server=http://dspacesolr:8983/solr From 0550cd55d1fad7faca49fcd66c7ea826e5341c3e Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 23 Dec 2021 20:12:25 +0100 Subject: [PATCH 073/102] [CST-5064] Show submitter in version history table --- src/app/core/shared/version.model.ts | 6 +++ .../item-versions.component.html | 8 ++-- .../item-versions/item-versions.component.ts | 38 +++++++++++++++++-- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/app/core/shared/version.model.ts b/src/app/core/shared/version.model.ts index 48d6eb0b68..7207637a21 100644 --- a/src/app/core/shared/version.model.ts +++ b/src/app/core/shared/version.model.ts @@ -47,6 +47,12 @@ export class Version extends DSpaceObject { @autoserialize summary: string; + /** + * The name of the submitter of this version + */ + @autoserialize + submitterName: string; + /** * The Date this version was created */ diff --git a/src/app/shared/item/item-versions/item-versions.component.html b/src/app/shared/item/item-versions/item-versions.component.html index d8850bc544..432b10e8f1 100644 --- a/src/app/shared/item/item-versions/item-versions.component.html +++ b/src/app/shared/item/item-versions/item-versions.component.html @@ -17,7 +17,7 @@ {{"item.version.history.table.version" | translate}} - {{"item.version.history.table.editor" | translate}} + {{"item.version.history.table.editor" | translate}} {{"item.version.history.table.date" | translate}} {{"item.version.history.table.summary" | translate}} @@ -87,10 +87,8 @@ - - - {{eperson?.name}} - + + {{version?.submitterName}} {{version?.created | date : 'yyyy-MM-dd HH:mm:ss'}} diff --git a/src/app/shared/item/item-versions/item-versions.component.ts b/src/app/shared/item/item-versions/item-versions.component.ts index e7d65919d6..8ca8acf007 100644 --- a/src/app/shared/item/item-versions/item-versions.component.ts +++ b/src/app/shared/item/item-versions/item-versions.component.ts @@ -5,7 +5,6 @@ import { RemoteData } from '../../../core/data/remote-data'; import { BehaviorSubject, combineLatest, - combineLatest as observableCombineLatest, Observable, of, Subscription, @@ -48,6 +47,7 @@ import { ItemVersionsSharedService } from './item-versions-shared.service'; import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model'; import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; +import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; @Component({ selector: 'ds-item-versions', @@ -180,6 +180,7 @@ export class ItemVersionsComponent implements OnInit { private authorizationService: AuthorizationDataService, private workspaceItemDataService: WorkspaceitemDataService, private workflowItemDataService: WorkflowItemDataService, + private configurationService: ConfigurationDataService, ) { } @@ -375,6 +376,37 @@ export class ItemVersionsComponent implements OnInit { return this.authorizationService.isAuthorized(FeatureID.CanEditVersion, version.self); } + /** + * Show submitter in version history table + */ + showSubmitter() { + + const includeSubmitter$ = this.configurationService.findByPropertyName('versioning.item.history.include.submitter').pipe( + getFirstSucceededRemoteDataPayload(), + map((configurationProperty) => configurationProperty.values[0]), + startWith(false), + ); + + const isAdmin$ = combineLatest([ + this.authorizationService.isAuthorized(FeatureID.IsCollectionAdmin), + this.authorizationService.isAuthorized(FeatureID.IsCommunityAdmin), + this.authorizationService.isAuthorized(FeatureID.AdministratorOf), + ]).pipe( + map(([isCollectionAdmin, isCommunityAdmin, isSiteAdmin]) => { + return isCollectionAdmin || isCommunityAdmin || isSiteAdmin; + }), + take(1), + tap((res) => { console.log('isAdmin = ' + res); }) + ); + + return combineLatest([includeSubmitter$, isAdmin$]).pipe( + map(([includeSubmitter, isAdmin]) => { + return includeSubmitter && isAdmin; + }) + ); + + } + /** * Check if the current user can delete the version * @param version @@ -389,7 +421,7 @@ export class ItemVersionsComponent implements OnInit { */ getAllVersions(versionHistory$: Observable): void { const currentPagination = this.paginationService.getCurrentPagination(this.options.id, this.options); - observableCombineLatest([versionHistory$, currentPagination]).pipe( + combineLatest([versionHistory$, currentPagination]).pipe( switchMap(([versionHistory, options]: [VersionHistory, PaginationComponentOptions]) => { return this.versionHistoryService.getVersions(versionHistory.id, new PaginatedSearchOptions({pagination: Object.assign({}, options, {currentPage: options.currentPage})}), @@ -486,7 +518,7 @@ export class ItemVersionsComponent implements OnInit { ); this.itemPageRoutes$ = this.versionsRD$.pipe( getAllSucceededRemoteDataPayload(), - switchMap((versions) => observableCombineLatest(...versions.page.map((version) => version.item.pipe(getAllSucceededRemoteDataPayload())))), + switchMap((versions) => combineLatest(versions.page.map((version) => version.item.pipe(getAllSucceededRemoteDataPayload())))), map((versions) => { const itemPageRoutes = {}; versions.forEach((item) => itemPageRoutes[item.uuid] = getItemPageRoute(item)); From 12ab877ae4965b8acc7102a05be0972dd07bf114 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Thu, 23 Dec 2021 21:59:24 +0100 Subject: [PATCH 074/102] [CST-4981] fixed and finished unit testing --- .../admin-sidebar.component.spec.ts | 43 +++++++++++++++---- .../community-authorizations.component.ts | 4 +- src/app/navbar/navbar.component.spec.ts | 35 ++++++++++++++- src/app/navbar/navbar.component.ts | 7 +-- src/app/shared/menu/menu.component.spec.ts | 36 ++++------------ src/app/shared/menu/menu.component.ts | 7 ++- .../resource-policy-form.component.spec.ts | 4 +- .../statistics-page-routing.module.ts | 2 +- src/test.ts | 2 +- 9 files changed, 87 insertions(+), 53 deletions(-) diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts index 948d7d86bc..65026c1504 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts @@ -18,6 +18,8 @@ import { ActivatedRoute } from '@angular/router'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import createSpy = jasmine.createSpy; +import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { Item } from '../../core/shared/item.model'; describe('AdminSidebarComponent', () => { let comp: AdminSidebarComponent; @@ -26,6 +28,28 @@ describe('AdminSidebarComponent', () => { let authorizationService: AuthorizationDataService; let scriptService; + + const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } + }); + + + const routeStub = { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(mockItem) + }), + children: [] + }; + + beforeEach(waitForAsync(() => { authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: observableOf(true) @@ -42,6 +66,7 @@ describe('AdminSidebarComponent', () => { { provide: ActivatedRoute, useValue: {} }, { provide: AuthorizationDataService, useValue: authorizationService }, { provide: ScriptDataService, useValue: scriptService }, + { provide: ActivatedRoute, useValue: routeStub }, { provide: NgbModal, useValue: { open: () => {/*comment*/ @@ -229,19 +254,19 @@ describe('AdminSidebarComponent', () => { it('should contain site admin section', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'admin_search', visible: true, + id: 'admin_search', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'registries', visible: true, + id: 'registries', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - parentID: 'registries', visible: true, + parentID: 'registries', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'curation_tasks', visible: true, + id: 'curation_tasks', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'workflow', visible: true, + id: 'workflow', visible: true, })); }); }); @@ -259,7 +284,7 @@ describe('AdminSidebarComponent', () => { it('should show edit_community', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'edit_community', visible: true, + id: 'edit_community', visible: true, })); }); }); @@ -277,7 +302,7 @@ describe('AdminSidebarComponent', () => { it('should show edit_collection', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'edit_collection', visible: true, + id: 'edit_collection', visible: true, })); }); }); @@ -295,10 +320,10 @@ describe('AdminSidebarComponent', () => { it('should show access control section', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'access_control', visible: true, + id: 'access_control', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - parentID: 'access_control', visible: true, + parentID: 'access_control', visible: true, })); }); }); diff --git a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts index 8b241af667..d2a95b1941 100644 --- a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts +++ b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; -import { RemoteData } from 'src/app/core/data/remote-data'; -import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { RemoteData } from '../../../core/data/remote-data'; @Component({ selector: 'ds-community-authorizations', diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts index cbe6738241..e8c9fae591 100644 --- a/src/app/navbar/navbar.component.spec.ts +++ b/src/app/navbar/navbar.component.spec.ts @@ -13,10 +13,39 @@ import { MenuService } from '../shared/menu/menu.service'; import { MenuServiceStub } from '../shared/testing/menu-service.stub'; import { ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; +import { Item } from '../core/shared/item.model'; +import { createSuccessfulRemoteDataObject } from '../shared/remote-data.utils'; +import { By } from '@angular/platform-browser'; +import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; let comp: NavbarComponent; let fixture: ComponentFixture; +const authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) +}); + +const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } +}); + +const routeStub = { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(mockItem) + }), + children: [] +}; + + + describe('NavbarComponent', () => { const menuService = new MenuServiceStub(); @@ -33,7 +62,8 @@ describe('NavbarComponent', () => { Injector, { provide: MenuService, useValue: menuService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, - { provide: ActivatedRoute, useValue: {} } + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: ActivatedRoute, useValue: routeStub }, ], schemas: [NO_ERRORS_SCHEMA] }) @@ -42,7 +72,6 @@ describe('NavbarComponent', () => { // synchronous beforeEach beforeEach(() => { - spyOn(menuService, 'getMenuTopSections').and.returnValue(observableOf([])); fixture = TestBed.createComponent(NavbarComponent); @@ -53,4 +82,6 @@ describe('NavbarComponent', () => { it('should create', () => { expect(comp).toBeTruthy(); }); + + }); diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index df4dd72477..103449ff71 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -7,11 +7,8 @@ import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; import { environment } from '../../environments/environment'; -import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; -import { Router, ActivatedRoute } from '@angular/router'; -import { map, take } from 'rxjs/operators'; -import { RemoteData } from '../core/data/remote-data'; -import { Collection } from 'src/app/core/shared/collection.model'; +import { ActivatedRoute } from '@angular/router'; +import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; /** * Component representing the public navbar diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts index 14e28591f3..ce764d7757 100644 --- a/src/app/shared/menu/menu.component.spec.ts +++ b/src/app/shared/menu/menu.component.spec.ts @@ -10,9 +10,9 @@ import { MenuSection } from './menu.reducer'; import { Router, ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { MenuID } from './initial-menus-state'; -import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; -import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils'; import { Item } from '../../core/shared/item.model'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { createSuccessfulRemoteDataObject } from '../remote-data.utils'; describe('MenuComponent', () => { let comp: MenuComponent; @@ -22,6 +22,8 @@ describe('MenuComponent', () => { const mockMenuID = 'mock-menuID' as MenuID; + const mockStatisticSection = { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }; + const authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: observableOf(true) }); @@ -38,6 +40,7 @@ describe('MenuComponent', () => { } }); + const routeStub = { data: observableOf({ dso: createSuccessfulRemoteDataObject(mockItem) @@ -125,36 +128,15 @@ describe('MenuComponent', () => { }); describe('when unauthorized statistics', () => { - - beforeEach(() => { - comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); - authorizationService.isAuthorized().and.returnValue(observableOf(false)); - fixture.detectChanges(); + it('should get observable of empty object', () => { + expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable({}); }); - - it('when authorized statistics', (done => { - comp.sections.subscribe((sections) => { - expect(sections.length).toEqual(2); - done(); - }); - })); }); describe('get authorized statistics', () => { - - beforeEach(() => { - comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); - fixture.detectChanges(); + it('should get observable of empty object', () => { + expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable(mockStatisticSection); }); - - it('get authorized statistics', (done => { - comp.sections.subscribe((sections) => { - expect(sections.length).toEqual(3); - done(); - }); - })); }); - - }); diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 1070521704..584bee0b22 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -9,9 +9,9 @@ import { hasValue, isNotEmpty, hasValueOperator, isNotEmptyOperator } from '../e import { MenuSectionComponent } from './menu-section/menu-section.component'; import { getComponentForMenu } from './menu-section.decorator'; import { compareArraysUsingIds } from '../../item-page/simple/item-types/shared/item-relationships-utils'; -import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; -import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; -import { Router, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; /** * A basic implementation of a MenuComponent @@ -87,7 +87,6 @@ export class MenuComponent implements OnInit, OnDestroy { this.subs.push( this.sections.pipe( - tap(t => console.log(t)), // if you return an array from a switchMap it will emit each element as a separate event. // So this switchMap is equivalent to a subscribe with a forEach inside switchMap((sections: MenuSection[]) => sections), 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 7f66eb052c..d65f7346a8 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 @@ -32,13 +32,13 @@ import { RESOURCE_POLICY } from '../../../core/resource-policy/models/resource-p import { EPersonMock } from '../../testing/eperson.mock'; import { isNotEmptyOperator } from '../../empty.util'; import { ActivatedRoute, Router } from '@angular/router'; -import { RemoteData } from 'src/app/core/data/remote-data'; import { RouterMock } from '../../mocks/router.mock'; import { Store } from '@ngrx/store'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; -import { PaginationService } from 'src/app/core/pagination/pagination.service'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { StoreMock } from '../../testing/store.mock'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginationService } from '../../../core/pagination/pagination.service'; export const mockResourcePolicyFormData = { name: [ diff --git a/src/app/statistics-page/statistics-page-routing.module.ts b/src/app/statistics-page/statistics-page-routing.module.ts index 6047a82f1e..ef6f68d557 100644 --- a/src/app/statistics-page/statistics-page-routing.module.ts +++ b/src/app/statistics-page/statistics-page-routing.module.ts @@ -10,7 +10,7 @@ import { ThemedCommunityStatisticsPageComponent } from './community-statistics-p import { ThemedItemStatisticsPageComponent } from './item-statistics-page/themed-item-statistics-page.component'; import { ThemedSiteStatisticsPageComponent } from './site-statistics-page/themed-site-statistics-page.component'; import { ItemResolver } from '../item-page/item.resolver'; -import { StatisticsAdministratorGuard } from 'src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; +import { StatisticsAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; @NgModule({ imports: [ diff --git a/src/test.ts b/src/test.ts index c77b4bb2f0..16317897b1 100644 --- a/src/test.ts +++ b/src/test.ts @@ -15,6 +15,6 @@ getTestBed().initTestEnvironment( platformBrowserDynamicTesting() ); // Then we find all the tests. -const context = require.context('./app/shared/menu', true, /\.spec\.ts$/); +const context = require.context('./', true, /\.spec\.ts$/); // And load the modules. context.keys().map(context); From d2a4a5550775b0595c48c4b7aa1583ff91f6aeba Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 23 Dec 2021 23:41:56 +0100 Subject: [PATCH 075/102] [CST-4633] fix issue with filter scope and configuration --- .../search-filters.component.ts | 19 +++++++++++++++---- .../search-sidebar.component.html | 5 ++++- .../search-sidebar.component.ts | 5 +++++ src/app/shared/search/search.component.html | 6 ++++-- src/app/shared/search/search.component.ts | 18 ++++++++++++++---- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index 8b870f6467..b3850f3b49 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { SearchService } from '../../../core/shared/search/search.service'; import { RemoteData } from '../../../core/data/remote-data'; @@ -36,6 +36,16 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { */ clearParams; + /** + * The configuration to use for the search options + */ + @Input() currentConfiguration; + + /** + * The current search scope + */ + @Input() currentScope: string; + /** * True when the search component should show results on the current page */ @@ -56,8 +66,9 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { /** * Initialize instance variables * @param {SearchService} searchService - * @param {SearchConfigurationService} searchConfigService * @param {SearchFilterService} filterService + * @param {Router} router + * @param {SearchConfigurationService} searchConfigService */ constructor( private searchService: SearchService, @@ -82,8 +93,8 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { } initFilters() { - this.filters = this.searchConfigService.searchOptions.pipe( - switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getFirstSucceededRemoteData())), + this.filters = this.searchService.getConfig(this.currentScope, this.currentConfiguration).pipe( + getFirstSucceededRemoteData() ); } diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index c19831d71d..c9e34d5007 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -15,7 +15,10 @@ [defaultConfiguration]="configuration" [inPlaceSearch]="inPlaceSearch" (changeConfiguration)="changeConfiguration.emit($event)"> - + diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index f9f7165401..1b22144565 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -32,6 +32,11 @@ export class SearchSidebarComponent { */ @Input() configurationList: SearchConfigurationOption[]; + /** + * The current search scope + */ + @Input() currentScope: string; + /** * The current sort option used */ diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index f21dbeab22..02f59b2163 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -6,11 +6,11 @@ -
+
- + @@ -41,6 +41,7 @@ = new BehaviorSubject(null); + /** + * The current sort options used + */ + currentScope$: BehaviorSubject = new BehaviorSubject(''); + + /** + * The current sort options used + */ + currentSortOptions$: BehaviorSubject = new BehaviorSubject(null); + /** * The current search results */ @@ -127,9 +137,9 @@ export class SearchComponent implements OnInit { sortOptionsList$: BehaviorSubject = new BehaviorSubject([]); /** - * The current sort options used + * TRUE if the search option are initialized */ - currentSortOptions$: BehaviorSubject = new BehaviorSubject(null); + initialized$: BehaviorSubject = new BehaviorSubject(false); /** * Observable for whether or not the sidebar is currently collapsed @@ -204,7 +214,6 @@ export class SearchComponent implements OnInit { return searchOptions.pagination.id === this.paginationId; }) ).subscribe(([configuration, searchSortOptions, searchOptions, sortOption]: [string, SortOptions[], PaginatedSearchOptions, SortOptions]) => { - // Build the PaginatedSearchOptions object const combinedOptions = Object.assign({}, searchOptions, { @@ -215,9 +224,10 @@ export class SearchComponent implements OnInit { // Initialize variables this.currentConfiguration$.next(configuration); this.currentSortOptions$.next(newSearchOptions.sort); + this.currentScope$.next(newSearchOptions.scope); this.sortOptionsList$.next(searchSortOptions); this.searchOptions$.next(newSearchOptions); - + this.initialized$.next(true); // retrieve results this.retrieveSearchResults(newSearchOptions); }); From ffaebabd3bb544f4f9cb450272ed4fd2f931f36f Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 24 Dec 2021 00:47:31 +0100 Subject: [PATCH 076/102] [CST-5064] Tests WIP --- .../item/item-versions/item-versions.component.spec.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/shared/item/item-versions/item-versions.component.spec.ts b/src/app/shared/item/item-versions/item-versions.component.spec.ts index fff0744aba..4600f6a165 100644 --- a/src/app/shared/item/item-versions/item-versions.component.spec.ts +++ b/src/app/shared/item/item-versions/item-versions.component.spec.ts @@ -24,8 +24,9 @@ import { AuthorizationDataService } from '../../../core/data/feature-authorizati import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; +import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; -describe('ItemVersionsComponent', () => { +fdescribe('ItemVersionsComponent', () => { let component: ItemVersionsComponent; let fixture: ComponentFixture; let authenticationService: AuthService; @@ -109,6 +110,10 @@ describe('ItemVersionsComponent', () => { findById: EMPTY, }); + const configurationServiceSpy = jasmine.createSpyObj('configurationService', { + findByPropertyName: of(true), + }); + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -125,6 +130,7 @@ describe('ItemVersionsComponent', () => { {provide: VersionDataService, useValue: versionServiceSpy}, {provide: WorkspaceitemDataService, useValue: workspaceItemDataServiceSpy}, {provide: WorkflowItemDataService, useValue: workflowItemDataServiceSpy}, + {provide: ConfigurationDataService, useValue: configurationServiceSpy()}, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); From 9fd34034b9459433bd495cb4f2997628bdd566ce Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 24 Dec 2021 10:13:50 +0100 Subject: [PATCH 077/102] [CST-5064] Tests --- .../item/item-versions/item-versions.component.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/shared/item/item-versions/item-versions.component.spec.ts b/src/app/shared/item/item-versions/item-versions.component.spec.ts index 4600f6a165..8bb5554b77 100644 --- a/src/app/shared/item/item-versions/item-versions.component.spec.ts +++ b/src/app/shared/item/item-versions/item-versions.component.spec.ts @@ -26,7 +26,7 @@ import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; -fdescribe('ItemVersionsComponent', () => { +describe('ItemVersionsComponent', () => { let component: ItemVersionsComponent; let fixture: ComponentFixture; let authenticationService: AuthService; @@ -35,6 +35,7 @@ fdescribe('ItemVersionsComponent', () => { let workspaceItemDataService: WorkspaceitemDataService; let workflowItemDataService: WorkflowItemDataService; let versionService: VersionDataService; + let configurationService: ConfigurationDataService; const versionHistory = Object.assign(new VersionHistory(), { id: '1', @@ -130,7 +131,7 @@ fdescribe('ItemVersionsComponent', () => { {provide: VersionDataService, useValue: versionServiceSpy}, {provide: WorkspaceitemDataService, useValue: workspaceItemDataServiceSpy}, {provide: WorkflowItemDataService, useValue: workflowItemDataServiceSpy}, - {provide: ConfigurationDataService, useValue: configurationServiceSpy()}, + {provide: ConfigurationDataService, useValue: configurationServiceSpy}, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -141,6 +142,7 @@ fdescribe('ItemVersionsComponent', () => { workspaceItemDataService = TestBed.inject(WorkspaceitemDataService); workflowItemDataService = TestBed.inject(WorkflowItemDataService); versionService = TestBed.inject(VersionDataService); + configurationService = TestBed.inject(ConfigurationDataService); })); From 6f86824f2302a64f27d1b09bcf0af92a5327e700 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 24 Dec 2021 11:16:16 +0100 Subject: [PATCH 078/102] [CST-4633] restored ds-my-dspace-new-submission from mydspace page --- src/app/my-dspace-page/my-dspace-page.component.html | 4 ++++ src/app/my-dspace-page/my-dspace-page.component.ts | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/app/my-dspace-page/my-dspace-page.component.html b/src/app/my-dspace-page/my-dspace-page.component.html index 2b0dc7108a..45cade0804 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.html +++ b/src/app/my-dspace-page/my-dspace-page.component.html @@ -1,3 +1,7 @@ +
+ +
+ = new InjectionToken('searchConfigurationService'); @@ -46,6 +47,11 @@ export class MyDSpacePageComponent implements OnInit { */ configuration: string; + /** + * Variable for enumeration RoleType + */ + roleTypeEnum = RoleType; + /** * List of available view mode */ From cff29539fbf58b7afbe9943e61c845950f06b5d1 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 24 Dec 2021 11:41:23 +0100 Subject: [PATCH 079/102] [CST-4633] fix issue with view mode list --- .../search-sidebar/search-sidebar.component.ts | 3 ++- src/app/shared/search/search.component.html | 2 ++ src/app/shared/search/search.component.ts | 12 ++++++++++-- .../view-mode-switch/view-mode-switch.component.ts | 6 +++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index 1b22144565..39207bdbd6 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -4,6 +4,7 @@ import { SearchConfigurationOption } from '../search-switch-configuration/search import { Observable } from 'rxjs'; import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; import { SortOptions } from '../../../core/cache/models/sort-options.model'; +import { ViewMode } from '../../../core/shared/view-mode.model'; /** * This component renders a simple item page. @@ -50,7 +51,7 @@ export class SearchSidebarComponent { /** * The list of available view mode options */ - @Input() viewModeList; + @Input() viewModeList: ViewMode[]; /** * Whether to show the view mode switch diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index 02f59b2163..e445b0669c 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -47,6 +47,7 @@ [sortOptionsList]="(sortOptionsList$ | async)" [currentSortOption]="(currentSortOptions$ | async)" [inPlaceSearch]="inPlaceSearch" + [viewModeList]="viewModeList" (changeConfiguration)="changeContext($event.context)">
diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index 04bb5a2a7b..f739242357 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -96,6 +96,11 @@ export class SearchComponent implements OnInit { */ @Input() showSidebar = true; + /** + * List of available view mode + */ + @Input() useUniquePageId: false; + /** * List of available view mode */ @@ -178,8 +183,11 @@ export class SearchComponent implements OnInit { * If something changes, update the list of scopes for the dropdown */ ngOnInit(): void { - // Create an unique pagination id related to the instance of the SearchComponent - this.paginationId = uniqueId(this.paginationId); + if (this.useUniquePageId) { + // Create an unique pagination id related to the instance of the SearchComponent + this.paginationId = uniqueId(this.paginationId); + } + this.searchConfigService.setPaginationId(this.paginationId); if (hasValue(this.fixedFilterQuery)) { diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.ts index 4feb8927c2..146cb042f3 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.ts +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.ts @@ -17,13 +17,17 @@ import { Router } from '@angular/router'; templateUrl: './view-mode-switch.component.html' }) export class ViewModeSwitchComponent implements OnInit, OnDestroy { - @Input() viewModeList: ViewMode[]; /** * True when the search component should show results on the current page */ @Input() inPlaceSearch; + /** + * List of available view mode + */ + @Input() viewModeList: ViewMode[]; + /** * The current view mode */ From e76514ca39f0a17a5597ab59b289284c76635321 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 24 Dec 2021 11:51:06 +0100 Subject: [PATCH 080/102] [CST-4633] fix issue with view mode --- .../object-collection/object-collection.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts index 52881f5eaf..b567a6f0ba 100644 --- a/src/app/shared/object-collection/object-collection.component.ts +++ b/src/app/shared/object-collection/object-collection.component.ts @@ -2,14 +2,14 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; -import { filter, map, startWith } from 'rxjs/operators'; +import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { RemoteData } from '../../core/data/remote-data'; import { PageInfo } from '../../core/shared/page-info.model'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { ListableObject } from './shared/listable-object.model'; -import { isNotEmpty } from '../empty.util'; +import { isEmpty, isNotEmpty } from '../empty.util'; import { ViewMode } from '../../core/shared/view-mode.model'; import { CollectionElementLinkType } from './collection-element-link.type'; import { PaginatedList } from '../../core/data/paginated-list.model'; @@ -137,8 +137,8 @@ export class ObjectCollectionComponent implements OnInit { .queryParams .pipe( filter((params) => isNotEmpty(params.view)), - map((params) => params.view), - startWith(ViewMode.ListElement) + map((params) => isEmpty(params?.view) ? ViewMode.ListElement : params.view), + distinctUntilChanged() ); } From f154fb60e09a19dd4500198c096a69a154371593 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Fri, 24 Dec 2021 17:49:55 +0100 Subject: [PATCH 081/102] [CST-4981] Fixed unit testing --- src/app/shared/menu/menu.component.spec.ts | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts index ce764d7757..b84fad2b33 100644 --- a/src/app/shared/menu/menu.component.spec.ts +++ b/src/app/shared/menu/menu.component.spec.ts @@ -24,9 +24,7 @@ describe('MenuComponent', () => { const mockStatisticSection = { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }; - const authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) - }); + let authorizationService: AuthorizationDataService; const mockItem = Object.assign(new Item(), { id: 'fake-id', @@ -49,6 +47,11 @@ describe('MenuComponent', () => { }; beforeEach(waitForAsync(() => { + + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(false) + }); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule], declarations: [MenuComponent], @@ -128,14 +131,32 @@ describe('MenuComponent', () => { }); describe('when unauthorized statistics', () => { - it('should get observable of empty object', () => { - expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable({}); + + beforeEach(() => { + (authorizationService as any).isAuthorized.and.returnValue(observableOf(false)); + fixture.detectChanges(); + }); + + it('should return observable of empty object', done => { + comp.getAuthorizedStatistics(mockStatisticSection).subscribe((res) => { + expect(res).toEqual({}); + done(); + }); }); }); describe('get authorized statistics', () => { - it('should get observable of empty object', () => { - expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable(mockStatisticSection); + + beforeEach(() => { + (authorizationService as any).isAuthorized.and.returnValue(observableOf(true)); + fixture.detectChanges(); + }); + + it('should return observable of statistics section menu', done => { + comp.getAuthorizedStatistics(mockStatisticSection).subscribe((res) => { + expect(res).toEqual(mockStatisticSection); + done(); + }); }); }); From 99aef984431ac35f98e054382e74913dd71bd640 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Fri, 24 Dec 2021 19:34:10 +0100 Subject: [PATCH 082/102] [CST-4981] structure improvement,removed unecessary informations --- src/app/collection-page/collection-page-routing.module.ts | 1 - src/app/community-page/community-page-routing.module.ts | 1 - .../statistics-administrator.guard.ts | 1 - src/app/home-page/home-page-routing.module.ts | 1 - src/app/item-page/item-page-routing.module.ts | 1 - 5 files changed, 5 deletions(-) diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index b92c5bf414..5879e523af 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -72,7 +72,6 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen id: 'statistics_collection_:id', active: true, visible: true, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index 1be5472010..ad1b1fd2f2 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -55,7 +55,6 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' id: 'statistics_community_:id', active: true, visible: true, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts index 41ea8550a7..680495686e 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts @@ -5,7 +5,6 @@ import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/ro import { AuthService } from '../../../auth/auth.service'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../feature-id'; -import { tap } from 'rxjs/operators'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group diff --git a/src/app/home-page/home-page-routing.module.ts b/src/app/home-page/home-page-routing.module.ts index 2356170d4b..2a41c079da 100644 --- a/src/app/home-page/home-page-routing.module.ts +++ b/src/app/home-page/home-page-routing.module.ts @@ -21,7 +21,6 @@ import { ThemedHomePageComponent } from './themed-home-page.component'; active: true, visible: true, index: 2, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index b0412e5a0b..7d7912bb42 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -58,7 +58,6 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; id: 'statistics_item_:id', active: true, visible: true, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', From 3b00d0146661869ee396a365b056dab9cdcbc1c2 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 4 Jan 2022 12:59:52 +0100 Subject: [PATCH 083/102] [CST-4633] fix issue with view mode when not in params --- .../shared/object-collection/object-collection.component.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts index b567a6f0ba..29fdb37ea1 100644 --- a/src/app/shared/object-collection/object-collection.component.ts +++ b/src/app/shared/object-collection/object-collection.component.ts @@ -2,14 +2,14 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; -import { distinctUntilChanged, filter, map } from 'rxjs/operators'; +import { distinctUntilChanged, map } from 'rxjs/operators'; import { RemoteData } from '../../core/data/remote-data'; import { PageInfo } from '../../core/shared/page-info.model'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { ListableObject } from './shared/listable-object.model'; -import { isEmpty, isNotEmpty } from '../empty.util'; +import { isEmpty } from '../empty.util'; import { ViewMode } from '../../core/shared/view-mode.model'; import { CollectionElementLinkType } from './collection-element-link.type'; import { PaginatedList } from '../../core/data/paginated-list.model'; @@ -136,7 +136,6 @@ export class ObjectCollectionComponent implements OnInit { this.currentMode$ = this.route .queryParams .pipe( - filter((params) => isNotEmpty(params.view)), map((params) => isEmpty(params?.view) ? ViewMode.ListElement : params.view), distinctUntilChanged() ); From 216eb40ab5bfa37bc857c64f742eea4694f73dfa Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 10 Jan 2022 10:30:11 +0100 Subject: [PATCH 084/102] [CST-5064] console.log removed --- src/app/shared/item/item-versions/item-versions.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/shared/item/item-versions/item-versions.component.ts b/src/app/shared/item/item-versions/item-versions.component.ts index 8ca8acf007..6796dfd1ad 100644 --- a/src/app/shared/item/item-versions/item-versions.component.ts +++ b/src/app/shared/item/item-versions/item-versions.component.ts @@ -396,7 +396,6 @@ export class ItemVersionsComponent implements OnInit { return isCollectionAdmin || isCommunityAdmin || isSiteAdmin; }), take(1), - tap((res) => { console.log('isAdmin = ' + res); }) ); return combineLatest([includeSubmitter$, isAdmin$]).pipe( From 7e7ad9a4f375e6592fe1d53e02ad6115ea6705d7 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 12 Jan 2022 18:32:33 +0100 Subject: [PATCH 085/102] [CST-4880] Add fallback strategy for hanging server check cached requests --- src/app/core/server-check/server-check.guard.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/core/server-check/server-check.guard.ts b/src/app/core/server-check/server-check.guard.ts index af216bef0e..7c3329f51d 100644 --- a/src/app/core/server-check/server-check.guard.ts +++ b/src/app/core/server-check/server-check.guard.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router'; -import { Observable } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; +import { interval, Observable, race } from 'rxjs'; +import { map, mergeMapTo, tap } from 'rxjs/operators'; import { RootDataService } from '../data/root-data.service'; import { RemoteData } from '../data/remote-data'; @@ -27,7 +27,11 @@ export class ServerCheckGuard implements CanActivateChild { route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.rootDataService.findRoot(false).pipe( + const uncachedCheck$ = this.rootDataService.findRoot(false); + // fallback observable used if the uncached one hangs and doesn't emit value + const cachedCheck$ = interval(200).pipe(mergeMapTo((this.rootDataService.findRoot()))); + + return race([uncachedCheck$, cachedCheck$]).pipe( getFirstCompletedRemoteData(), map((res: RemoteData) => res.hasSucceeded), tap((hasSucceeded: boolean) => { From 6d1674cc8a31d66f9d9ca9e8a41501c1be1e3353 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 12 Jan 2022 20:09:21 +0100 Subject: [PATCH 086/102] [CST-4981] Fix issue with missing statistics menu --- src/app/shared/menu/menu.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 584bee0b22..caf613a33f 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, Component, Injector, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, Observable, Subscription, of as observableOf } from 'rxjs'; +import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; import { MenuService } from './menu.service'; import { MenuID } from './initial-menus-state'; import { MenuSection } from './menu.reducer'; -import { distinctUntilChanged, map, switchMap, mergeMap, tap, isEmpty } from 'rxjs/operators'; +import { distinctUntilChanged, map, mergeMap, switchMap } from 'rxjs/operators'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { hasValue, isNotEmpty, hasValueOperator, isNotEmptyOperator } from '../empty.util'; +import { hasValue, isNotEmptyOperator } from '../empty.util'; import { MenuSectionComponent } from './menu-section/menu-section.component'; import { getComponentForMenu } from './menu-section.decorator'; import { compareArraysUsingIds } from '../../item-page/simple/item-types/shared/item-relationships-utils'; @@ -90,7 +90,7 @@ export class MenuComponent implements OnInit, OnDestroy { // if you return an array from a switchMap it will emit each element as a separate event. // So this switchMap is equivalent to a subscribe with a forEach inside switchMap((sections: MenuSection[]) => sections), - switchMap((section: MenuSection) => { + mergeMap((section: MenuSection) => { if (section.id.includes('statistics')) { return this.getAuthorizedStatistics(section); } From 0c9dc4286c43e8ac73a784a5803c2b1fcc5cb075 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 12 Jan 2022 20:09:48 +0100 Subject: [PATCH 087/102] [CST-4981] Fix error with yarn clean command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 403973ef98..278afdf6c3 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "clean:json": "rimraf *.records.json", "clean:node": "rimraf node_modules", "clean:prod": "yarn run clean:dist && yarn run clean:log && yarn run clean:doc && yarn run clean:coverage && yarn run clean:json", - "clean": "yarn run clean:prod && yarn run clean:node && yarn run clean:dev:config", + "clean": "yarn run clean:prod && yarn run clean:dev:config && yarn run clean:node", "sync-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts", "build:mirador": "webpack --config webpack/webpack.mirador.config.ts", "merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", From 8666ae74f6caf172e955220c392746ce79ae1aaa Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 13 Jan 2022 10:48:46 +0100 Subject: [PATCH 088/102] 86367: Process empty parameters fix --- .../process-page/form/process-form.component.spec.ts | 11 +++++++++++ src/app/process-page/form/process-form.component.ts | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/app/process-page/form/process-form.component.spec.ts b/src/app/process-page/form/process-form.component.spec.ts index fce7368d54..10bc8294d3 100644 --- a/src/app/process-page/form/process-form.component.spec.ts +++ b/src/app/process-page/form/process-form.component.spec.ts @@ -87,4 +87,15 @@ describe('ProcessFormComponent', () => { component.submitForm({ controls: {} } as any); expect(scriptService.invoke).toHaveBeenCalled(); }); + + describe('when undefined parameters are provided', () => { + beforeEach(() => { + component.parameters = undefined; + }); + + it('should invoke the script with an empty array of parameters', () => { + component.submitForm({ controls: {} } as any); + expect(scriptService.invoke).toHaveBeenCalledWith(script.id, [], jasmine.anything()); + }); + }); }); diff --git a/src/app/process-page/form/process-form.component.ts b/src/app/process-page/form/process-form.component.ts index 1507fe05e9..70eb3160a8 100644 --- a/src/app/process-page/form/process-form.component.ts +++ b/src/app/process-page/form/process-form.component.ts @@ -12,6 +12,7 @@ import { Router } from '@angular/router'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; import { RemoteData } from '../../core/data/remote-data'; import { getProcessListRoute } from '../process-page-routing.paths'; +import { isEmpty } from '../../shared/empty.util'; /** * Component to create a new script @@ -35,7 +36,7 @@ export class ProcessFormComponent implements OnInit { /** * The parameter values to use to start the process */ - @Input() public parameters: ProcessParameter[]; + @Input() public parameters: ProcessParameter[] = []; /** * Optional files that are used as parameter values @@ -69,6 +70,9 @@ export class ProcessFormComponent implements OnInit { * @param form */ submitForm(form: NgForm) { + if (isEmpty(this.parameters)) { + this.parameters = []; + } if (!this.validateForm(form) || this.isRequiredMissing()) { return; } From 539ae50214755e66b5cf227e16c6ee9edeca64f9 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 13 Jan 2022 15:16:40 +0100 Subject: [PATCH 089/102] [CST-4633] Change label translations --- src/assets/i18n/en.json5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 1ab27fdb0e..6b47238f5b 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2588,7 +2588,7 @@ "mydspace.search-form.placeholder": "Search in mydspace...", - "mydspace.show.workflow": "All tasks", + "mydspace.show.workflow": "Workflow tasks", "mydspace.show.workspace": "Your Submissions", @@ -4023,7 +4023,7 @@ "workflowAdmin.search.results.head": "Administer Workflow", - "workflow.search.results.head": "All tasks", + "workflow.search.results.head": "Workflow tasks", From 6b2efd2b1622d049a5c2dab0799058e488d24a5d Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 13 Jan 2022 15:17:06 +0100 Subject: [PATCH 090/102] [CST-4633] Change view mode labels --- src/app/core/shared/view-mode.model.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/core/shared/view-mode.model.ts b/src/app/core/shared/view-mode.model.ts index c2f076a5e5..a27cb6954b 100644 --- a/src/app/core/shared/view-mode.model.ts +++ b/src/app/core/shared/view-mode.model.ts @@ -3,8 +3,8 @@ */ export enum ViewMode { - ListElement = 'listElement', - GridElement = 'gridElement', - DetailedListElement = 'detailedListElement', - StandalonePage = 'standalonePage', + ListElement = 'list', + GridElement = 'grid', + DetailedListElement = 'detailed', + StandalonePage = 'standalone', } From 0d73b6d164979f122522b5e0ba5ffb4b637fdb4c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 14 Jan 2022 12:15:12 +0100 Subject: [PATCH 091/102] [CST-4633] Fix issue with detail view mode --- .../search-sidebar.component.html | 4 +- .../search-sidebar.component.ts | 5 + src/app/shared/search/search.component.html | 6 +- src/app/shared/search/search.component.ts | 7 ++ .../view-mode-switch.component.html | 2 +- .../view-mode-switch.component.spec.ts | 93 ++++++++++++++----- .../view-mode-switch.component.ts | 22 ++++- 7 files changed, 110 insertions(+), 29 deletions(-) diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index c9e34d5007..e17fe941ba 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -8,7 +8,9 @@
- +
- + + + diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts index 09929f1973..72c026e42e 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts @@ -4,7 +4,6 @@ import { DsDynamicLookupRelationSearchTabComponent } from './dynamic-lookup-rela import { SearchService } from '../../../../../../core/shared/search/search.service'; import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service'; import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; -import { RouteService } from '../../../../../../core/services/route.service'; import { RouterTestingModule } from '@angular/router/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { VarDirective } from '../../../../../utils/var.directive'; @@ -15,13 +14,13 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../remote-data.ut import { buildPaginatedList } from '../../../../../../core/data/paginated-list.model'; import { ItemSearchResult } from '../../../../../object-collection/shared/item-search-result.model'; import { Item } from '../../../../../../core/shared/item.model'; -import { ActivatedRoute } from '@angular/router'; import { LookupRelationService } from '../../../../../../core/data/lookup-relation.service'; import { PaginationService } from '../../../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub'; import { RelationshipService } from '../../../../../../core/data/relationship.service'; import { relatedRelationships } from '../../../../../testing/related-relationships.mock'; import { RelationshipType } from '../../../../../../core/shared/item-relationships/relationship-type.model'; +import { SearchObjects } from '../../../../../search/models/search-objects.model'; describe('DsDynamicLookupRelationSearchTabComponent', () => { @@ -41,6 +40,7 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => { let selection$; let results; + let searchResult; let selectableListService; let lookupRelationService; const relationshipService = jasmine.createSpyObj('searchByItemsAndType',{ @@ -79,6 +79,9 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => { selection$ = observableOf([searchResult1, searchResult2]); results = buildPaginatedList(undefined, [searchResult1, searchResult2, searchResult3]); + searchResult = Object.assign(new SearchObjects(), { + page: [searchResult1, searchResult2, searchResult3] + }); selectableListService = jasmine.createSpyObj('selectableListService', ['deselect', 'select', 'deselectAll']); lookupRelationService = jasmine.createSpyObj('lookupRelationService', { getLocalResults: createSuccessfulRemoteDataObject$(results) @@ -101,14 +104,6 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => { paginatedSearchOptions: observableOf(pSearchOptions) } }, - { - provide: RouteService, useValue: { - setParameter: () => { - // do nothing - } - } - }, - { provide: ActivatedRoute, useValue: { snapshot: { queryParams: {} } } }, { provide: LookupRelationService, useValue: lookupRelationService }, { provide: PaginationService, useValue: new PaginationServiceStub() }, { provide: RelationshipService, useValue: relationshipService } @@ -186,6 +181,7 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => { describe('check searchByItemsAndType', () => { it('should call relationshipService.searchByItemsAndType', () => { + component.onResultFound(searchResult); expect(relationshipService.searchByItemsAndType).toHaveBeenCalled(); }); }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts index 23dd97601e..5a6b07fd44 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts @@ -4,18 +4,15 @@ import { SearchConfigurationService } from '../../../../../../core/shared/search import { Item } from '../../../../../../core/shared/item.model'; import { SearchResult } from '../../../../../search/models/search-result.model'; import { PaginatedList } from '../../../../../../core/data/paginated-list.model'; -import { RemoteData } from '../../../../../../core/data/remote-data'; import { Observable } from 'rxjs'; import { RelationshipOptions } from '../../../models/relationship-options.model'; import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; import { ListableObject } from '../../../../../object-collection/shared/listable-object.model'; import { SearchService } from '../../../../../../core/shared/search/search.service'; -import { ActivatedRoute, Router } from '@angular/router'; import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service'; import { hasValue } from '../../../../../empty.util'; -import { map, mapTo, startWith, switchMap, take, tap } from 'rxjs/operators'; +import { map, mapTo, switchMap, take, tap } from 'rxjs/operators'; import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../../../../../core/shared/operators'; -import { RouteService } from '../../../../../../core/services/route.service'; import { CollectionElementLinkType } from '../../../../../object-collection/collection-element-link.type'; import { Context } from '../../../../../../core/shared/context.model'; import { LookupRelationService } from '../../../../../../core/data/lookup-relation.service'; @@ -24,6 +21,9 @@ import { RelationshipService } from '../../../../../../core/data/relationship.se import { RelationshipType } from '../../../../../../core/shared/item-relationships/relationship-type.model'; import { Relationship } from '../../../../../../core/shared/item-relationships/relationship.model'; +import { SearchObjects } from '../../../../../search/models/search-objects.model'; +import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; @Component({ @@ -107,7 +107,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest /** * Search results */ - resultsRD$: Observable>>>; + resultsRD$: BehaviorSubject> = new BehaviorSubject>(null); /** * Are all results selected? @@ -142,13 +142,15 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest */ linkTypes = CollectionElementLinkType; + /** + * Emits an event with the current search result entries + */ + @Output() resultFound: EventEmitter> = new EventEmitter>(); + constructor( private searchService: SearchService, - private router: Router, - private route: ActivatedRoute, private selectableListService: SelectableListService, public searchConfigService: SearchConfigurationService, - private routeService: RouteService, public lookupRelationService: LookupRelationService, private relationshipService: RelationshipService, private paginationService: PaginationService @@ -160,21 +162,6 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest */ ngOnInit(): void { this.resetRoute(); - this.routeService.setParameter('fixedFilterQuery', this.relationship.filter); - this.routeService.setParameter('configuration', this.relationship.searchConfiguration); - this.resultsRD$ = this.searchConfigService.paginatedSearchOptions.pipe( - switchMap((options) => this.lookupRelationService.getLocalResults(this.relationship, options).pipe( - tap( res => { - if ( !!res && res.hasSucceeded && this.isEditRelationship ) { - const idOfItems = res.payload.page.map( itemSearchResult => { - return itemSearchResult.indexableObject.uuid; - }); - this.setSelectedIds(idOfItems,res.payload.page); - } - }), - startWith(undefined), - )) - ); } /** @@ -188,7 +175,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest * Selects a page in the store * @param page The page to select */ - selectPage(page: SearchResult[]) { + selectPage(page: SearchResult[]) { this.selection$ .pipe(take(1)) .subscribe((selection: SearchResult[]) => { @@ -202,7 +189,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest * Deselects a page in the store * @param page the page to deselect */ - deselectPage(page: SearchResult[]) { + deselectPage(page: SearchResult[]) { this.allSelected = false; this.selection$ .pipe(take(1)) @@ -306,4 +293,16 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest this.subscription.unsubscribe(); } } + + onResultFound($event: SearchObjects) { + console.log($event); + this.resultsRD$.next($event); + this.resultFound.emit($event); + if (this.isEditRelationship ) { + const idOfItems = $event.page.map( itemSearchResult => { + return itemSearchResult.indexableObject.uuid; + }); + this.setSelectedIds(idOfItems, $event); + } + } } From b7c46ffe29b40280314afa34ff9b1849d6219d0d Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 14 Jan 2022 12:37:29 +0100 Subject: [PATCH 094/102] [CST-4633] Fix e2e test --- cypress/integration/search-page.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/search-page.spec.ts b/cypress/integration/search-page.spec.ts index a2bfbe6a5b..859c765d2e 100644 --- a/cypress/integration/search-page.spec.ts +++ b/cypress/integration/search-page.spec.ts @@ -53,7 +53,7 @@ describe('Search Page', () => { // Click to display grid view // TODO: These buttons should likely have an easier way to uniquely select - cy.get('#search-sidebar-content > ds-view-mode-switch > .btn-group > [href="/search?spc.sf=score&spc.sd=DESC&view=grid"] > .fas').click(); + cy.get('#search-sidebar-content > ds-view-mode-switch > .btn-group > [href="/search?view=grid"] > .fas').click(); // tag must be loaded cy.get('ds-search-page').should('exist'); From fd610dbf4d5db093cedce46f0a5d32342c5e36c1 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 20 Jan 2022 14:24:54 +0100 Subject: [PATCH 095/102] [CST-4880] Set 500 response status to internal server error page --- src/app/core/services/server-response.service.ts | 4 ++++ .../page-internal-server-error.component.ts | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/app/core/services/server-response.service.ts b/src/app/core/services/server-response.service.ts index df5662991d..02e00446bc 100644 --- a/src/app/core/services/server-response.service.ts +++ b/src/app/core/services/server-response.service.ts @@ -31,4 +31,8 @@ export class ServerResponseService { setNotFound(message = 'Not found'): this { return this.setStatus(404, message); } + + setInternalServerError(message = 'Internal Server Error'): this { + return this.setStatus(500, message); + } } diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.ts b/src/app/page-internal-server-error/page-internal-server-error.component.ts index 09b441969a..7ecb0a7609 100644 --- a/src/app/page-internal-server-error/page-internal-server-error.component.ts +++ b/src/app/page-internal-server-error/page-internal-server-error.component.ts @@ -1,7 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ServerResponseService } from '../core/services/server-response.service'; /** - * This component representing the `PageNotFound` DSpace page. + * This component representing the `PageInternalServer` DSpace page. */ @Component({ selector: 'ds-page-internal-server-error', @@ -10,4 +11,13 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; changeDetection: ChangeDetectionStrategy.Default }) export class PageInternalServerErrorComponent { + + /** + * Initialize instance variables + * + * @param {ServerResponseService} responseService + */ + constructor(private responseService: ServerResponseService) { + this.responseService.setInternalServerError(); + } } From 98bdc59c2869480629e3f58cf2068760363ddc24 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 20 Jan 2022 14:26:04 +0100 Subject: [PATCH 096/102] [CST-4880] use getPageInternalServerErrorRoute function to determinate 500 page route --- src/app/root/root.component.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index b70573e42b..c0ccc712e9 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -1,5 +1,5 @@ import { map } from 'rxjs/operators'; -import { Component, Inject, OnInit, Input } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { combineLatest as combineLatestObservable, Observable, of } from 'rxjs'; @@ -19,6 +19,7 @@ import { ThemeConfig } from '../../config/theme.model'; import { Angulartics2DSpace } from '../statistics/angulartics/dspace-provider'; import { environment } from '../../environments/environment'; import { slideSidebarPadding } from '../shared/animations/slide'; +import { getPageInternalServerErrorRoute } from '../app-routing-paths'; @Component({ selector: 'ds-root', @@ -67,12 +68,12 @@ export class RootComponent implements OnInit { this.totalSidebarWidth = this.cssService.getVariable('totalSidebarWidth'); const sidebarCollapsed = this.menuService.isMenuCollapsed(MenuID.ADMIN); - this.slideSidebarOver = combineLatestObservable(sidebarCollapsed, this.windowService.isXsOrSm()) + this.slideSidebarOver = combineLatestObservable([sidebarCollapsed, this.windowService.isXsOrSm()]) .pipe( map(([collapsed, mobile]) => collapsed || mobile) ); - if (this.router.url === '/500') { + if (this.router.url === getPageInternalServerErrorRoute()) { this.shouldShowRouteLoader = false; } } From 016bf4b368cf0165d5a0a6bfb201be894e04fb63 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 20 Jan 2022 18:08:57 +0100 Subject: [PATCH 097/102] [CST-4633] Add flag to hide view mode switcher --- .../dynamic-lookup-relation-search-tab.component.html | 1 + src/app/shared/search/search.component.html | 2 ++ src/app/shared/search/search.component.ts | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html index 08d5198f08..54ac149d48 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html @@ -7,6 +7,7 @@ [searchFormPlaceholder]="'submission.sections.describe.relationship-lookup.search-tab.search-form.placeholder'" [selectable]="true" [selectionConfig]="{ repeatable: repeatable, listId: listId }" + [showViewModes]="false" (resultFound)="onResultFound($event)" (deselectObject)="deselectObject.emit($event)" (selectObject)="selectObject.emit($event)"> diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index d1aa1390db..5ca3e925a5 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -54,6 +54,7 @@ [currentSortOption]="(currentSortOptions$ | async)" [inPlaceSearch]="inPlaceSearch" [viewModeList]="viewModeList" + [showViewModes]="showViewModes" (changeConfiguration)="changeContext($event.context)" (changeViewMode)="changeViewMode()"> diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index feebb90d03..c017a5065b 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -119,6 +119,11 @@ export class SearchComponent implements OnInit { */ @Input() showSidebar = true; + /** + * Whether to show the view mode switch + */ + @Input() showViewModes = true; + /** * List of available view mode */ From a95bf63ad0d2643a12f92c128aea458f50249d1a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 20 Jan 2022 18:14:29 +0100 Subject: [PATCH 098/102] [CST-4633] Fix issue with already selected relationship in the dynamic-lookup-relation-search-tab.component --- .../dynamic-lookup-relation-search-tab.component.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts index 5a6b07fd44..f70f073743 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts @@ -234,7 +234,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest * @param idOfItems the uuid of items that are being checked * @param resultListOfItems the list of results of the items */ - setSelectedIds(idOfItems, resultListOfItems) { + setSelectedIds(idOfItems: string[], resultListOfItems: SearchResult[]) { let relationType = this.relationshipType.rightwardType; if ( this.isLeft ) { relationType = this.relationshipType.leftwardType; @@ -254,7 +254,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest } const uuid = arrUrl[ arrUrl.length - 1 ]; - return this.getRelatedItem(uuid,resultListOfItems); + return this.getRelatedItem(uuid, resultListOfItems); }); selectableObject = selectableObject.filter( (selObject) => { @@ -274,11 +274,11 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest this.allSelected = false; this.selection$ .pipe(take(1)) - .subscribe((selection: SearchResult[]) => this.deselectObject.emit(...selection)); + .subscribe((selection: SearchResult[]) => this.deselectObject.emit(...selection)); this.selectableListService.deselectAll(this.listId); } - getRelatedItem(uuid: string, resultList: SearchResult[]) { + getRelatedItem(uuid: string, resultList: SearchResult[]) { return resultList.find( (resultItem) => { return resultItem.indexableObject.uuid === uuid; }); @@ -295,14 +295,13 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest } onResultFound($event: SearchObjects) { - console.log($event); this.resultsRD$.next($event); this.resultFound.emit($event); if (this.isEditRelationship ) { const idOfItems = $event.page.map( itemSearchResult => { return itemSearchResult.indexableObject.uuid; }); - this.setSelectedIds(idOfItems, $event); + this.setSelectedIds(idOfItems, $event.page); } } } From bcb96c522c3086349ed0eda8888dcc201aa2a16e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 20 Jan 2022 14:08:22 -0600 Subject: [PATCH 099/102] Environment variables must be strings or numbers, not booleans --- docker/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index adeb61dfc6..1387b1de39 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -16,11 +16,11 @@ services: dspace-angular: container_name: dspace-angular environment: - DSPACE_UI_SSL: false + DSPACE_UI_SSL: 'false' DSPACE_UI_HOST: dspace-angular DSPACE_UI_PORT: '4000' DSPACE_UI_NAMESPACE: / - DSPACE_REST_SSL: false + DSPACE_REST_SSL: 'false' DSPACE_REST_HOST: localhost DSPACE_REST_PORT: 8080 DSPACE_REST_NAMESPACE: /server From e61de0682f47315c1a2298af6f55df82ae6f8e70 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 21 Jan 2022 11:50:08 -0600 Subject: [PATCH 100/102] Revert "Show an error page if the rest api is not available" --- src/app/app-routing-paths.ts | 6 -- src/app/app-routing.module.ts | 39 ++++------- src/app/app.module.ts | 6 +- src/app/core/data/root-data.service.ts | 7 -- .../server-check/server-check.guard.spec.ts | 68 ------------------- .../core/server-check/server-check.guard.ts | 46 ------------- .../core/services/server-response.service.ts | 4 -- .../page-internal-server-error.component.html | 10 --- .../page-internal-server-error.component.scss | 0 .../page-internal-server-error.component.ts | 23 ------- ...ed-page-internal-server-error.component.ts | 26 ------- src/app/root/root.component.ts | 9 +-- src/assets/i18n/en.json5 | 5 -- 13 files changed, 15 insertions(+), 234 deletions(-) delete mode 100644 src/app/core/server-check/server-check.guard.spec.ts delete mode 100644 src/app/core/server-check/server-check.guard.ts delete mode 100644 src/app/page-internal-server-error/page-internal-server-error.component.html delete mode 100644 src/app/page-internal-server-error/page-internal-server-error.component.scss delete mode 100644 src/app/page-internal-server-error/page-internal-server-error.component.ts delete mode 100644 src/app/page-internal-server-error/themed-page-internal-server-error.component.ts diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index d9c3410931..db6b22a023 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -89,12 +89,6 @@ export function getPageNotFoundRoute() { return `/${PAGE_NOT_FOUND_PATH}`; } -export const INTERNAL_SERVER_ERROR = '500'; - -export function getPageInternalServerErrorRoute() { - return `/${INTERNAL_SERVER_ERROR}`; -} - export const INFO_MODULE_PATH = 'info'; export function getInfoModulePath() { return `/${INFO_MODULE_PATH}`; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 88f7791b1b..04d2c55bdd 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -11,12 +11,10 @@ import { FORBIDDEN_PATH, FORGOT_PASSWORD_PATH, INFO_MODULE_PATH, - INTERNAL_SERVER_ERROR, - LEGACY_BITSTREAM_MODULE_PATH, PROFILE_MODULE_PATH, REGISTER_PATH, - REQUEST_COPY_MODULE_PATH, WORKFLOW_ITEM_MODULE_PATH, + LEGACY_BITSTREAM_MODULE_PATH, REQUEST_COPY_MODULE_PATH, } from './app-routing-paths'; import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routing-paths'; import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths'; @@ -28,25 +26,14 @@ import { SiteRegisterGuard } from './core/data/feature-authorization/feature-aut 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 { ServerCheckGuard } from './core/server-check/server-check.guard'; @NgModule({ imports: [ - RouterModule.forRoot([ - { path: INTERNAL_SERVER_ERROR, component: ThemedPageInternalServerErrorComponent }, - { - path: '', - canActivate: [AuthBlockingGuard], - canActivateChild: [ServerCheckGuard], + RouterModule.forRoot([{ + path: '', canActivate: [AuthBlockingGuard], children: [ { path: '', redirectTo: '/home', pathMatch: 'full' }, - { - path: 'reload/:rnd', - component: ThemedPageNotFoundComponent, - pathMatch: 'full', - canActivate: [ReloadGuard] - }, + { path: 'reload/:rnd', component: ThemedPageNotFoundComponent, pathMatch: 'full', canActivate: [ReloadGuard] }, { path: 'home', loadChildren: () => import('./home-page/home-page.module') @@ -102,8 +89,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; .then((m) => m.ItemPageModule), canActivate: [EndUserAgreementCurrentUserGuard] }, - { - path: 'entities/:entity-type', + { path: 'entities/:entity-type', loadChildren: () => import('./item-page/item-page.module') .then((m) => m.ItemPageModule), canActivate: [EndUserAgreementCurrentUserGuard] @@ -147,12 +133,12 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; { path: 'login', loadChildren: () => import('./login-page/login-page.module') - .then((m) => m.LoginPageModule) + .then((m) => m.LoginPageModule), }, { path: 'logout', loadChildren: () => import('./logout-page/logout-page.module') - .then((m) => m.LogoutPageModule) + .then((m) => m.LogoutPageModule), }, { path: 'submit', @@ -192,7 +178,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; }, { path: INFO_MODULE_PATH, - loadChildren: () => import('./info/info.module').then((m) => m.InfoModule) + loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), }, { path: REQUEST_COPY_MODULE_PATH, @@ -206,7 +192,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; { path: 'statistics', loadChildren: () => import('./statistics-page/statistics-page-routing.module') - .then((m) => m.StatisticsPageRoutingModule) + .then((m) => m.StatisticsPageRoutingModule), }, { path: ACCESS_CONTROL_MODULE_PATH, @@ -214,10 +200,9 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; canActivate: [GroupAdministratorGuard], }, { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, - ] - } - ], { - onSameUrlNavigation: 'reload', + ]} + ],{ + onSameUrlNavigation: 'reload', }) ], exports: [RouterModule], diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ceda63e7ec..32c3c78348 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -54,8 +54,6 @@ import { ThemedFooterComponent } from './footer/themed-footer.component'; import { ThemedBreadcrumbsComponent } from './breadcrumbs/themed-breadcrumbs.component'; import { ThemedHeaderNavbarWrapperComponent } from './header-nav-wrapper/themed-header-navbar-wrapper.component'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; -import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component'; -import { PageInternalServerErrorComponent } from './page-internal-server-error/page-internal-server-error.component'; import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; @@ -183,9 +181,7 @@ const DECLARATIONS = [ ThemedBreadcrumbsComponent, ForbiddenComponent, ThemedForbiddenComponent, - IdleModalComponent, - ThemedPageInternalServerErrorComponent, - PageInternalServerErrorComponent + IdleModalComponent ]; const EXPORTS = [ diff --git a/src/app/core/data/root-data.service.ts b/src/app/core/data/root-data.service.ts index ce1b33a9e3..8b4e836671 100644 --- a/src/app/core/data/root-data.service.ts +++ b/src/app/core/data/root-data.service.ts @@ -106,12 +106,5 @@ export class RootDataService { findAllByHref(href: string | Observable, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { return this.dataService.findAllByHref(href, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } - - /** - * Set to sale the root endpoint cache hit - */ - invalidateRootCache() { - this.requestService.setStaleByHrefSubstring('server/api'); - } } /* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/server-check/server-check.guard.spec.ts b/src/app/core/server-check/server-check.guard.spec.ts deleted file mode 100644 index 30d07234a3..0000000000 --- a/src/app/core/server-check/server-check.guard.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ServerCheckGuard } from './server-check.guard'; -import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { take } from 'rxjs/operators'; -import { getPageInternalServerErrorRoute } from '../../app-routing-paths'; -import { Router } from '@angular/router'; -import { RootDataService } from '../data/root-data.service'; -import { Root } from '../data/root.model'; -import SpyObj = jasmine.SpyObj; - -describe('ServerCheckGuard', () => { - let guard: ServerCheckGuard; - let router: SpyObj; - let rootDataServiceStub: SpyObj; - - rootDataServiceStub = jasmine.createSpyObj('RootDataService', { - findRoot: jasmine.createSpy('findRoot'), - invalidateRootCache: jasmine.createSpy('invalidateRootCache') - }); - router = jasmine.createSpyObj('Router', { - navigateByUrl: jasmine.createSpy('navigateByUrl') - }); - - beforeEach(() => { - guard = new ServerCheckGuard(router, rootDataServiceStub); - }); - - afterEach(() => { - router.navigateByUrl.calls.reset(); - rootDataServiceStub.invalidateRootCache.calls.reset(); - rootDataServiceStub.findRoot.calls.reset(); - }); - - it('should be created', () => { - expect(guard).toBeTruthy(); - }); - - describe('when root endpoint has succeeded', () => { - beforeEach(() => { - rootDataServiceStub.findRoot.and.returnValue(createSuccessfulRemoteDataObject$({} as any)); - }); - - it('should not redirect to error page', () => { - guard.canActivateChild({} as any, {} as any).pipe( - take(1) - ).subscribe((canActivate: boolean) => { - expect(canActivate).toEqual(true); - expect(rootDataServiceStub.invalidateRootCache).not.toHaveBeenCalled(); - expect(router.navigateByUrl).not.toHaveBeenCalled(); - }); - }); - }); - - describe('when root endpoint has not succeeded', () => { - beforeEach(() => { - rootDataServiceStub.findRoot.and.returnValue(createFailedRemoteDataObject$()); - }); - - it('should redirect to error page', () => { - guard.canActivateChild({} as any, {} as any).pipe( - take(1) - ).subscribe((canActivate: boolean) => { - expect(canActivate).toEqual(false); - expect(rootDataServiceStub.invalidateRootCache).toHaveBeenCalled(); - expect(router.navigateByUrl).toHaveBeenCalledWith(getPageInternalServerErrorRoute()); - }); - }); - }); -}); diff --git a/src/app/core/server-check/server-check.guard.ts b/src/app/core/server-check/server-check.guard.ts deleted file mode 100644 index 7c3329f51d..0000000000 --- a/src/app/core/server-check/server-check.guard.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router'; - -import { interval, Observable, race } from 'rxjs'; -import { map, mergeMapTo, tap } from 'rxjs/operators'; - -import { RootDataService } from '../data/root-data.service'; -import { RemoteData } from '../data/remote-data'; -import { getPageInternalServerErrorRoute } from '../../app-routing-paths'; -import { getFirstCompletedRemoteData } from '../shared/operators'; - -@Injectable({ - providedIn: 'root' -}) -/** - * A guard that checks if root api endpoint is reachable. - * If not redirect to 500 error page - */ -export class ServerCheckGuard implements CanActivateChild { - constructor(private router: Router, private rootDataService: RootDataService) { - } - - /** - * True when root api endpoint is reachable. - */ - canActivateChild( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot): Observable { - - const uncachedCheck$ = this.rootDataService.findRoot(false); - // fallback observable used if the uncached one hangs and doesn't emit value - const cachedCheck$ = interval(200).pipe(mergeMapTo((this.rootDataService.findRoot()))); - - return race([uncachedCheck$, cachedCheck$]).pipe( - getFirstCompletedRemoteData(), - map((res: RemoteData) => res.hasSucceeded), - tap((hasSucceeded: boolean) => { - if (!hasSucceeded) { - this.rootDataService.invalidateRootCache(); - this.router.navigateByUrl(getPageInternalServerErrorRoute()); - } - }), - ); - - } -} diff --git a/src/app/core/services/server-response.service.ts b/src/app/core/services/server-response.service.ts index 02e00446bc..df5662991d 100644 --- a/src/app/core/services/server-response.service.ts +++ b/src/app/core/services/server-response.service.ts @@ -31,8 +31,4 @@ export class ServerResponseService { setNotFound(message = 'Not found'): this { return this.setStatus(404, message); } - - setInternalServerError(message = 'Internal Server Error'): this { - return this.setStatus(500, message); - } } diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.html b/src/app/page-internal-server-error/page-internal-server-error.component.html deleted file mode 100644 index 09b499b496..0000000000 --- a/src/app/page-internal-server-error/page-internal-server-error.component.html +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.scss b/src/app/page-internal-server-error/page-internal-server-error.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.ts b/src/app/page-internal-server-error/page-internal-server-error.component.ts deleted file mode 100644 index 7ecb0a7609..0000000000 --- a/src/app/page-internal-server-error/page-internal-server-error.component.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ServerResponseService } from '../core/services/server-response.service'; - -/** - * This component representing the `PageInternalServer` DSpace page. - */ -@Component({ - selector: 'ds-page-internal-server-error', - styleUrls: ['./page-internal-server-error.component.scss'], - templateUrl: './page-internal-server-error.component.html', - changeDetection: ChangeDetectionStrategy.Default -}) -export class PageInternalServerErrorComponent { - - /** - * Initialize instance variables - * - * @param {ServerResponseService} responseService - */ - constructor(private responseService: ServerResponseService) { - this.responseService.setInternalServerError(); - } -} diff --git a/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts b/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts deleted file mode 100644 index e8792c4789..0000000000 --- a/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component } from '@angular/core'; -import { ThemedComponent } from '../shared/theme-support/themed.component'; -import { PageInternalServerErrorComponent } from './page-internal-server-error.component'; - -/** - * Themed wrapper for PageInternalServerErrorComponent - */ -@Component({ - selector: 'ds-themed-search-page', - styleUrls: [], - templateUrl: '../shared/theme-support/themed.component.html', -}) -export class ThemedPageInternalServerErrorComponent extends ThemedComponent { - - protected getComponentName(): string { - return 'PageInternalServerErrorComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../themes/${themeName}/app/page-internal-server-error/page-internal-server-error.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./page-internal-server-error.component`); - } -} diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index dc44095573..6ba859ef23 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -1,5 +1,5 @@ import { map } from 'rxjs/operators'; -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { Component, Inject, OnInit, Input } from '@angular/core'; import { Router } from '@angular/router'; import { combineLatest as combineLatestObservable, Observable, of } from 'rxjs'; @@ -19,7 +19,6 @@ import { ThemeConfig } from '../../config/theme.model'; import { Angulartics2DSpace } from '../statistics/angulartics/dspace-provider'; import { environment } from '../../environments/environment'; import { slideSidebarPadding } from '../shared/animations/slide'; -import { getPageInternalServerErrorRoute } from '../app-routing-paths'; @Component({ selector: 'ds-root', @@ -69,13 +68,9 @@ export class RootComponent implements OnInit { this.totalSidebarWidth = this.cssService.getVariable('totalSidebarWidth'); const sidebarCollapsed = this.menuService.isMenuCollapsed(MenuID.ADMIN); - this.slideSidebarOver = combineLatestObservable([sidebarCollapsed, this.windowService.isXsOrSm()]) + this.slideSidebarOver = combineLatestObservable(sidebarCollapsed, this.windowService.isXsOrSm()) .pipe( map(([collapsed, mobile]) => collapsed || mobile) ); - - if (this.router.url === getPageInternalServerErrorRoute()) { - this.shouldShowRouteLoader = false; - } } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index f82835401d..6b47238f5b 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -14,11 +14,6 @@ "403.forbidden": "forbidden", - "500.page-internal-server-error": "Service Unavailable", - - "500.help": "The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.", - - "500.link.home-page": "Take me to the 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. ", From c34f75b4430126e0c2f97f4fe2f335db95661843 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Mon, 24 Jan 2022 09:27:19 +0100 Subject: [PATCH 101/102] replace href="#" with href="javascript:void(0);" everywhere --- .../org-unit-input-suggestions.component.html | 2 +- .../person-input-suggestions.component.html | 2 +- src/app/footer/footer.component.html | 2 +- .../field-components/collections/collections.component.html | 2 +- .../expandable-navbar-section.component.html | 2 +- src/app/shared/auth-nav-menu/auth-nav-menu.component.html | 4 ++-- src/app/shared/chips/chips.component.html | 2 +- .../filter-input-suggestions.component.html | 2 +- .../shared/input-suggestions/input-suggestions.component.html | 4 ++-- .../validation-suggestions.component.html | 2 +- src/app/shared/lang-switch/lang-switch.component.html | 2 +- .../sections/container/section-container.component.html | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component.html b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component.html index 87a422e7db..c4e31d3d81 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component.html +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component.html @@ -15,7 +15,7 @@