From fc6678515a62c4d1d1b05eeb0dd85c1f633489f1 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Thu, 4 Nov 2021 14:21:12 +0100 Subject: [PATCH 001/151] [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/151] [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/151] [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/151] [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/151] [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/151] [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/151] [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/151] [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 df957fc31b04be2b8c8590100382c9e1e2cf1dc6 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 19 Nov 2021 16:22:52 +0100 Subject: [PATCH 009/151] [CST-4878] Remove start and end date when embargo policy is changed to 'open access' --- .../file/section-upload-file.component.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index ac6c0d70c4..64d229e3a7 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -262,17 +262,24 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { .forEach((element) => accessConditionOpt = element); if (accessConditionOpt) { - accessConditionOpt = Object.assign({}, accessCondition); - accessConditionOpt.name = this.retrieveValueFromField(accessCondition.name); - if (accessCondition.startDate) { - const startDate = this.retrieveValueFromField(accessCondition.startDate); - accessConditionOpt.startDate = dateToISOFormat(startDate); - } - if (accessCondition.endDate) { - const endDate = this.retrieveValueFromField(accessCondition.endDate); - accessConditionOpt.endDate = dateToISOFormat(endDate); - } - accessConditionsToSave.push(accessConditionOpt); + const currentAccessCondition = Object.assign({}, accessCondition); + currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name); + + /* When start and end date fields are deactivated, their values may be still present in formData, + therefore it is necessary to delete them if they're not allowed by the current access condition option. */ + if (!accessConditionOpt.hasStartDate) { + delete currentAccessCondition.startDate; + } else if (accessCondition.startDate) { + const startDate = this.retrieveValueFromField(accessCondition.startDate); + currentAccessCondition.startDate = dateToISOFormat(startDate); + } + if (!accessConditionOpt.hasEndDate) { + delete currentAccessCondition.endDate; + } else if (accessCondition.endDate) { + const endDate = this.retrieveValueFromField(accessCondition.endDate); + currentAccessCondition.endDate = dateToISOFormat(endDate); + } + accessConditionsToSave.push(currentAccessCondition); } }); From 9363b0fb35f3b756f8ad6603e89c178108bf1d7f Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 19 Nov 2021 19:58:07 +0100 Subject: [PATCH 010/151] [CST-4947] File description on bitstream can now be deleted (test TBD) --- .../file/section-upload-file.component.ts | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index 64d229e3a7..9807aecda3 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -6,7 +6,7 @@ import { DynamicFormControlModel, } from '@ng-dynamic-forms/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { SectionUploadService } from '../section-upload.service'; -import { isNotEmpty, isNotNull, isNotUndefined } from '../../../../shared/empty.util'; +import { hasNoValue, isNotEmpty, isNotNull, isNotUndefined } from '../../../../shared/empty.util'; import { FormService } from '../../../../shared/form/form.service'; import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder'; import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner'; @@ -129,6 +129,12 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { */ protected subscriptions: Subscription[] = []; + /** + * Array containing all the form metadata defined in configMetadataForm + * @type {Array} + */ + protected formMetadata: string[] = []; + /** * The [[SubmissionSectionUploadFileEditComponent]] reference * @type {SubmissionSectionUploadFileEditComponent} @@ -158,6 +164,17 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { this.readMode = true; } + protected loadFormMetadata() { + this.configMetadataForm.rows.forEach((row) => { + row.fields.forEach((field) => { + field.selectableMetadata.forEach((metadatum) => { + this.formMetadata.push(metadatum.metadata); + }); + }); + } + ); + } + /** * Retrieve bitstream's metadata */ @@ -182,6 +199,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { ngOnInit() { this.formId = this.formService.getUniqueId(this.fileId); this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId, 'files', this.fileIndex); + this.loadFormMetadata(); } /** @@ -250,6 +268,15 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { const path = `metadata/${metadataKey}`; this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true); }); + Object.keys((this.fileData.metadata)) + .filter((key) => isNotEmpty(this.fileData.metadata[key])) + .filter((key) => hasNoValue(formData.metadata[key])) + .filter((key) => this.formMetadata.includes(key)) + .forEach((key) => { + const metadataKey = key.replace(/_/g, '.'); + const path = `metadata/${metadataKey}`; + this.operationsBuilder.remove(this.pathCombiner.getPath(path)); + }); const accessConditionsToSave = []; formData.accessConditions .map((accessConditions) => accessConditions.accessConditionGroup) From 25795772257a2ec05e39da2d7f7199481f08e138 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 19 Nov 2021 19:58:07 +0100 Subject: [PATCH 011/151] [CST-4947] Test WIP --- .../section-upload-file.component.spec.ts | 25 ++++++++++++---- .../file/section-upload-file.component.ts | 29 ++++++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts index 39aebf7413..d16f648e68 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from import { BrowserModule, By } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; -import { of as observableOf } from 'rxjs'; +import { of, of as observableOf } from 'rxjs'; import { TranslateModule } from '@ngx-translate/core'; import { FormService } from '../../../../shared/form/form.service'; @@ -36,8 +36,20 @@ import { FormFieldMetadataValueObject } from '../../../../shared/form/builder/mo import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { dateToISOFormat } from '../../../../shared/date.util'; +import { SubmissionFormModel } from '../../../../core/config/models/config-submission-form.model'; -describe('SubmissionSectionUploadFileComponent test suite', () => { +const configMetadataFormMock = { + rows: [{ + fields: [{ + selectableMetadata: [ + {metadata: 'dc.title', label: null, closed: false}, + {metadata: 'dc.description', label: null, closed: false} + ] + }] + }] +}; + +fdescribe('SubmissionSectionUploadFileComponent test suite', () => { let comp: SubmissionSectionUploadFileComponent; let compAsAny: any; @@ -117,6 +129,9 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { testFixture = createTestComponent(html, TestComponent) as ComponentFixture; testComp = testFixture.componentInstance; + + // testComp.configMetadataForm = configMetadataFormMock; + // testFixture.detectChanges(); }); afterEach(() => { @@ -124,9 +139,8 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { }); it('should create SubmissionSectionUploadFileComponent', inject([SubmissionSectionUploadFileComponent], (app: SubmissionSectionUploadFileComponent) => { - + app.configMetadataForm = Object.assign(new SubmissionFormModel(), configMetadataFormMock); expect(app).toBeDefined(); - })); }); @@ -135,6 +149,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { fixture = TestBed.createComponent(SubmissionSectionUploadFileComponent); comp = fixture.componentInstance; compAsAny = comp; + compAsAny.configMetadataForm = configMetadataFormMock; submissionServiceStub = TestBed.inject(SubmissionService as any); uploadService = TestBed.inject(SectionUploadService); formService = TestBed.inject(FormService); @@ -314,7 +329,7 @@ class TestComponent { availableAccessConditionOptions; collectionId = mockSubmissionCollectionId; collectionPolicyType; - configMetadataForm$; + configMetadataForm$ = of(configMetadataFormMock); fileIndexes = []; fileList = []; fileNames = []; diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index 64d229e3a7..9807aecda3 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -6,7 +6,7 @@ import { DynamicFormControlModel, } from '@ng-dynamic-forms/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { SectionUploadService } from '../section-upload.service'; -import { isNotEmpty, isNotNull, isNotUndefined } from '../../../../shared/empty.util'; +import { hasNoValue, isNotEmpty, isNotNull, isNotUndefined } from '../../../../shared/empty.util'; import { FormService } from '../../../../shared/form/form.service'; import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder'; import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner'; @@ -129,6 +129,12 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { */ protected subscriptions: Subscription[] = []; + /** + * Array containing all the form metadata defined in configMetadataForm + * @type {Array} + */ + protected formMetadata: string[] = []; + /** * The [[SubmissionSectionUploadFileEditComponent]] reference * @type {SubmissionSectionUploadFileEditComponent} @@ -158,6 +164,17 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { this.readMode = true; } + protected loadFormMetadata() { + this.configMetadataForm.rows.forEach((row) => { + row.fields.forEach((field) => { + field.selectableMetadata.forEach((metadatum) => { + this.formMetadata.push(metadatum.metadata); + }); + }); + } + ); + } + /** * Retrieve bitstream's metadata */ @@ -182,6 +199,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { ngOnInit() { this.formId = this.formService.getUniqueId(this.fileId); this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId, 'files', this.fileIndex); + this.loadFormMetadata(); } /** @@ -250,6 +268,15 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { const path = `metadata/${metadataKey}`; this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true); }); + Object.keys((this.fileData.metadata)) + .filter((key) => isNotEmpty(this.fileData.metadata[key])) + .filter((key) => hasNoValue(formData.metadata[key])) + .filter((key) => this.formMetadata.includes(key)) + .forEach((key) => { + const metadataKey = key.replace(/_/g, '.'); + const path = `metadata/${metadataKey}`; + this.operationsBuilder.remove(this.pathCombiner.getPath(path)); + }); const accessConditionsToSave = []; formData.accessConditions .map((accessConditions) => accessConditions.accessConditionGroup) From 5df2f6f8d5c4b4f697bb3bbd0bbe55279d222704 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 22 Nov 2021 11:15:11 +0100 Subject: [PATCH 012/151] [CST-4947] File description on bitstream can now be deleted --- .../upload/file/section-upload-file.component.spec.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts index d16f648e68..cf02dadea7 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts @@ -36,7 +36,6 @@ import { FormFieldMetadataValueObject } from '../../../../shared/form/builder/mo import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { dateToISOFormat } from '../../../../shared/date.util'; -import { SubmissionFormModel } from '../../../../core/config/models/config-submission-form.model'; const configMetadataFormMock = { rows: [{ @@ -49,7 +48,7 @@ const configMetadataFormMock = { }] }; -fdescribe('SubmissionSectionUploadFileComponent test suite', () => { +describe('SubmissionSectionUploadFileComponent test suite', () => { let comp: SubmissionSectionUploadFileComponent; let compAsAny: any; @@ -130,8 +129,6 @@ fdescribe('SubmissionSectionUploadFileComponent test suite', () => { testFixture = createTestComponent(html, TestComponent) as ComponentFixture; testComp = testFixture.componentInstance; - // testComp.configMetadataForm = configMetadataFormMock; - // testFixture.detectChanges(); }); afterEach(() => { @@ -139,7 +136,6 @@ fdescribe('SubmissionSectionUploadFileComponent test suite', () => { }); it('should create SubmissionSectionUploadFileComponent', inject([SubmissionSectionUploadFileComponent], (app: SubmissionSectionUploadFileComponent) => { - app.configMetadataForm = Object.assign(new SubmissionFormModel(), configMetadataFormMock); expect(app).toBeDefined(); })); }); @@ -228,6 +224,7 @@ fdescribe('SubmissionSectionUploadFileComponent test suite', () => { it('should save Bitstream File data properly when form is valid', fakeAsync(() => { compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); compAsAny.fileEditComp.formRef = {formGroup: null}; + compAsAny.fileData = fileData; compAsAny.pathCombiner = pathCombiner; const event = new Event('click', null); spyOn(comp, 'switchMode'); From 624f39df1e4cadba6358b1bc4e094627bf27f2e8 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 22 Nov 2021 18:14:45 +0100 Subject: [PATCH 013/151] [CST-4884] Bitstream edit form moved inside modal (test TBD) --- .../section-upload-file-edit.component.html | 26 +- .../section-upload-file-edit.component.ts | 355 ++++++++++++------ .../file/section-upload-file.component.html | 37 +- .../file/section-upload-file.component.ts | 231 ++++-------- 4 files changed, 355 insertions(+), 294 deletions(-) diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.html b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.html index f6e0646974..2baa6c1555 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.html +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.html @@ -1,9 +1,21 @@
- + +
diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index 96725f151e..78bcca876f 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Input, OnChanges, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core'; import { FormControl } from '@angular/forms'; import { @@ -32,13 +32,23 @@ import { BITSTREAM_METADATA_FORM_GROUP_LAYOUT } from './section-upload-file-edit.model'; import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component'; -import { isNotEmpty } from '../../../../../shared/empty.util'; +import { hasNoValue, hasValue, isNotEmpty, isNotNull } from '../../../../../shared/empty.util'; import { SubmissionFormsModel } from '../../../../../core/config/models/config-submission-forms.model'; import { FormFieldModel } from '../../../../../shared/form/builder/models/form-field.model'; import { AccessConditionOption } from '../../../../../core/config/models/config-access-condition-option.model'; import { SubmissionService } from '../../../../submission.service'; import { FormService } from '../../../../../shared/form/form.service'; import { FormComponent } from '../../../../../shared/form/form.component'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { filter, mergeMap, take } from 'rxjs/operators'; +import { dateToISOFormat } from '../../../../../shared/date.util'; +import { SubmissionObject } from '../../../../../core/submission/models/submission-object.model'; +import { WorkspaceitemSectionUploadObject } from '../../../../../core/submission/models/workspaceitem-section-upload.model'; +import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder'; +import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service'; +import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; +import { SectionUploadService } from '../../section-upload.service'; +import { Subscription } from 'rxjs'; /** * This component represents the edit form for bitstream @@ -48,7 +58,31 @@ import { FormComponent } from '../../../../../shared/form/form.component'; styleUrls: ['./section-upload-file-edit.component.scss'], templateUrl: './section-upload-file-edit.component.html', }) -export class SubmissionSectionUploadFileEditComponent implements OnChanges { +export class SubmissionSectionUploadFileEditComponent implements OnInit { + + /** + * Initialize instance variables + * + * @param activeModal + * @param {ChangeDetectorRef} cdr + * @param {FormBuilderService} formBuilderService + * @param {FormService} formService + * @param {SubmissionService} submissionService + * @param {JsonPatchOperationsBuilder} operationsBuilder + * @param {SubmissionJsonPatchOperationsService} operationsService + * @param {SectionUploadService} uploadService + */ + constructor( + protected activeModal: NgbActiveModal, + private cdr: ChangeDetectorRef, + private formBuilderService: FormBuilderService, + private formService: FormService, + private submissionService: SubmissionService, + private operationsBuilder: JsonPatchOperationsBuilder, + private operationsService: SubmissionJsonPatchOperationsService, + private uploadService: SectionUploadService, + ) { + } /** * The list of available access condition @@ -113,10 +147,15 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges { @Input() submissionId: string; /** - * The form model - * @type {DynamicFormControlModel[]} + * The list of all available metadata */ - public formModel: DynamicFormControlModel[]; + @Input() formMetadata: string[] = []; + + /** + * The [JsonPatchOperationPathCombiner] object + * @type {JsonPatchOperationPathCombiner} + */ + @Input() pathCombiner: JsonPatchOperationPathCombiner; /** * The FormComponent reference @@ -124,108 +163,18 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges { @ViewChild('formRef') public formRef: FormComponent; /** - * Initialize instance variables - * - * @param {ChangeDetectorRef} cdr - * @param {FormBuilderService} formBuilderService - * @param {FormService} formService - * @param {SubmissionService} submissionService + * The form model + * @type {DynamicFormControlModel[]} */ - constructor(private cdr: ChangeDetectorRef, - private formBuilderService: FormBuilderService, - private formService: FormService, - private submissionService: SubmissionService) { - } + formModel: DynamicFormControlModel[]; - /** - * Dispatch form model init - */ - ngOnChanges() { - if (this.fileData && this.formId) { - this.formModel = this.buildFileEditForm(); - this.cdr.detectChanges(); - } - } + isSaving = false; - /** - * Initialize form model - */ - protected buildFileEditForm() { - const configDescr: FormFieldModel = Object.assign({}, this.configMetadataForm.rows[0].fields[0]); - configDescr.repeatable = false; - const configForm = Object.assign({}, this.configMetadataForm, { - fields: Object.assign([], this.configMetadataForm.rows[0].fields[0], [ - this.configMetadataForm.rows[0].fields[0], - configDescr - ]) - }); - const formModel: DynamicFormControlModel[] = []; - const metadataGroupModelConfig = Object.assign({}, BITSTREAM_METADATA_FORM_GROUP_CONFIG); - metadataGroupModelConfig.group = this.formBuilderService.modelFromConfiguration( - this.submissionId, - configForm, - this.collectionId, - this.fileData.metadata, - this.submissionService.getSubmissionScope() - ); - formModel.push(new DynamicFormGroupModel(metadataGroupModelConfig, BITSTREAM_METADATA_FORM_GROUP_LAYOUT)); - const accessConditionTypeModelConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_TYPE_CONFIG); - const accessConditionsArrayConfig = Object.assign({}, BITSTREAM_ACCESS_CONDITIONS_FORM_ARRAY_CONFIG); - const accessConditionTypeOptions = []; + protected subscriptions: Subscription[] = []; - if (this.collectionPolicyType === POLICY_DEFAULT_WITH_LIST) { - for (const accessCondition of this.availableAccessConditionOptions) { - accessConditionTypeOptions.push( - { - label: accessCondition.name, - value: accessCondition.name - } - ); - } - accessConditionTypeModelConfig.options = accessConditionTypeOptions; - - // Dynamically assign of relation in config. For startdate, endDate, groups. - const hasStart = []; - const hasEnd = []; - const hasGroups = []; - this.availableAccessConditionOptions.forEach((condition) => { - const showStart: boolean = condition.hasStartDate === true; - const showEnd: boolean = condition.hasEndDate === true; - const showGroups: boolean = showStart || showEnd; - if (showStart) { - hasStart.push({ id: 'name', value: condition.name }); - } - if (showEnd) { - hasEnd.push({ id: 'name', value: condition.name }); - } - if (showGroups) { - hasGroups.push({ id: 'name', value: condition.name }); - } - }); - const confStart = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart }] }; - const confEnd = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd }] }; - - accessConditionsArrayConfig.groupFactory = () => { - const type = new DynamicSelectModel(accessConditionTypeModelConfig, BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT); - const startDateConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_CONFIG, confStart); - const endDateConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_CONFIG, confEnd); - - const startDate = new DynamicDatePickerModel(startDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT); - const endDate = new DynamicDatePickerModel(endDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_LAYOUT); - const accessConditionGroupConfig = Object.assign({}, BITSTREAM_ACCESS_CONDITION_GROUP_CONFIG); - accessConditionGroupConfig.group = [type, startDate, endDate]; - return [new DynamicFormGroupModel(accessConditionGroupConfig, BITSTREAM_ACCESS_CONDITION_GROUP_LAYOUT)]; - }; - - // Number of access conditions blocks in form - accessConditionsArrayConfig.initialCount = isNotEmpty(this.fileData.accessConditions) ? this.fileData.accessConditions.length : 1; - formModel.push( - new DynamicFormArrayModel(accessConditionsArrayConfig, BITSTREAM_ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT) - ); - - } - this.initModelData(formModel); - return formModel; + private static retrieveValueFromField(field: any) { + const temp = Array.isArray(field) ? field[0] : field; + return (temp) ? temp.value : undefined; } /** @@ -262,12 +211,21 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges { * @param event * The event emitted */ - public onChange(event: DynamicFormControlEvent) { + onChange(event: DynamicFormControlEvent) { if (event.model.id === 'name') { this.setOptions(event.model, event.control); } } + onModalClose() { + this.activeModal.dismiss(); + } + + onSubmit() { + this.isSaving = true; + this.saveBitstreamData(); + } + /** * Update `startDate`, 'groupUUID' and 'endDate' model * @@ -323,4 +281,191 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges { } } + /** + * Dispatch form model init + */ + ngOnInit() { + if (this.fileData && this.formId) { + this.formModel = this.buildFileEditForm(); + this.cdr.detectChanges(); + } + } + + ngOnDestroy(): void { + this.unsubscribeAll(); + } + + /** + * Initialize form model + */ + protected buildFileEditForm() { + const configDescr: FormFieldModel = Object.assign({}, this.configMetadataForm.rows[0].fields[0]); + configDescr.repeatable = false; + const configForm = Object.assign({}, this.configMetadataForm, { + fields: Object.assign([], this.configMetadataForm.rows[0].fields[0], [ + this.configMetadataForm.rows[0].fields[0], + configDescr + ]) + }); + const formModel: DynamicFormControlModel[] = []; + const metadataGroupModelConfig = Object.assign({}, BITSTREAM_METADATA_FORM_GROUP_CONFIG); + metadataGroupModelConfig.group = this.formBuilderService.modelFromConfiguration( + this.submissionId, + configForm, + this.collectionId, + this.fileData.metadata, + this.submissionService.getSubmissionScope() + ); + formModel.push(new DynamicFormGroupModel(metadataGroupModelConfig, BITSTREAM_METADATA_FORM_GROUP_LAYOUT)); + const accessConditionTypeModelConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_TYPE_CONFIG); + const accessConditionsArrayConfig = Object.assign({}, BITSTREAM_ACCESS_CONDITIONS_FORM_ARRAY_CONFIG); + const accessConditionTypeOptions = []; + + if (this.collectionPolicyType === POLICY_DEFAULT_WITH_LIST) { + for (const accessCondition of this.availableAccessConditionOptions) { + accessConditionTypeOptions.push( + { + label: accessCondition.name, + value: accessCondition.name + } + ); + } + accessConditionTypeModelConfig.options = accessConditionTypeOptions; + + // Dynamically assign of relation in config. For startdate, endDate, groups. + const hasStart = []; + const hasEnd = []; + const hasGroups = []; + this.availableAccessConditionOptions.forEach((condition) => { + const showStart: boolean = condition.hasStartDate === true; + const showEnd: boolean = condition.hasEndDate === true; + const showGroups: boolean = showStart || showEnd; + if (showStart) { + hasStart.push({id: 'name', value: condition.name}); + } + if (showEnd) { + hasEnd.push({id: 'name', value: condition.name}); + } + if (showGroups) { + hasGroups.push({id: 'name', value: condition.name}); + } + }); + const confStart = {relations: [{match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart}]}; + const confEnd = {relations: [{match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd}]}; + + accessConditionsArrayConfig.groupFactory = () => { + const type = new DynamicSelectModel(accessConditionTypeModelConfig, BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT); + const startDateConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_CONFIG, confStart); + const endDateConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_CONFIG, confEnd); + + const startDate = new DynamicDatePickerModel(startDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT); + const endDate = new DynamicDatePickerModel(endDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_LAYOUT); + const accessConditionGroupConfig = Object.assign({}, BITSTREAM_ACCESS_CONDITION_GROUP_CONFIG); + accessConditionGroupConfig.group = [type, startDate, endDate]; + return [new DynamicFormGroupModel(accessConditionGroupConfig, BITSTREAM_ACCESS_CONDITION_GROUP_LAYOUT)]; + }; + + // Number of access conditions blocks in form + accessConditionsArrayConfig.initialCount = isNotEmpty(this.fileData.accessConditions) ? this.fileData.accessConditions.length : 1; + formModel.push( + new DynamicFormArrayModel(accessConditionsArrayConfig, BITSTREAM_ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT) + ); + + } + this.initModelData(formModel); + return formModel; + } + + /** + * Save bitstream metadata + */ + protected saveBitstreamData() { + // validate form + this.formService.validateAllFormFields(this.formRef.formGroup); + const saveBitstreamDataSubscription = this.formService.isValid(this.formId).pipe( + take(1), + filter((isValid) => isValid), + mergeMap(() => this.formService.getFormData(this.formId)), + take(1), + mergeMap((formData: any) => { + // collect bitstream metadata + Object.keys((formData.metadata)) + .filter((key) => isNotEmpty(formData.metadata[key])) + .forEach((key) => { + const metadataKey = key.replace(/_/g, '.'); + const path = `metadata/${metadataKey}`; + this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true); + }); + Object.keys((this.fileData.metadata)) + .filter((key) => isNotEmpty(this.fileData.metadata[key])) + .filter((key) => hasNoValue(formData.metadata[key])) + .filter((key) => this.formMetadata.includes(key)) + .forEach((key) => { + const metadataKey = key.replace(/_/g, '.'); + const path = `metadata/${metadataKey}`; + this.operationsBuilder.remove(this.pathCombiner.getPath(path)); + }); + const accessConditionsToSave = []; + formData.accessConditions + .map((accessConditions) => accessConditions.accessConditionGroup) + .filter((accessCondition) => isNotEmpty(accessCondition)) + .forEach((accessCondition) => { + let accessConditionOpt; + + this.availableAccessConditionOptions + .filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value) + .forEach((element) => accessConditionOpt = element); + + if (accessConditionOpt) { + const currentAccessCondition = Object.assign({}, accessCondition); + currentAccessCondition.name = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.name); + + /* When start and end date fields are deactivated, their values may be still present in formData, + therefore it is necessary to delete them if they're not allowed by the current access condition option. */ + if (!accessConditionOpt.hasStartDate) { + delete currentAccessCondition.startDate; + } else if (accessCondition.startDate) { + const startDate = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.startDate); + currentAccessCondition.startDate = dateToISOFormat(startDate); + } + if (!accessConditionOpt.hasEndDate) { + delete currentAccessCondition.endDate; + } else if (accessCondition.endDate) { + const endDate = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.endDate); + currentAccessCondition.endDate = dateToISOFormat(endDate); + } + accessConditionsToSave.push(currentAccessCondition); + } + }); + + if (isNotEmpty(accessConditionsToSave)) { + this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true); + } + + // dispatch a PATCH request to save metadata + return this.operationsService.jsonPatchByResourceID( + this.submissionService.getSubmissionObjectLinkName(), + this.submissionId, + this.pathCombiner.rootElement, + this.pathCombiner.subRootElement); + }) + ).subscribe((result: SubmissionObject[]) => { + if (result[0].sections[this.sectionId]) { + const uploadSection = (result[0].sections[this.sectionId] as WorkspaceitemSectionUploadObject); + Object.keys(uploadSection.files) + .filter((key) => uploadSection.files[key].uuid === this.fileId) + .forEach((key) => this.uploadService.updateFileData( + this.submissionId, this.sectionId, this.fileId, uploadSection.files[key]) + ); + } + this.isSaving = false; + this.activeModal.close(); + }); + this.subscriptions.push(saveBitstreamDataSubscription); + } + + private unsubscribeAll() { + this.subscriptions.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); + } + } diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.html b/src/app/submission/sections/upload/file/section-upload-file.component.html index 259418c22c..8749c8dcf2 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.html +++ b/src/app/submission/sections/upload/file/section-upload-file.component.html @@ -9,14 +9,14 @@

{{fileName}} ({{fileData?.sizeBytes | dsFileSize}})

- + - - - - -
- - + diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index 9807aecda3..e152d2f651 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -1,25 +1,23 @@ import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core'; import { BehaviorSubject, Subscription } from 'rxjs'; -import { filter, mergeMap, take } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; import { DynamicFormControlModel, } from '@ng-dynamic-forms/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { SectionUploadService } from '../section-upload.service'; -import { hasNoValue, isNotEmpty, isNotNull, isNotUndefined } from '../../../../shared/empty.util'; +import { hasValue, isNotUndefined } from '../../../../shared/empty.util'; import { FormService } from '../../../../shared/form/form.service'; import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder'; import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { WorkspaceitemSectionUploadFileObject } from '../../../../core/submission/models/workspaceitem-section-upload-file.model'; import { SubmissionFormsModel } from '../../../../core/config/models/config-submission-forms.model'; -import { dateToISOFormat } from '../../../../shared/date.util'; import { SubmissionService } from '../../../submission.service'; import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; import { SubmissionJsonPatchOperationsService } from '../../../../core/submission/submission-json-patch-operations.service'; -import { SubmissionObject } from '../../../../core/submission/models/submission-object.model'; -import { WorkspaceitemSectionUploadObject } from '../../../../core/submission/models/workspaceitem-section-upload.model'; import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component'; import { Bitstream } from '../../../../core/shared/bitstream.model'; +import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap/modal/modal-config'; /** * This component represents a single bitstream contained in the submission @@ -87,6 +85,13 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { */ @Input() submissionId: string; + /** + * The [[SubmissionSectionUploadFileEditComponent]] reference + * @type {SubmissionSectionUploadFileEditComponent} + */ + @ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent; + + /** * The bitstream's metadata data * @type {WorkspaceitemSectionUploadFileObject} @@ -135,12 +140,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { */ protected formMetadata: string[] = []; - /** - * The [[SubmissionSectionUploadFileEditComponent]] reference - * @type {SubmissionSectionUploadFileEditComponent} - */ - @ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent; - /** * Initialize instance variables * @@ -153,28 +152,19 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { * @param {SubmissionService} submissionService * @param {SectionUploadService} uploadService */ - constructor(private cdr: ChangeDetectorRef, - private formService: FormService, - private halService: HALEndpointService, - private modalService: NgbModal, - private operationsBuilder: JsonPatchOperationsBuilder, - private operationsService: SubmissionJsonPatchOperationsService, - private submissionService: SubmissionService, - private uploadService: SectionUploadService) { + constructor( + private cdr: ChangeDetectorRef, + private formService: FormService, + private halService: HALEndpointService, + private modalService: NgbModal, + private operationsBuilder: JsonPatchOperationsBuilder, + private operationsService: SubmissionJsonPatchOperationsService, + private submissionService: SubmissionService, + private uploadService: SectionUploadService, + ) { this.readMode = true; } - protected loadFormMetadata() { - this.configMetadataForm.rows.forEach((row) => { - row.fields.forEach((field) => { - field.selectableMetadata.forEach((metadatum) => { - this.formMetadata.push(metadatum.metadata); - }); - }); - } - ); - } - /** * Retrieve bitstream's metadata */ @@ -202,22 +192,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { this.loadFormMetadata(); } - /** - * Delete bitstream from submission - */ - protected deleteFile() { - this.operationsBuilder.remove(this.pathCombiner.getPath()); - this.subscriptions.push(this.operationsService.jsonPatchByResourceID( - this.submissionService.getSubmissionObjectLinkName(), - this.submissionId, - this.pathCombiner.rootElement, - this.pathCombiner.subRootElement) - .subscribe(() => { - this.uploadService.removeUploadedFile(this.submissionId, this.sectionId, this.fileId); - this.processingDelete$.next(false); - })); - } - /** * Show confirmation dialog for delete */ @@ -243,108 +217,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { }); } - /** - * Save bitstream metadata - * - * @param event - * the click event emitted - */ - public saveBitstreamData(event) { - event.preventDefault(); - - // validate form - this.formService.validateAllFormFields(this.fileEditComp.formRef.formGroup); - this.subscriptions.push(this.formService.isValid(this.formId).pipe( - take(1), - filter((isValid) => isValid), - mergeMap(() => this.formService.getFormData(this.formId)), - take(1), - mergeMap((formData: any) => { - // collect bitstream metadata - Object.keys((formData.metadata)) - .filter((key) => isNotEmpty(formData.metadata[key])) - .forEach((key) => { - const metadataKey = key.replace(/_/g, '.'); - const path = `metadata/${metadataKey}`; - this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true); - }); - Object.keys((this.fileData.metadata)) - .filter((key) => isNotEmpty(this.fileData.metadata[key])) - .filter((key) => hasNoValue(formData.metadata[key])) - .filter((key) => this.formMetadata.includes(key)) - .forEach((key) => { - const metadataKey = key.replace(/_/g, '.'); - const path = `metadata/${metadataKey}`; - this.operationsBuilder.remove(this.pathCombiner.getPath(path)); - }); - const accessConditionsToSave = []; - formData.accessConditions - .map((accessConditions) => accessConditions.accessConditionGroup) - .filter((accessCondition) => isNotEmpty(accessCondition)) - .forEach((accessCondition) => { - let accessConditionOpt; - - this.availableAccessConditionOptions - .filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value) - .forEach((element) => accessConditionOpt = element); - - if (accessConditionOpt) { - const currentAccessCondition = Object.assign({}, accessCondition); - currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name); - - /* When start and end date fields are deactivated, their values may be still present in formData, - therefore it is necessary to delete them if they're not allowed by the current access condition option. */ - if (!accessConditionOpt.hasStartDate) { - delete currentAccessCondition.startDate; - } else if (accessCondition.startDate) { - const startDate = this.retrieveValueFromField(accessCondition.startDate); - currentAccessCondition.startDate = dateToISOFormat(startDate); - } - if (!accessConditionOpt.hasEndDate) { - delete currentAccessCondition.endDate; - } else if (accessCondition.endDate) { - const endDate = this.retrieveValueFromField(accessCondition.endDate); - currentAccessCondition.endDate = dateToISOFormat(endDate); - } - accessConditionsToSave.push(currentAccessCondition); - } - }); - - if (isNotEmpty(accessConditionsToSave)) { - this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true); - } - - // dispatch a PATCH request to save metadata - return this.operationsService.jsonPatchByResourceID( - this.submissionService.getSubmissionObjectLinkName(), - this.submissionId, - this.pathCombiner.rootElement, - this.pathCombiner.subRootElement); - }) - ).subscribe((result: SubmissionObject[]) => { - if (result[0].sections[this.sectionId]) { - const uploadSection = (result[0].sections[this.sectionId] as WorkspaceitemSectionUploadObject); - Object.keys(uploadSection.files) - .filter((key) => uploadSection.files[key].uuid === this.fileId) - .forEach((key) => this.uploadService.updateFileData( - this.submissionId, this.sectionId, this.fileId, uploadSection.files[key]) - ); - } - this.switchMode(); - })); - } - - /** - * Retrieve field value - * - * @param field - * the specified field object - */ - private retrieveValueFromField(field: any) { - const temp = Array.isArray(field) ? field[0] : field; - return (temp) ? temp.value : undefined; - } - /** * Switch from edit form to metadata view */ @@ -353,4 +225,67 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { this.cdr.detectChanges(); } + editBitstreamData() { + + const options: NgbModalOptions = { + size: 'xl', + backdrop: 'static', + }; + + const activeModal = this.modalService.open(SubmissionSectionUploadFileEditComponent, options); + + activeModal.componentInstance.availableAccessConditionOptions = this.availableAccessConditionOptions; + activeModal.componentInstance.collectionId = this.collectionId; + activeModal.componentInstance.collectionPolicyType = this.collectionPolicyType; + activeModal.componentInstance.configMetadataForm = this.configMetadataForm; + activeModal.componentInstance.fileData = this.fileData; + activeModal.componentInstance.fileId = this.fileId; + activeModal.componentInstance.fileIndex = this.fileIndex; + activeModal.componentInstance.formId = this.formId; + activeModal.componentInstance.sectionId = this.sectionId; + activeModal.componentInstance.formMetadata = this.formMetadata; + activeModal.componentInstance.pathCombiner = this.pathCombiner; + activeModal.componentInstance.submissionId = this.submissionId; + + /*activeModal.componentInstance.saveBitstreamDataEvent.subscribe((res) => { + console.log(JSON.stringify(res)); + });*/ + + } + + ngOnDestroy(): void { + this.unsubscribeAll(); + } + + unsubscribeAll() { + this.subscriptions.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); + } + + protected loadFormMetadata() { + this.configMetadataForm.rows.forEach((row) => { + row.fields.forEach((field) => { + field.selectableMetadata.forEach((metadatum) => { + this.formMetadata.push(metadatum.metadata); + }); + }); + } + ); + } + + /** + * Delete bitstream from submission + */ + protected deleteFile() { + this.operationsBuilder.remove(this.pathCombiner.getPath()); + this.subscriptions.push(this.operationsService.jsonPatchByResourceID( + this.submissionService.getSubmissionObjectLinkName(), + this.submissionId, + this.pathCombiner.rootElement, + this.pathCombiner.subRootElement) + .subscribe(() => { + this.uploadService.removeUploadedFile(this.submissionId, this.sectionId, this.fileId); + this.processingDelete$.next(false); + })); + } + } From 31442f36a3fbe9101de5b03a45f672722f48d48b Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 22 Nov 2021 19:11:58 +0100 Subject: [PATCH 014/151] [CST-4884] Bitstream edit form moved inside modal (test WIP) --- ...section-upload-file-edit.component.spec.ts | 43 +++++++++++++++++-- .../section-upload-file-edit.component.ts | 16 +++---- .../section-upload-file.component.spec.ts | 21 +++------ 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts index a644cf8270..9203930ef5 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts @@ -30,15 +30,31 @@ import { FormService } from '../../../../../shared/form/form.service'; import { getMockFormService } from '../../../../../shared/mocks/form-service.mock'; import { Group } from '../../../../../core/eperson/models/group.model'; import { createTestComponent } from '../../../../../shared/testing/utils.test'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder'; +import { SubmissionJsonPatchOperationsServiceStub } from '../../../../../shared/testing/submission-json-patch-operations-service.stub'; +import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service'; +import { SectionUploadService } from '../../section-upload.service'; +import { getMockSectionUploadService } from '../../../../../shared/mocks/section-upload.service.mock'; +import { FormFieldMetadataValueObject } from '../../../../../shared/form/builder/models/form-field-metadata-value.model'; -describe('SubmissionSectionUploadFileEditComponent test suite', () => { +const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { + add: jasmine.createSpy('add'), + replace: jasmine.createSpy('replace'), + remove: jasmine.createSpy('remove'), +}); + +fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { let comp: SubmissionSectionUploadFileEditComponent; let compAsAny: any; let fixture: ComponentFixture; let submissionServiceStub: SubmissionServiceStub; let formbuilderService: any; + let operationsBuilder: any; + let operationsService: any; + const submissionJsonPatchOperationsServiceStub = new SubmissionJsonPatchOperationsServiceStub(); const submissionId = mockSubmissionId; const sectionId = 'upload'; const collectionId = mockSubmissionCollectionId; @@ -66,9 +82,14 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { providers: [ { provide: FormService, useValue: getMockFormService() }, { provide: SubmissionService, useClass: SubmissionServiceStub }, + { provide: SubmissionJsonPatchOperationsService, useValue: submissionJsonPatchOperationsServiceStub }, + { provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder }, + { provide: SectionUploadService, useValue: getMockSectionUploadService() }, FormBuilderService, ChangeDetectorRef, - SubmissionSectionUploadFileEditComponent + SubmissionSectionUploadFileEditComponent, + NgbModal, + NgbActiveModal, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents().then(); @@ -114,6 +135,8 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { compAsAny = comp; submissionServiceStub = TestBed.inject(SubmissionService as any); formbuilderService = TestBed.inject(FormBuilderService); + operationsBuilder = TestBed.inject(JsonPatchOperationsBuilder); + operationsService = TestBed.inject(SubmissionJsonPatchOperationsService); comp.submissionId = submissionId; comp.collectionId = collectionId; @@ -135,7 +158,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { comp.fileData = fileData; comp.formId = 'testFileForm'; - comp.ngOnChanges(); + comp.ngOnInit(); expect(comp.formModel).toBeDefined(); expect(comp.formModel.length).toBe(2); @@ -165,7 +188,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { comp.fileData = fileData; comp.formId = 'testFileForm'; - comp.ngOnChanges(); + comp.ngOnInit(); const model: DynamicSelectModel = formbuilderService.findById('name', comp.formModel, 0); const formGroup = formbuilderService.createFormGroup(comp.formModel); @@ -186,6 +209,18 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { comp.setOptions(model, control); expect(formbuilderService.findById).toHaveBeenCalledWith('startDate', (model.parent as DynamicFormArrayGroupModel).group); }); + + it('should retrieve Value From Field properly', () => { + let field; + expect(compAsAny.retrieveValueFromField(field)).toBeUndefined(); + + field = new FormFieldMetadataValueObject('test'); + expect(compAsAny.retrieveValueFromField(field)).toBe('test'); + + field = [new FormFieldMetadataValueObject('test')]; + expect(compAsAny.retrieveValueFromField(field)).toBe('test'); + }); + }); }); diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index 78bcca876f..eb8fbf95d0 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -172,11 +172,6 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { protected subscriptions: Subscription[] = []; - private static retrieveValueFromField(field: any) { - const temp = Array.isArray(field) ? field[0] : field; - return (temp) ? temp.value : undefined; - } - /** * Initialize form model values * @@ -295,6 +290,11 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { this.unsubscribeAll(); } + protected retrieveValueFromField(field: any) { + const temp = Array.isArray(field) ? field[0] : field; + return (temp) ? temp.value : undefined; + } + /** * Initialize form model */ @@ -418,20 +418,20 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { if (accessConditionOpt) { const currentAccessCondition = Object.assign({}, accessCondition); - currentAccessCondition.name = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.name); + currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name); /* When start and end date fields are deactivated, their values may be still present in formData, therefore it is necessary to delete them if they're not allowed by the current access condition option. */ if (!accessConditionOpt.hasStartDate) { delete currentAccessCondition.startDate; } else if (accessCondition.startDate) { - const startDate = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.startDate); + const startDate = this.retrieveValueFromField(accessCondition.startDate); currentAccessCondition.startDate = dateToISOFormat(startDate); } if (!accessConditionOpt.hasEndDate) { delete currentAccessCondition.endDate; } else if (accessCondition.endDate) { - const endDate = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.endDate); + const endDate = this.retrieveValueFromField(accessCondition.endDate); currentAccessCondition.endDate = dateToISOFormat(endDate); } accessConditionsToSave.push(currentAccessCondition); diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts index cf02dadea7..a8ab34d866 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts @@ -48,7 +48,7 @@ const configMetadataFormMock = { }] }; -describe('SubmissionSectionUploadFileComponent test suite', () => { +fdescribe('SubmissionSectionUploadFileComponent test suite', () => { let comp: SubmissionSectionUploadFileComponent; let compAsAny: any; @@ -221,7 +221,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { pathCombiner.subRootElement); }); - it('should save Bitstream File data properly when form is valid', fakeAsync(() => { + /*it('should save Bitstream File data properly when form is valid', fakeAsync(() => { compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); compAsAny.fileEditComp.formRef = {formGroup: null}; compAsAny.fileData = fileData; @@ -275,9 +275,9 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { expect(comp.switchMode).toHaveBeenCalled(); expect(uploadService.updateFileData).toHaveBeenCalledWith(submissionId, sectionId, mockUploadFiles[0].uuid, mockUploadFiles[0]); - })); + }));*/ - it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => { + /*it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => { compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); compAsAny.fileEditComp.formRef = {formGroup: null}; compAsAny.pathCombiner = pathCombiner; @@ -289,18 +289,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { expect(comp.switchMode).not.toHaveBeenCalled(); expect(uploadService.updateFileData).not.toHaveBeenCalled(); - })); - - it('should retrieve Value From Field properly', () => { - let field; - expect(compAsAny.retrieveValueFromField(field)).toBeUndefined(); - - field = new FormFieldMetadataValueObject('test'); - expect(compAsAny.retrieveValueFromField(field)).toBe('test'); - - field = [new FormFieldMetadataValueObject('test')]; - expect(compAsAny.retrieveValueFromField(field)).toBe('test'); - }); + }));*/ it('should switch read mode', () => { comp.readMode = false; From ffff337abafdca88a594ab8d910af4576f4726df Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Tue, 23 Nov 2021 16:43:53 +0100 Subject: [PATCH 015/151] 85123: Remove favicon --- src/assets/images/favicon.ico | Bin 3262 -> 0 bytes src/index.csr.html | 1 - src/index.html | 1 - 3 files changed, 2 deletions(-) delete mode 100644 src/assets/images/favicon.ico diff --git a/src/assets/images/favicon.ico b/src/assets/images/favicon.ico deleted file mode 100644 index c73ad96b2699f44ca88a0e333bc6567c639a8610..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3262 zcmeH}p>GsH6vnrK1k;2nf+80XAmKkiRGc_bfk0G1Qc+QHBo!wr7PycV6)P%6RE(%t zQL&<8Ma7a-jA+6L#E6O^sTff)qGAQf@|&C9wOP#NXaLda?sVtvo9}(|y?HyABuo7D zdI|mGq*zXpM@f>r;Kr1^<_`AIQr>mj&!)M%{-FYeRePpBw5pjV;=9&|MNyTd_a0&w z&MBp442>~yi2rd_#aZXpmPK(?mN?_dM5af}K;;Kk?U-^~7ZC*Tle!i{?BzKY!j02( z7X8eat<6n2d5awf=iJU{R8>_WB_XijvppOF0+S6TBkY@6ocRzPe?shL+01z~91=_9 zd0bJJrl93n27ml<+ZQ5&#NJvDc(+#P7flc(9gPm8)LKV0qji?0Ji|5u z)^$yET6BPCTp;409tiLU|5PbJ)|Eh+O0#3|S9K!SF5F;VxnbMgR(eC>HM*&iQ-gq}Dj8zVU-P$I@5%hL(I)76xbHP!|!uZ^Y50Iw-)^I`12aJ@iKI znQGw>*ZBGhpFMx-t@8HRX-YCg(Jaj_79uy*0DUHex29je8PsLdyj5agWTqEi0@<|Q zAr2`ZgfO) DSpace - diff --git a/src/index.html b/src/index.html index 491a2d319c..ddd448f289 100644 --- a/src/index.html +++ b/src/index.html @@ -6,7 +6,6 @@ DSpace - From a5a91d51399a5a197ae4f2d9bf8929bd40c04477 Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Tue, 23 Nov 2021 17:03:25 +0100 Subject: [PATCH 016/151] 85123: Add new favicon files --- .../favicons/android-chrome-192x192.png | Bin 0 -> 11844 bytes .../favicons/android-chrome-512x512.png | Bin 0 -> 33567 bytes .../images/favicons/apple-touch-icon.png | Bin 0 -> 2371 bytes .../dspace/assets/images/favicons/favicon.ico | Bin 0 -> 15086 bytes .../dspace/assets/images/favicons/favicon.svg | 7 +++++++ .../images/favicons/manifest.webmanifest | 19 ++++++++++++++++++ 6 files changed, 26 insertions(+) create mode 100644 src/themes/dspace/assets/images/favicons/android-chrome-192x192.png create mode 100644 src/themes/dspace/assets/images/favicons/android-chrome-512x512.png create mode 100644 src/themes/dspace/assets/images/favicons/apple-touch-icon.png create mode 100644 src/themes/dspace/assets/images/favicons/favicon.ico create mode 100644 src/themes/dspace/assets/images/favicons/favicon.svg create mode 100644 src/themes/dspace/assets/images/favicons/manifest.webmanifest diff --git a/src/themes/dspace/assets/images/favicons/android-chrome-192x192.png b/src/themes/dspace/assets/images/favicons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..1aaffb1b90e56ff30d06451586641e3ddaf0fbd6 GIT binary patch literal 11844 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 QUbzK;LY#+JS^3=i2VZyi4FCWD literal 0 HcmV?d00001 diff --git a/src/themes/dspace/assets/images/favicons/favicon.ico b/src/themes/dspace/assets/images/favicons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a5dfd5e3226ca4dd5990a674f5b053afbe1f4d38 GIT binary patch literal 15086 zcmdU$50F(=9mnso$TR=2s3;)Nt|>}tfn!RcvVi8K*jSoY4mn!XXpQ~BY&3QDG0vDx z777`El8nm6X-1m_6U&LUF)PQ)NFhTBBh4fgp=_0v*XMip{$7W(=iYbkdv9U)&3yKE z?(h80@Ao_B-h1x3=Ry>M)#10^{4EEalV8Cs2%Zb{W?NJ)+XMMq zPS2C)#n9@f`(1Huwj)G1AJq7djiv|51F@e1^IG$tvS(7)*l)LGyoF?xtUXjmF20i^^AM`<|?v z>Z!x$c=$<z*AsY}5L-kha6>$up}BK92$2A8B!=*E;62pzAoxE~`%S*{rNw z>aD}?XF$*2toV0Re-zvUMdtwX`#aiBsW;EGcKAF3o&k#!l255UHf?Ywd>phsHs(?O z9NYpAL)x{bbNyi`wDwKQJ;`Yw{M`$oyiK<~%ZFR1+(3O?1)(+HYv|X)^`O4Y@tDo= zXxC@0GWL2muv|m(&00SEdi;%q8zAd?({-ypy7X{-AB^*TrX6Fe^B&5(s!MB|rN?Ip zH1|~fjgS5FY>2$zAiljcpz|n8v?cFn9HDP~=Y@TAw^I6!RzN{a|Swxoge) z(X2V&t;4qI&o6u)0^fkwA#}a#-AQ$tS6#MqthM@KZjM=HY@3olep*0zp9jAHjiaBt z^3&Q*>(#TN6J{i+JPq2i^7ZSmUjlv{_qbPWu+clVa{D}d!9(lG74UmVTeCez-8rzY zpF_RJ_!SMes-6zAyz&0=NBC zjyqt#U_(cV%|)PjW@`XFV}1t5IZdiX^hd%Ipmm?y{soRVg4$jSdhZB+(?-7qYz|A4 zw^f5W1=<}AdG=Xl^EH^@G>?nu(qg*$RoKPf_1*U9^2Rq`kI-hfccYx~S2roX^3eG@ z(fY9$;wy)47^@s|#=jrC5tg$_bYnp0RnNWU4tG-4dN^%gMmdz_2WjUfOUhU3eQ5cZ z#8&t8!7vxJml5*)H2UM9)?R|zZiCaze-TN$li?}wbI@8u_ol7~zb^$xfK z)`8h;eYgfb2{U0joB|r-)I9_0M!@}GeoV&uj9LAljn+^Pcx^QHw^T3Uw^cHJFEq_ zzr7r9hgU#zv*aSE>;9wN{xHtL)`MR{r*XB|J(M+9>bdf6(6|V#uMY69dpoAC1Z%+j znADtn255fyi-Y!1)hGAar1|E{u)q1uD`_(ksqhS#FO!7I*}>$ew)?UHDmp4ejmhN zJXh?6_wb;q{*LtpN%!2;6n0#u zT*4N6wTautK4O3JDc_3Gu9*61pF-Ob9Z=n;h`)fKaDMdV)1W!{JBfV1RdakOd>!t9 z+hGCBfNK9KYn^KMpX5UjKeLcJkADZN*EOR(g~gEeo~Su7-cdoSy{<8})4XbaB!378 zc{=GeKOL$*Aoct`2QGwsQbJvuq4piC)*#r=SPaW*y9dtKe$T zILg2DifPwO_1TI}bL)DDa|rnVAayUp8=$e$v-DE4qdXEGg*a!^n?$d79?k1+pRRH0 z^R!=6PkY)7g9|{gO#8@Yc4$xbiW-9)%`BY<6r}*e~R&H zQnvo;{GDm>T!~)i?{cu`*B>ccJClDxXV=dGls^C$!N=fMcnM@HAM=y4wUvAf{OebG zUBj-Oo~xr^ho{%tCB9yeq5DT;x)AI+r+3z+und;K+16*ux_@ti<)Au^@pPy@^A|D; gme|27vDaIA7J8y+3-nyQJJP8xwvjDu7P5c(KTAbi>Hq)$ literal 0 HcmV?d00001 diff --git a/src/themes/dspace/assets/images/favicons/favicon.svg b/src/themes/dspace/assets/images/favicons/favicon.svg new file mode 100644 index 0000000000..8ea65cb72f --- /dev/null +++ b/src/themes/dspace/assets/images/favicons/favicon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/themes/dspace/assets/images/favicons/manifest.webmanifest b/src/themes/dspace/assets/images/favicons/manifest.webmanifest new file mode 100644 index 0000000000..1784ba2b4c --- /dev/null +++ b/src/themes/dspace/assets/images/favicons/manifest.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "DSpace", + "short_name": "DSpace", + "icons": [ + { + "src": "android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#091119", + "background_color": "#091119", + "display": "standalone" +} From 99a2cf926a02a0acdbdac1d60ecb2a4dc99dff7b Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Tue, 23 Nov 2021 19:36:49 +0100 Subject: [PATCH 017/151] 85123: WIP: Support for theme-specific head tags --- src/app/app.component.ts | 62 +++++++++++++++++++++++--- src/config/theme.model.ts | 22 +++++++++ src/environments/environment.common.ts | 34 +++++++++++++- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6f06a84144..f138753596 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -31,12 +31,12 @@ import { AuthService } from './core/auth/auth.service'; import { CSSVariableService } from './shared/sass-helper/sass-helper.service'; import { MenuService } from './shared/menu/menu.service'; import { HostWindowService } from './shared/host-window.service'; -import { ThemeConfig } from '../config/theme.model'; +import {HeadTagConfig, ThemeConfig} from '../config/theme.model'; import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider'; import { environment } from '../environments/environment'; import { models } from './core/core.module'; import { LocaleService } from './core/locale/locale.service'; -import { hasValue, isNotEmpty } from './shared/empty.util'; +import { hasValue, isEmpty, 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'; @@ -115,11 +115,11 @@ export class AppComponent implements OnInit, AfterViewInit { this.isThemeCSSLoading$.next(true); } if (hasValue(themeName)) { - this.setThemeCss(themeName); + this.loadGlobalThemeConfig(themeName); } else if (hasValue(DEFAULT_THEME_CONFIG)) { - this.setThemeCss(DEFAULT_THEME_CONFIG.name); + this.loadGlobalThemeConfig(DEFAULT_THEME_CONFIG.name); } else { - this.setThemeCss(BASE_THEME_NAME); + this.loadGlobalThemeConfig(BASE_THEME_NAME); } }); @@ -233,6 +233,11 @@ export class AppComponent implements OnInit, AfterViewInit { } } + private loadGlobalThemeConfig(themeName: string): void { + this.setThemeCss(themeName); + this.setHeadTags(themeName); + } + /** * Update the theme css file in * @@ -243,7 +248,7 @@ export class AppComponent implements OnInit, AfterViewInit { const head = this.document.getElementsByTagName('head')[0]; // Array.from to ensure we end up with an array, not an HTMLCollection, which would be // automatically updated if we add nodes later - const currentThemeLinks = Array.from(this.document.getElementsByClassName('theme-css')); + const currentThemeLinks = Array.from(head.getElementsByClassName('theme-css')); const link = this.document.createElement('link'); link.setAttribute('rel', 'stylesheet'); link.setAttribute('type', 'text/css'); @@ -265,6 +270,51 @@ export class AppComponent implements OnInit, AfterViewInit { head.appendChild(link); } + private setHeadTags(themeName: string): void { + const head = this.document.getElementsByTagName('head')[0]; + + // clear head tags + const currentHeadTags = Array.from(head.getElementsByClassName('theme-head-tag')); + if (isNotEmpty(currentHeadTags)) { + currentHeadTags.forEach((currentHeadTag: any) => currentHeadTag.remove()); + } + + // create new head tags (not yet added to DOM) + const headTagFragment = document.createDocumentFragment(); + this.createHeadTags(themeName) + .forEach(newHeadTag => headTagFragment.appendChild(newHeadTag)); + + // add new head tags to DOM + head.appendChild(headTagFragment); + } + + private createHeadTags(themeName: string): HTMLElement[] { + const themeConfig = this.themeService.getThemeConfigFor(themeName); + const headTagConfigs = themeConfig?.headTags; + + // if the current theme does not have head tags, we inherit the head tags of the parent + if (isEmpty(headTagConfigs)) { + const parentThemeName = themeConfig.extends; + return isNotEmpty(parentThemeName) ? this.createHeadTags(parentThemeName) : []; + } + + return headTagConfigs.map(this.createHeadTag.bind(this)); + } + + private createHeadTag(themeHeadTag: HeadTagConfig): HTMLElement { + const tag = this.document.createElement(themeHeadTag.tagName); + + if (isNotEmpty(themeHeadTag.attributes)) { + Object.entries(themeHeadTag.attributes) + .forEach(([key, value]) => tag.setAttribute(key, value)); + } + + // 'class' attribute should always be 'theme-head-tag' for removal + tag.setAttribute('class', 'theme-head-tag'); + + return tag; + } + private trackIdleModal() { const isIdle$ = this.authService.isUserIdle(); const isAuthenticated$ = this.authService.isAuthenticated(); diff --git a/src/config/theme.model.ts b/src/config/theme.model.ts index 0130b5ffd8..d65118d5d4 100644 --- a/src/config/theme.model.ts +++ b/src/config/theme.model.ts @@ -12,6 +12,28 @@ export interface NamedThemeConfig extends Config { * its ancestor theme(s) will be checked recursively before falling back to the default theme. */ extends?: string; + + /** + * A list of HTML tags that should be added to the HEAD section of the document, whenever this theme is active. + */ + headTags?: HeadTagConfig[]; +} + +/** + * Interface that represents a single theme-specific HTML tag in the HEAD section of the page. + */ +export interface HeadTagConfig extends Config { + /** + * The name of the HTML tag + */ + tagName: string; + + /** + * The attributes on the HTML tag + */ + attributes?: { + [key: string]: string; + }; } export interface RegExThemeConfig extends NamedThemeConfig { diff --git a/src/environments/environment.common.ts b/src/environments/environment.common.ts index b1cbd699a3..b8b7c31092 100644 --- a/src/environments/environment.common.ts +++ b/src/environments/environment.common.ts @@ -292,7 +292,39 @@ export const environment: GlobalConfig = { { // The default dspace theme - name: 'dspace' + 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"). From 893a306da066d18064fe9e9ae0b75d1454261d12 Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Wed, 24 Nov 2021 12:18:45 +0100 Subject: [PATCH 018/151] 85123: Fallback to favicon of default theme and src/assets/images/favicon.ico --- src/app/app.component.ts | 38 +++++++++++++++++++++++++++------- src/assets/images/favicon.ico | Bin 0 -> 15086 bytes 2 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 src/assets/images/favicon.ico diff --git a/src/app/app.component.ts b/src/app/app.component.ts index f138753596..9dc2b5a5ab 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -31,7 +31,7 @@ import { AuthService } from './core/auth/auth.service'; import { CSSVariableService } from './shared/sass-helper/sass-helper.service'; import { MenuService } from './shared/menu/menu.service'; import { HostWindowService } from './shared/host-window.service'; -import {HeadTagConfig, ThemeConfig} from '../config/theme.model'; +import { HeadTagConfig, ThemeConfig } from '../config/theme.model'; import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider'; import { environment } from '../environments/environment'; import { models } from './core/core.module'; @@ -292,20 +292,44 @@ export class AppComponent implements OnInit, AfterViewInit { const themeConfig = this.themeService.getThemeConfigFor(themeName); const headTagConfigs = themeConfig?.headTags; - // if the current theme does not have head tags, we inherit the head tags of the parent if (isEmpty(headTagConfigs)) { const parentThemeName = themeConfig.extends; - return isNotEmpty(parentThemeName) ? this.createHeadTags(parentThemeName) : []; + if (isNotEmpty(parentThemeName)) { + // inherit the head tags of the parent theme + return this.createHeadTags(parentThemeName); + } + + const defaultThemeName = DEFAULT_THEME_CONFIG.name; + if ( + isEmpty(defaultThemeName) || + themeName === defaultThemeName || + themeName === BASE_THEME_NAME + ) { + // last resort, use fallback favicon.ico + return [ + this.createHeadTag({ + 'tagName': 'link', + 'attributes': { + 'rel': 'icon', + 'href': 'assets/images/favicon.ico', + 'sizes': 'any', + } + }) + ]; + } + + // inherit the head tags of the default theme + return this.createHeadTags(DEFAULT_THEME_CONFIG.name); } return headTagConfigs.map(this.createHeadTag.bind(this)); } - private createHeadTag(themeHeadTag: HeadTagConfig): HTMLElement { - const tag = this.document.createElement(themeHeadTag.tagName); + private createHeadTag(headTagConfig: HeadTagConfig): HTMLElement { + const tag = this.document.createElement(headTagConfig.tagName); - if (isNotEmpty(themeHeadTag.attributes)) { - Object.entries(themeHeadTag.attributes) + if (isNotEmpty(headTagConfig.attributes)) { + Object.entries(headTagConfig.attributes) .forEach(([key, value]) => tag.setAttribute(key, value)); } diff --git a/src/assets/images/favicon.ico b/src/assets/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a5dfd5e3226ca4dd5990a674f5b053afbe1f4d38 GIT binary patch literal 15086 zcmdU$50F(=9mnso$TR=2s3;)Nt|>}tfn!RcvVi8K*jSoY4mn!XXpQ~BY&3QDG0vDx z777`El8nm6X-1m_6U&LUF)PQ)NFhTBBh4fgp=_0v*XMip{$7W(=iYbkdv9U)&3yKE z?(h80@Ao_B-h1x3=Ry>M)#10^{4EEalV8Cs2%Zb{W?NJ)+XMMq zPS2C)#n9@f`(1Huwj)G1AJq7djiv|51F@e1^IG$tvS(7)*l)LGyoF?xtUXjmF20i^^AM`<|?v z>Z!x$c=$<z*AsY}5L-kha6>$up}BK92$2A8B!=*E;62pzAoxE~`%S*{rNw z>aD}?XF$*2toV0Re-zvUMdtwX`#aiBsW;EGcKAF3o&k#!l255UHf?Ywd>phsHs(?O z9NYpAL)x{bbNyi`wDwKQJ;`Yw{M`$oyiK<~%ZFR1+(3O?1)(+HYv|X)^`O4Y@tDo= zXxC@0GWL2muv|m(&00SEdi;%q8zAd?({-ypy7X{-AB^*TrX6Fe^B&5(s!MB|rN?Ip zH1|~fjgS5FY>2$zAiljcpz|n8v?cFn9HDP~=Y@TAw^I6!RzN{a|Swxoge) z(X2V&t;4qI&o6u)0^fkwA#}a#-AQ$tS6#MqthM@KZjM=HY@3olep*0zp9jAHjiaBt z^3&Q*>(#TN6J{i+JPq2i^7ZSmUjlv{_qbPWu+clVa{D}d!9(lG74UmVTeCez-8rzY zpF_RJ_!SMes-6zAyz&0=NBC zjyqt#U_(cV%|)PjW@`XFV}1t5IZdiX^hd%Ipmm?y{soRVg4$jSdhZB+(?-7qYz|A4 zw^f5W1=<}AdG=Xl^EH^@G>?nu(qg*$RoKPf_1*U9^2Rq`kI-hfccYx~S2roX^3eG@ z(fY9$;wy)47^@s|#=jrC5tg$_bYnp0RnNWU4tG-4dN^%gMmdz_2WjUfOUhU3eQ5cZ z#8&t8!7vxJml5*)H2UM9)?R|zZiCaze-TN$li?}wbI@8u_ol7~zb^$xfK z)`8h;eYgfb2{U0joB|r-)I9_0M!@}GeoV&uj9LAljn+^Pcx^QHw^T3Uw^cHJFEq_ zzr7r9hgU#zv*aSE>;9wN{xHtL)`MR{r*XB|J(M+9>bdf6(6|V#uMY69dpoAC1Z%+j znADtn255fyi-Y!1)hGAar1|E{u)q1uD`_(ksqhS#FO!7I*}>$ew)?UHDmp4ejmhN zJXh?6_wb;q{*LtpN%!2;6n0#u zT*4N6wTautK4O3JDc_3Gu9*61pF-Ob9Z=n;h`)fKaDMdV)1W!{JBfV1RdakOd>!t9 z+hGCBfNK9KYn^KMpX5UjKeLcJkADZN*EOR(g~gEeo~Su7-cdoSy{<8})4XbaB!378 zc{=GeKOL$*Aoct`2QGwsQbJvuq4piC)*#r=SPaW*y9dtKe$T zILg2DifPwO_1TI}bL)DDa|rnVAayUp8=$e$v-DE4qdXEGg*a!^n?$d79?k1+pRRH0 z^R!=6PkY)7g9|{gO#8@Yc4$xbiW-9)%`BY<6r}*e~R&H zQnvo;{GDm>T!~)i?{cu`*B>ccJClDxXV=dGls^C$!N=fMcnM@HAM=y4wUvAf{OebG zUBj-Oo~xr^ho{%tCB9yeq5DT;x)AI+r+3z+und;K+16*ux_@ti<)Au^@pPy@^A|D; gme|27vDaIA7J8y+3-nyQJJP8xwvjDu7P5c(KTAbi>Hq)$ literal 0 HcmV?d00001 From 5e8813f5b6c811ceb611eebdcd128dc3d67a4d8b Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 24 Nov 2021 13:12:01 +0100 Subject: [PATCH 019/151] [CST-4884] Bitstream edit form moved inside modal (test WIP) --- ...section-upload-file-edit.component.spec.ts | 89 ++++++++- .../section-upload-file-edit.component.ts | 183 +++++++++--------- .../section-upload-file.component.spec.ts | 6 +- 3 files changed, 179 insertions(+), 99 deletions(-) diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts index 9203930ef5..866277108f 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core'; -import { waitForAsync, ComponentFixture, inject, TestBed } from '@angular/core/testing'; +import { waitForAsync, ComponentFixture, inject, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { BrowserModule } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; @@ -17,18 +17,18 @@ import { SubmissionService } from '../../../../submission.service'; import { SubmissionSectionUploadFileEditComponent } from './section-upload-file-edit.component'; import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component'; import { - mockGroup, mockSubmissionCollectionId, mockSubmissionId, mockUploadConfigResponse, mockUploadConfigResponseMetadata, - mockUploadFiles + mockUploadFiles, + mockFileFormData, + mockSubmissionObject, } from '../../../../../shared/mocks/submission.mock'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormComponent } from '../../../../../shared/form/form.component'; import { FormService } from '../../../../../shared/form/form.service'; import { getMockFormService } from '../../../../../shared/mocks/form-service.mock'; -import { Group } from '../../../../../core/eperson/models/group.model'; import { createTestComponent } from '../../../../../shared/testing/utils.test'; import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder'; @@ -37,6 +37,9 @@ import { SubmissionJsonPatchOperationsService } from '../../../../../core/submis import { SectionUploadService } from '../../section-upload.service'; import { getMockSectionUploadService } from '../../../../../shared/mocks/section-upload.service.mock'; import { FormFieldMetadataValueObject } from '../../../../../shared/form/builder/models/form-field-metadata-value.model'; +import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; +import { dateToISOFormat } from '../../../../../shared/date.util'; +import { of } from 'rxjs'; const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { add: jasmine.createSpy('add'), @@ -44,6 +47,8 @@ const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { remove: jasmine.createSpy('remove'), }); +const formMetadataMock = ['dc.title', 'dc.description']; + fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { let comp: SubmissionSectionUploadFileEditComponent; @@ -53,6 +58,8 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { let formbuilderService: any; let operationsBuilder: any; let operationsService: any; + let formService: any; + let uploadService: any; const submissionJsonPatchOperationsServiceStub = new SubmissionJsonPatchOperationsServiceStub(); const submissionId = mockSubmissionId; @@ -64,6 +71,7 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { const fileIndex = '0'; const fileId = '123456-test-upload'; const fileData: any = mockUploadFiles[0]; + const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex); beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -137,6 +145,8 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { formbuilderService = TestBed.inject(FormBuilderService); operationsBuilder = TestBed.inject(JsonPatchOperationsBuilder); operationsService = TestBed.inject(SubmissionJsonPatchOperationsService); + formService = TestBed.inject(FormService); + uploadService = TestBed.inject(SectionUploadService); comp.submissionId = submissionId; comp.collectionId = collectionId; @@ -146,6 +156,7 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { comp.fileIndex = fileIndex; comp.fileId = fileId; comp.configMetadataForm = configMetadataForm; + comp.formMetadata = formMetadataMock; }); afterEach(() => { @@ -221,6 +232,76 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { expect(compAsAny.retrieveValueFromField(field)).toBe('test'); }); + it('should save Bitstream File data properly when form is valid', fakeAsync(() => { + compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); + compAsAny.fileEditComp.formRef = {formGroup: null}; + compAsAny.fileData = fileData; + compAsAny.pathCombiner = pathCombiner; + // const event = new Event('click', null); + // spyOn(comp, 'switchMode'); + formService.validateAllFormFields.and.callFake(() => null); + formService.isValid.and.returnValue(of(true)); + formService.getFormData.and.returnValue(of(mockFileFormData)); + + const response = [ + Object.assign(mockSubmissionObject, { + sections: { + upload: { + files: mockUploadFiles + } + } + }) + ]; + operationsService.jsonPatchByResourceID.and.returnValue(of(response)); + + const accessConditionsToSave = [ + { name: 'openaccess' }, + { name: 'lease', endDate: dateToISOFormat('2019-01-16T00:00:00Z') }, + { name: 'embargo', startDate: dateToISOFormat('2019-01-16T00:00:00Z') }, + ]; + comp.saveBitstreamData(); + tick(); + + let path = 'metadata/dc.title'; + expect(operationsBuilder.add).toHaveBeenCalledWith( + pathCombiner.getPath(path), + mockFileFormData.metadata['dc.title'], + true + ); + + path = 'metadata/dc.description'; + expect(operationsBuilder.add).toHaveBeenCalledWith( + pathCombiner.getPath(path), + mockFileFormData.metadata['dc.description'], + true + ); + + path = 'accessConditions'; + expect(operationsBuilder.add).toHaveBeenCalledWith( + pathCombiner.getPath(path), + accessConditionsToSave, + true + ); + + // expect(comp.switchMode).toHaveBeenCalled(); + expect(uploadService.updateFileData).toHaveBeenCalledWith(submissionId, sectionId, mockUploadFiles[0].uuid, mockUploadFiles[0]); + + })); + + it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => { + compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); + compAsAny.fileEditComp.formRef = {formGroup: null}; + compAsAny.pathCombiner = pathCombiner; + // const event = new Event('click', null); + // spyOn(comp, 'switchMode'); + formService.validateAllFormFields.and.callFake(() => null); + formService.isValid.and.returnValue(of(false)); + + // expect(comp.switchMode).not.toHaveBeenCalled(); + expect(uploadService.updateFileData).not.toHaveBeenCalled(); + + })); + }); }); diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index eb8fbf95d0..32b82bec45 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; import { FormControl } from '@angular/forms'; import { @@ -60,6 +60,97 @@ import { Subscription } from 'rxjs'; }) export class SubmissionSectionUploadFileEditComponent implements OnInit { + /** + * The FormComponent reference + */ + @ViewChild('formRef') public formRef: FormComponent; + + /** + * The list of available access condition + * @type {Array} + */ + public availableAccessConditionOptions: any[]; + + /** + * The submission id + * @type {string} + */ + public collectionId: string; + + /** + * Define if collection access conditions policy type : + * POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file + * POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file + * @type {number} + */ + public collectionPolicyType: number; + + /** + * The configuration for the bitstream's metadata form + * @type {SubmissionFormsModel} + */ + public configMetadataForm: SubmissionFormsModel; + + /** + * The bitstream's metadata data + * @type {WorkspaceitemSectionUploadFileObject} + */ + public fileData: WorkspaceitemSectionUploadFileObject; + + /** + * The bitstream id + * @type {string} + */ + public fileId: string; + + /** + * The bitstream array key + * @type {string} + */ + public fileIndex: string; + + /** + * The form id + * @type {string} + */ + public formId: string; + + /** + * The section id + * @type {string} + */ + public sectionId: string; + + /** + * The submission id + * @type {string} + */ + public submissionId: string; + + /** + * The list of all available metadata + */ + formMetadata: string[] = []; + + /** + * The form model + * @type {DynamicFormControlModel[]} + */ + formModel: DynamicFormControlModel[]; + + /** + * When `true` form controls are deactivated + */ + isSaving = false; + + /** + * The [JsonPatchOperationPathCombiner] object + * @type {JsonPatchOperationPathCombiner} + */ + protected pathCombiner: JsonPatchOperationPathCombiner; + + protected subscriptions: Subscription[] = []; + /** * Initialize instance variables * @@ -84,94 +175,6 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { ) { } - /** - * The list of available access condition - * @type {Array} - */ - @Input() availableAccessConditionOptions: any[]; - - /** - * The submission id - * @type {string} - */ - @Input() collectionId: string; - - /** - * Define if collection access conditions policy type : - * POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file - * POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file - * @type {number} - */ - @Input() collectionPolicyType: number; - - /** - * The configuration for the bitstream's metadata form - * @type {SubmissionFormsModel} - */ - @Input() configMetadataForm: SubmissionFormsModel; - - /** - * The bitstream's metadata data - * @type {WorkspaceitemSectionUploadFileObject} - */ - @Input() fileData: WorkspaceitemSectionUploadFileObject; - - /** - * The bitstream id - * @type {string} - */ - @Input() fileId: string; - - /** - * The bitstream array key - * @type {string} - */ - @Input() fileIndex: string; - - /** - * The form id - * @type {string} - */ - @Input() formId: string; - - /** - * The section id - * @type {string} - */ - @Input() sectionId: string; - - /** - * The submission id - * @type {string} - */ - @Input() submissionId: string; - - /** - * The list of all available metadata - */ - @Input() formMetadata: string[] = []; - - /** - * The [JsonPatchOperationPathCombiner] object - * @type {JsonPatchOperationPathCombiner} - */ - @Input() pathCombiner: JsonPatchOperationPathCombiner; - - /** - * The FormComponent reference - */ - @ViewChild('formRef') public formRef: FormComponent; - - /** - * The form model - * @type {DynamicFormControlModel[]} - */ - formModel: DynamicFormControlModel[]; - - isSaving = false; - - protected subscriptions: Subscription[] = []; - /** * Initialize form model values * @@ -379,7 +382,7 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { /** * Save bitstream metadata */ - protected saveBitstreamData() { + saveBitstreamData() { // validate form this.formService.validateAllFormFields(this.formRef.formGroup); const saveBitstreamDataSubscription = this.formService.isValid(this.formId).pipe( diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts index a8ab34d866..2fd7be96d0 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { BrowserModule, By } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; @@ -17,10 +17,8 @@ import { SubmissionJsonPatchOperationsService } from '../../../../core/submissio import { SubmissionSectionUploadFileComponent } from './section-upload-file.component'; import { SubmissionServiceStub } from '../../../../shared/testing/submission-service.stub'; import { - mockFileFormData, mockSubmissionCollectionId, mockSubmissionId, - mockSubmissionObject, mockUploadConfigResponse, mockUploadFiles } from '../../../../shared/mocks/submission.mock'; @@ -32,10 +30,8 @@ import { FileSizePipe } from '../../../../shared/utils/file-size-pipe'; import { POLICY_DEFAULT_WITH_LIST } from '../section-upload.component'; import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { getMockSectionUploadService } from '../../../../shared/mocks/section-upload.service.mock'; -import { FormFieldMetadataValueObject } from '../../../../shared/form/builder/models/form-field-metadata-value.model'; import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; -import { dateToISOFormat } from '../../../../shared/date.util'; const configMetadataFormMock = { rows: [{ From 7c23e2ef8238aa210c93131e62db43af2fa127e2 Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Wed, 24 Nov 2021 14:44:38 +0100 Subject: [PATCH 020/151] 85123: Fix tests --- src/app/app.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9dc2b5a5ab..8b706d1fb6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -293,7 +293,7 @@ export class AppComponent implements OnInit, AfterViewInit { const headTagConfigs = themeConfig?.headTags; if (isEmpty(headTagConfigs)) { - const parentThemeName = themeConfig.extends; + const parentThemeName = themeConfig?.extends; if (isNotEmpty(parentThemeName)) { // inherit the head tags of the parent theme return this.createHeadTags(parentThemeName); From b6dc7af13ec3a6c1a17db29f8e2888a8e886b1f9 Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Wed, 24 Nov 2021 15:13:50 +0100 Subject: [PATCH 021/151] 85123: Fix tests #2 --- src/app/app.component.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 8b706d1fb6..e614fd918b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -246,6 +246,10 @@ export class AppComponent implements OnInit, AfterViewInit { */ private setThemeCss(themeName: string): void { const head = this.document.getElementsByTagName('head')[0]; + if (isEmpty(head)) { + return; + } + // Array.from to ensure we end up with an array, not an HTMLCollection, which would be // automatically updated if we add nodes later const currentThemeLinks = Array.from(head.getElementsByClassName('theme-css')); @@ -272,6 +276,9 @@ export class AppComponent implements OnInit, AfterViewInit { private setHeadTags(themeName: string): void { const head = this.document.getElementsByTagName('head')[0]; + if (isEmpty(head)) { + return; + } // clear head tags const currentHeadTags = Array.from(head.getElementsByClassName('theme-head-tag')); From 787358d1b0dd996ba4702e65ef5cc6dbfb5697bf Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Wed, 24 Nov 2021 16:08:26 +0100 Subject: [PATCH 022/151] 85123: Fix tests - 3 --- src/app/app.component.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 3f2dc45ce7..937b71eb5a 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -171,7 +171,8 @@ describe('App component', () => { TestBed.configureTestingModule(getDefaultTestBedConf()); TestBed.overrideProvider(ThemeService, {useValue: getMockThemeService('custom')}); document = TestBed.inject(DOCUMENT); - headSpy = jasmine.createSpyObj('head', ['appendChild']); + headSpy = jasmine.createSpyObj('head', ['appendChild', 'getElementsByClassName']); + headSpy.getElementsByClassName.and.returnValue([]); spyOn(document, 'getElementsByTagName').and.returnValue([headSpy]); fixture = TestBed.createComponent(AppComponent); comp = fixture.componentInstance; From 6752acbf128e611a9593679ca64aa52b5fe1d88f Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 24 Nov 2021 20:00:21 +0100 Subject: [PATCH 023/151] [CST-4884] Test --- ...section-upload-file-edit.component.spec.ts | 19 ++++++++----------- .../section-upload-file.component.spec.ts | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts index 866277108f..aa03d37eb2 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts @@ -49,7 +49,7 @@ const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { const formMetadataMock = ['dc.title', 'dc.description']; -fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { +describe('SubmissionSectionUploadFileEditComponent test suite', () => { let comp: SubmissionSectionUploadFileEditComponent; let compAsAny: any; @@ -98,6 +98,7 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { SubmissionSectionUploadFileEditComponent, NgbModal, NgbActiveModal, + FormComponent, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents().then(); @@ -157,6 +158,8 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { comp.fileId = fileId; comp.configMetadataForm = configMetadataForm; comp.formMetadata = formMetadataMock; + + formService.isValid.and.returnValue(of(true)); }); afterEach(() => { @@ -233,12 +236,9 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { }); it('should save Bitstream File data properly when form is valid', fakeAsync(() => { - compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); - compAsAny.fileEditComp.formRef = {formGroup: null}; + compAsAny.formRef = {formGroup: null}; compAsAny.fileData = fileData; compAsAny.pathCombiner = pathCombiner; - // const event = new Event('click', null); - // spyOn(comp, 'switchMode'); formService.validateAllFormFields.and.callFake(() => null); formService.isValid.and.returnValue(of(true)); formService.getFormData.and.returnValue(of(mockFileFormData)); @@ -283,21 +283,18 @@ fdescribe('SubmissionSectionUploadFileEditComponent test suite', () => { true ); - // expect(comp.switchMode).toHaveBeenCalled(); expect(uploadService.updateFileData).toHaveBeenCalledWith(submissionId, sectionId, mockUploadFiles[0].uuid, mockUploadFiles[0]); })); it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => { - compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); - compAsAny.fileEditComp.formRef = {formGroup: null}; + compAsAny.formRef = {formGroup: null}; compAsAny.pathCombiner = pathCombiner; - // const event = new Event('click', null); - // spyOn(comp, 'switchMode'); formService.validateAllFormFields.and.callFake(() => null); formService.isValid.and.returnValue(of(false)); + comp.saveBitstreamData(); + tick(); - // expect(comp.switchMode).not.toHaveBeenCalled(); expect(uploadService.updateFileData).not.toHaveBeenCalled(); })); diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts index 2fd7be96d0..50b245e481 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts @@ -44,7 +44,7 @@ const configMetadataFormMock = { }] }; -fdescribe('SubmissionSectionUploadFileComponent test suite', () => { +describe('SubmissionSectionUploadFileComponent test suite', () => { let comp: SubmissionSectionUploadFileComponent; let compAsAny: any; From 27bce0e5bb550c1d02d7023eb7ffaf72243ca7cf Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 24 Nov 2021 20:17:16 +0100 Subject: [PATCH 024/151] [CST-4879] After changing options in access-conditions.xml, "grant access" fields are displayed incorrectly --- .../upload/file/edit/section-upload-file-edit.component.ts | 6 ++++-- .../sections/upload/file/section-upload-file.component.ts | 4 ---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index 32b82bec45..fda86dd629 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -235,7 +235,7 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { public setOptions(model: DynamicFormControlModel, control: FormControl) { let accessCondition: AccessConditionOption = null; this.availableAccessConditionOptions.filter((element) => element.name === control.value) - .forEach((element) => accessCondition = element); + .forEach((element) => accessCondition = element ); if (isNotEmpty(accessCondition)) { const showGroups: boolean = accessCondition.hasStartDate === true || accessCondition.hasEndDate === true; @@ -364,7 +364,9 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { const startDate = new DynamicDatePickerModel(startDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT); const endDate = new DynamicDatePickerModel(endDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_LAYOUT); const accessConditionGroupConfig = Object.assign({}, BITSTREAM_ACCESS_CONDITION_GROUP_CONFIG); - accessConditionGroupConfig.group = [type, startDate, endDate]; + accessConditionGroupConfig.group = [type]; + if (hasStart.length > 0) { accessConditionGroupConfig.group.push(startDate); } + if (hasEnd.length > 0) { accessConditionGroupConfig.group.push(endDate); } return [new DynamicFormGroupModel(accessConditionGroupConfig, BITSTREAM_ACCESS_CONDITION_GROUP_LAYOUT)]; }; diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index e152d2f651..0841e1ca3a 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -247,10 +247,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { activeModal.componentInstance.pathCombiner = this.pathCombiner; activeModal.componentInstance.submissionId = this.submissionId; - /*activeModal.componentInstance.saveBitstreamDataEvent.subscribe((res) => { - console.log(JSON.stringify(res)); - });*/ - } ngOnDestroy(): void { From 7218c450e6993a4b50f6e169a45e41532affe701 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Wed, 24 Nov 2021 23:39:59 +0100 Subject: [PATCH 025/151] [CST-4884] Code cleanup and test improvement --- .../file/section-upload-file.component.html | 2 +- .../file/section-upload-file.component.scss | 6 -- .../section-upload-file.component.spec.ts | 84 ++----------------- .../file/section-upload-file.component.ts | 8 -- 4 files changed, 10 insertions(+), 90 deletions(-) diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.html b/src/app/submission/sections/upload/file/section-upload-file.component.html index 8749c8dcf2..1bfc52529b 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.html +++ b/src/app/submission/sections/upload/file/section-upload-file.component.html @@ -8,7 +8,7 @@

{{fileName}} ({{fileData?.sizeBytes | dsFileSize}})

-
+
diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.scss b/src/app/submission/sections/upload/file/section-upload-file.component.scss index 256775eb66..e69de29bb2 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.scss +++ b/src/app/submission/sections/upload/file/section-upload-file.component.scss @@ -1,6 +0,0 @@ -.sticky-buttons { - position: sticky; - top: calc(var(--bs-dropdown-item-padding-x) * 3); - z-index: var(--ds-submission-footer-z-index); - background-color: rgba(255, 255, 255, .97); -} diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts index 50b245e481..4fea8d3f25 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts @@ -217,86 +217,20 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { pathCombiner.subRootElement); }); - /*it('should save Bitstream File data properly when form is valid', fakeAsync(() => { - compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); - compAsAny.fileEditComp.formRef = {formGroup: null}; - compAsAny.fileData = fileData; - compAsAny.pathCombiner = pathCombiner; - const event = new Event('click', null); - spyOn(comp, 'switchMode'); - formService.validateAllFormFields.and.callFake(() => null); - formService.isValid.and.returnValue(observableOf(true)); - formService.getFormData.and.returnValue(observableOf(mockFileFormData)); + it('should open edit modal when edit button is clicked', () => { + spyOn(compAsAny, 'editBitstreamData').and.callThrough(); + comp.fileData = fileData; - const response = [ - Object.assign(mockSubmissionObject, { - sections: { - upload: { - files: mockUploadFiles - } - } - }) - ]; - operationsService.jsonPatchByResourceID.and.returnValue(observableOf(response)); + fixture.detectChanges(); - const accessConditionsToSave = [ - { name: 'openaccess' }, - { name: 'lease', endDate: dateToISOFormat('2019-01-16T00:00:00Z') }, - { name: 'embargo', startDate: dateToISOFormat('2019-01-16T00:00:00Z') }, - ]; - comp.saveBitstreamData(event); - tick(); + const modalBtn = fixture.debugElement.query(By.css('.fa-edit ')); - let path = 'metadata/dc.title'; - expect(operationsBuilder.add).toHaveBeenCalledWith( - pathCombiner.getPath(path), - mockFileFormData.metadata['dc.title'], - true - ); + modalBtn.nativeElement.click(); + fixture.detectChanges(); - path = 'metadata/dc.description'; - expect(operationsBuilder.add).toHaveBeenCalledWith( - pathCombiner.getPath(path), - mockFileFormData.metadata['dc.description'], - true - ); - - path = 'accessConditions'; - expect(operationsBuilder.add).toHaveBeenCalledWith( - pathCombiner.getPath(path), - accessConditionsToSave, - true - ); - - expect(comp.switchMode).toHaveBeenCalled(); - expect(uploadService.updateFileData).toHaveBeenCalledWith(submissionId, sectionId, mockUploadFiles[0].uuid, mockUploadFiles[0]); - - }));*/ - - /*it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => { - compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent); - compAsAny.fileEditComp.formRef = {formGroup: null}; - compAsAny.pathCombiner = pathCombiner; - const event = new Event('click', null); - spyOn(comp, 'switchMode'); - formService.validateAllFormFields.and.callFake(() => null); - formService.isValid.and.returnValue(observableOf(false)); - - expect(comp.switchMode).not.toHaveBeenCalled(); - expect(uploadService.updateFileData).not.toHaveBeenCalled(); - - }));*/ - - it('should switch read mode', () => { - comp.readMode = false; - - comp.switchMode(); - expect(comp.readMode).toBeTruthy(); - - comp.switchMode(); - - expect(comp.readMode).toBeFalsy(); + expect(compAsAny.editBitstreamData).toHaveBeenCalled(); }); + }); }); diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index 0841e1ca3a..53358d48e2 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -217,14 +217,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { }); } - /** - * Switch from edit form to metadata view - */ - public switchMode() { - this.readMode = !this.readMode; - this.cdr.detectChanges(); - } - editBitstreamData() { const options: NgbModalOptions = { From 6df1ee64f27a844aa4a80118d8fb6000fb298406 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 2 Aug 2021 09:18:21 +0200 Subject: [PATCH 026/151] [DSC-184] Fix issue with mydpsace page that hangs forever on error response --- src/app/my-dspace-page/my-dspace-page.component.html | 4 ++-- src/app/my-dspace-page/my-dspace-page.component.ts | 11 +++++------ src/app/shared/error/error.component.html | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) 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 4aadb16255..5a15abcc08 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.html +++ b/src/app/my-dspace-page/my-dspace-page.component.html @@ -4,7 +4,7 @@ = new InjectionToken('searchConfigurationService'); @@ -111,8 +111,7 @@ export class MyDSpacePageComponent implements OnInit { constructor(private service: SearchService, private sidebarService: SidebarService, private windowService: HostWindowService, - @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService, - private routeService: RouteService) { + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) { this.isXsOrSm$ = this.windowService.isXsOrSm(); this.service.setServiceOptions(MyDSpaceResponseParsingService, MyDSpaceRequest); } @@ -134,8 +133,8 @@ export class MyDSpacePageComponent implements OnInit { this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; this.sub = this.searchOptions$.pipe( tap(() => this.resultsRD$.next(null)), - switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getFirstSucceededRemoteData()))) - .subscribe((results) => { + switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getFirstCompletedRemoteData()))) + .subscribe((results: RemoteData>) => { this.resultsRD$.next(results); }); diff --git a/src/app/shared/error/error.component.html b/src/app/shared/error/error.component.html index d41760e258..dd75cb4b1b 100644 --- a/src/app/shared/error/error.component.html +++ b/src/app/shared/error/error.component.html @@ -1,3 +1,3 @@ -
- -
+ + {{ message }} + From 7a567a47b9ff7851c645b69b4a0d6627aa80fb5f Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 26 Nov 2021 11:20:35 +0100 Subject: [PATCH 027/151] [CST-4882] Show error message if the search query fails - Handle error 422 (invalid query) separately --- .../my-dspace-results/my-dspace-results.component.html | 2 +- .../my-dspace-results/my-dspace-results.component.ts | 8 ++++++++ src/app/shared/error/error.component.html | 5 +++-- src/app/shared/error/error.component.spec.ts | 2 +- src/app/shared/error/error.component.ts | 7 +++++++ src/assets/i18n/en.json5 | 2 ++ 6 files changed, 22 insertions(+), 4 deletions(-) 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 index 2710285f0d..ad06f97c88 100644 --- 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 @@ -10,5 +10,5 @@
- +

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

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 32b6d9c9f7..96395adea2 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 @@ -58,4 +58,12 @@ export class MyDSpaceResultsComponent { isLoading() { return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading; } + + showError(searchResults: RemoteData>>): boolean { + return searchResults?.hasFailed && (!searchResults?.errorMessage || searchResults?.statusCode !== 400); + } + + errorMessageLabel(searchResults: RemoteData>>): string { + return (searchResults?.statusCode === 422) ? 'error.invalid-search-query' : 'error.search-results'; + } } diff --git a/src/app/shared/error/error.component.html b/src/app/shared/error/error.component.html index dd75cb4b1b..157c47f2cd 100644 --- a/src/app/shared/error/error.component.html +++ b/src/app/shared/error/error.component.html @@ -1,3 +1,4 @@ - - {{ message }} + + + diff --git a/src/app/shared/error/error.component.spec.ts b/src/app/shared/error/error.component.spec.ts index d3e3e80c07..f8483ac7aa 100644 --- a/src/app/shared/error/error.component.spec.ts +++ b/src/app/shared/error/error.component.spec.ts @@ -36,7 +36,7 @@ describe('ErrorComponent (inline template)', () => { comp = fixture.componentInstance; // ErrorComponent test instance // query for the message
  • - +
diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts index 1f05ad2258..861e431635 100644 --- a/src/app/shared/browse-by/browse-by.component.ts +++ b/src/app/shared/browse-by/browse-by.component.ts @@ -8,6 +8,7 @@ import { Observable } from 'rxjs'; import { ListableObject } from '../object-collection/shared/listable-object.model'; import { getStartsWithComponent, StartsWithType } from '../starts-with/starts-with-decorator'; import { PaginationService } from '../../core/pagination/pagination.service'; +import { ViewMode } from '../../core/shared/view-mode.model'; @Component({ selector: 'ds-browse-by', @@ -22,6 +23,12 @@ import { PaginationService } from '../../core/pagination/pagination.service'; * Component to display a browse-by page for any ListableObject */ export class BrowseByComponent implements OnInit { + + /** + * ViewMode that should be passed to {@link ListableObjectComponentLoaderComponent}. + */ + viewMode: ViewMode = ViewMode.ListElement; + /** * The i18n message to display as title */ From dcb80b7c9c950f49de7815fc65732769263f2bb6 Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Wed, 1 Dec 2021 15:56:49 +0100 Subject: [PATCH 031/151] 85192: Write test for themed browse entries --- .../browse-by/browse-by.component.spec.ts | 90 ++++++++++++++++++- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index 806f4bdb6f..31c4feb8bd 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -2,7 +2,7 @@ import { BrowseByComponent } from './browse-by.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; import { SharedModule } from '../shared.module'; import { CommonModule } from '@angular/common'; @@ -18,9 +18,24 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { storeModuleConfig } from '../../app.reducer'; -import { FindListOptions } from '../../core/data/request.models'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; +import { ListableObjectComponentLoaderComponent } from '../object-collection/shared/listable-object/listable-object-component-loader.component'; +import { ViewMode } from '../../core/shared/view-mode.model'; +import { BrowseEntryListElementComponent } from '../object-list/browse-entry-list-element/browse-entry-list-element.component'; +import { listableObjectComponent } from '../object-collection/shared/listable-object/listable-object.decorator'; +import { BrowseEntry } from '../../core/shared/browse-entry.model'; +import { ITEM } from '../../core/shared/item.resource-type'; +import { DEFAULT_CONTEXT } from '../metadata-representation/metadata-representation.decorator'; +import { ThemeService } from '../theme-support/theme.service'; +import SpyObj = jasmine.SpyObj; + +@listableObjectComponent(BrowseEntry, ViewMode.ListElement, DEFAULT_CONTEXT, 'custom') +@Component({ + selector: 'ds-browse-entry-list-element', + template: '' +}) +class MockThemedBrowseEntryListElementComponent {} describe('BrowseByComponent', () => { let comp: BrowseByComponent; @@ -56,7 +71,11 @@ describe('BrowseByComponent', () => { }); const paginationService = new PaginationServiceStub(paginationConfig); + let themeService: SpyObj; + beforeEach(waitForAsync(() => { + themeService = jasmine.createSpyObj('ThemeService', ['getThemeName', 'getThemeName$']); + TestBed.configureTestingModule({ imports: [ CommonModule, @@ -75,7 +94,9 @@ describe('BrowseByComponent', () => { ], declarations: [], providers: [ - {provide: PaginationService, useValue: paginationService} + {provide: PaginationService, useValue: paginationService}, + {provide: MockThemedBrowseEntryListElementComponent}, + {provide: ThemeService, useValue: themeService}, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -163,4 +184,67 @@ describe('BrowseByComponent', () => { }); }); + describe('when enableArrows is true and browseEntries are provided', () => { + let browseEntries; + + beforeEach(() => { + browseEntries = [ + Object.assign(new BrowseEntry(), { + type: ITEM, + authority: 'authority key 1', + value: 'browse entry 1', + language: null, + count: 1, + }), + Object.assign(new BrowseEntry(), { + type: ITEM, + authority: null, + value: 'browse entry 2', + language: null, + count: 4, + }), + ]; + + comp.enableArrows = true; + comp.objects$ = createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), browseEntries)); + comp.paginationConfig = paginationConfig; + comp.sortConfig = Object.assign(new SortOptions('dc.title', SortDirection.ASC)); + // NOTE: do NOT trigger change detection until the theme is set, such that the theme can be picked up as well + }); + + describe('when theme is base', () => { + beforeEach(() => { + themeService.getThemeName.and.returnValue('base'); + themeService.getThemeName$.and.returnValue(observableOf('base')); + fixture.detectChanges(); + }); + + it('should use the base component to render browse entries', () => { + const componentLoaders = fixture.debugElement.queryAll(By.directive(ListableObjectComponentLoaderComponent)); + expect(componentLoaders.length).toEqual(browseEntries.length); + componentLoaders.forEach((componentLoader) => { + const browseEntry = componentLoader.query(By.css('ds-browse-entry-list-element')); + expect(browseEntry.componentInstance).toBeInstanceOf(BrowseEntryListElementComponent); + }); + }); + }); + + describe('when theme is custom', () => { + beforeEach(() => { + themeService.getThemeName.and.returnValue('custom'); + themeService.getThemeName$.and.returnValue(observableOf('custom')); + fixture.detectChanges(); + }); + + it('should use the themed component to render browse entries', () => { + const componentLoaders = fixture.debugElement.queryAll(By.directive(ListableObjectComponentLoaderComponent)); + expect(componentLoaders.length).toEqual(browseEntries.length); + componentLoaders.forEach((componentLoader) => { + const browseEntry = componentLoader.query(By.css('ds-browse-entry-list-element')); + expect(browseEntry.componentInstance).toBeInstanceOf(MockThemedBrowseEntryListElementComponent); + }); + }); + }); + }); + }); From e99086c228df2104bb5c99901f5f9945576b049a Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Wed, 1 Dec 2021 17:16:21 +0100 Subject: [PATCH 032/151] Fix bad merge --- src/app/shared/browse-by/browse-by.component.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index cb0c799762..896537b63a 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -23,10 +23,12 @@ import { PaginationServiceStub } from '../testing/pagination-service.stub'; import { ListableObjectComponentLoaderComponent } from '../object-collection/shared/listable-object/listable-object-component-loader.component'; import { ViewMode } from '../../core/shared/view-mode.model'; import { BrowseEntryListElementComponent } from '../object-list/browse-entry-list-element/browse-entry-list-element.component'; -import { listableObjectComponent } from '../object-collection/shared/listable-object/listable-object.decorator'; +import { + DEFAULT_CONTEXT, + listableObjectComponent, +} from '../object-collection/shared/listable-object/listable-object.decorator'; import { BrowseEntry } from '../../core/shared/browse-entry.model'; import { ITEM } from '../../core/shared/item.resource-type'; -import { DEFAULT_CONTEXT } from '../metadata-representation/metadata-representation.decorator'; import { ThemeService } from '../theme-support/theme.service'; import SpyObj = jasmine.SpyObj; @@ -41,8 +43,6 @@ describe('BrowseByComponent', () => { let comp: BrowseByComponent; let fixture: ComponentFixture; - let themeService: ThemeService; - const mockItems = [ Object.assign(new Item(), { id: 'fakeId-1', From c8de6ccb4c42dfbb08cbc5aed6398a2f61168449 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Wed, 1 Dec 2021 18:50:42 +0100 Subject: [PATCH 033/151] [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 034/151] 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 47ed6bedb4e2f92bf78d38c924debfde7f4d2f4b Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Thu, 2 Dec 2021 12:47:38 +0100 Subject: [PATCH 035/151] 85123: BUGFIX: Use this.document instead of document Typo caused ReferenceError in SSR --- src/app/app.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e614fd918b..1d7e86ff80 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -287,7 +287,7 @@ export class AppComponent implements OnInit, AfterViewInit { } // create new head tags (not yet added to DOM) - const headTagFragment = document.createDocumentFragment(); + const headTagFragment = this.document.createDocumentFragment(); this.createHeadTags(themeName) .forEach(newHeadTag => headTagFragment.appendChild(newHeadTag)); From 03fd57e426e3575adec2790641b3b2b904294c86 Mon Sep 17 00:00:00 2001 From: Bruno Roemers Date: Thu, 2 Dec 2021 14:25:16 +0100 Subject: [PATCH 036/151] 85194: Fix favicon switching in production mode --- src/app/app.component.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1d7e86ff80..03075f4f18 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -36,7 +36,7 @@ import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider'; import { environment } from '../environments/environment'; import { models } from './core/core.module'; import { LocaleService } from './core/locale/locale.service'; -import { hasValue, isEmpty, isNotEmpty } from './shared/empty.util'; +import { hasNoValue, 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'; @@ -246,7 +246,7 @@ export class AppComponent implements OnInit, AfterViewInit { */ private setThemeCss(themeName: string): void { const head = this.document.getElementsByTagName('head')[0]; - if (isEmpty(head)) { + if (hasNoValue(head)) { return; } @@ -276,13 +276,13 @@ export class AppComponent implements OnInit, AfterViewInit { private setHeadTags(themeName: string): void { const head = this.document.getElementsByTagName('head')[0]; - if (isEmpty(head)) { + if (hasNoValue(head)) { return; } // clear head tags const currentHeadTags = Array.from(head.getElementsByClassName('theme-head-tag')); - if (isNotEmpty(currentHeadTags)) { + if (hasValue(currentHeadTags)) { currentHeadTags.forEach((currentHeadTag: any) => currentHeadTag.remove()); } @@ -299,16 +299,16 @@ export class AppComponent implements OnInit, AfterViewInit { const themeConfig = this.themeService.getThemeConfigFor(themeName); const headTagConfigs = themeConfig?.headTags; - if (isEmpty(headTagConfigs)) { + if (hasNoValue(headTagConfigs)) { const parentThemeName = themeConfig?.extends; - if (isNotEmpty(parentThemeName)) { + if (hasValue(parentThemeName)) { // inherit the head tags of the parent theme return this.createHeadTags(parentThemeName); } const defaultThemeName = DEFAULT_THEME_CONFIG.name; if ( - isEmpty(defaultThemeName) || + hasNoValue(defaultThemeName) || themeName === defaultThemeName || themeName === BASE_THEME_NAME ) { @@ -335,7 +335,7 @@ export class AppComponent implements OnInit, AfterViewInit { private createHeadTag(headTagConfig: HeadTagConfig): HTMLElement { const tag = this.document.createElement(headTagConfig.tagName); - if (isNotEmpty(headTagConfig.attributes)) { + if (hasValue(headTagConfig.attributes)) { Object.entries(headTagConfig.attributes) .forEach(([key, value]) => tag.setAttribute(key, value)); } From 768de1a1e73a585dfb50f3f1eb6d09b04dbcde06 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Fri, 3 Dec 2021 10:54:41 -0500 Subject: [PATCH 037/151] 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 038/151] 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 039/151] 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 040/151] 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 041/151] 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 042/151] 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 0dba48be13a7a3514d7ba84872348e95d58e0a25 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 7 Dec 2021 16:50:43 -0600 Subject: [PATCH 043/151] Update to latest version of all GitHub actions --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7758020724..539fd740ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,11 +29,11 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v1 + uses: actions/checkout@v2 # https://github.com/actions/setup-node - name: Install Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} @@ -82,7 +82,7 @@ jobs: # Upload coverage reports to Codecov (for Node v12 only) # https://github.com/codecov/codecov-action - name: Upload coverage to Codecov.io - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 if: matrix.node-version == '12.x' # Using docker-compose start backend using CI configuration From bc999d0b5f13e603fabc7668fbc2c867a4a29526 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 7 Dec 2021 21:33:43 -0600 Subject: [PATCH 044/151] 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 86c2f389d5087c89687967aa81728acc6c934a23 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 7 Dec 2021 16:51:54 -0600 Subject: [PATCH 045/151] Create a Docker GitHub action to autobuild on each commit. Update Dockerfile and docker-compose.yml to support. --- .github/workflows/docker.yml | 61 ++++++++++++++++++++++++++++++++++++ Dockerfile | 4 +-- docker/README.md | 14 +++++++++ docker/docker-compose.yml | 2 +- 4 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000..591df8b7d9 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,61 @@ +# DSpace Docker image build for hub.docker.com +name: Docker images + +# Run this Build for all pushes / PRs to current branch +on: [push, pull_request] + +jobs: + docker: + runs-on: ubuntu-latest + env: + # Define tags to use for Docker images based on Git tags/branches + # For a new branch commit, tag Docker image with that branch name + # Except for 'main' branch, where we use the literal 'dspace-7_x' tag (see below). + # For a new tag, tag Docker image with that same tag + IMAGE_TAGS: | + type=ref,event=branch,enable=${{ github.ref != 'refs/heads/main' }} + type=raw,value=dspace-7_x,enable=${{ github.ref == 'refs/heads/main' }} + type=ref,event=tag + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v2 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v1 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + ############################################### + # Build/Push the 'dspace/dspace-angular' image + ############################################### + # https://github.com/docker/metadata-action + # Get Metadata for docker_build step below + - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-angular' image + id: meta_build + uses: docker/metadata-action@v3 + with: + images: dspace/dspace-angular + tags: ${{ env.IMAGE_TAGS }} + + # https://github.com/docker/build-push-action + - name: Build and push 'dspace-angular' image + id: docker_build + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + # For pull requests, we run the Docker build (to ensure no PR changes break the build), + # but we ONLY do an image push to DockerHub if it's NOT a PR + push: ${{ github.event_name != 'pull_request' }} + # Use tags / labels provided by 'docker/metadata-action' above + tags: ${{ steps.meta_build.outputs.tags }} + labels: ${{ steps.meta_build.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index db9983cace..2d98971112 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # This image will be published as dspace/dspace-angular -# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details +# See https://github.com/DSpace/dspace-angular/tree/main/docker for usage details -FROM node:12-alpine +FROM node:14-alpine WORKDIR /app ADD . /app/ EXPOSE 4000 diff --git a/docker/README.md b/docker/README.md index 747db22143..b0943562af 100644 --- a/docker/README.md +++ b/docker/README.md @@ -4,6 +4,20 @@ :warning: **NOT PRODUCTION READY** The below Docker Compose resources are not guaranteed "production ready" at this time. They have been built for development/testing only. Therefore, DSpace Docker images may not be fully secured or up-to-date. While you are welcome to base your own images on these DSpace images/resources, these should not be used "as is" in any production scenario. *** +## 'Dockerfile' in root directory +This Dockerfile is used to build a *development* DSpace 7 Angular UI image, published as 'dspace/dspace-angular' + +``` +docker build -t dspace/dspace-angular:dspace-7_x . +``` + +This image is built *automatically* after each commit is made to the `main` branch. + +Admins to our DockerHub repo can manually publish with the following command. +``` +docker push dspace/dspace-angular:dspace-7_x +``` + ## docker directory - docker-compose.yml - Starts DSpace Angular with Docker Compose from the current branch. This file assumes that a DSpace 7 REST instance will also be started in Docker. diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7c5c326959..e518dc99d2 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -20,7 +20,7 @@ services: DSPACE_NAMESPACE: / DSPACE_PORT: '4000' DSPACE_SSL: "false" - image: dspace/dspace-angular:latest + image: dspace/dspace-angular:dspace-7_x build: context: .. dockerfile: Dockerfile From 35584d44aa1a97d3a7cd05dc7861de6ace0a28eb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 8 Dec 2021 09:42:48 -0600 Subject: [PATCH 046/151] Minor cleanup. Disable 'latest' tag in Docker. --- .github/workflows/docker.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 591df8b7d9..913a1f514a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -8,14 +8,19 @@ jobs: docker: runs-on: ubuntu-latest env: - # Define tags to use for Docker images based on Git tags/branches - # For a new branch commit, tag Docker image with that branch name - # Except for 'main' branch, where we use the literal 'dspace-7_x' tag (see below). - # For a new tag, tag Docker image with that same tag + # Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action) + # For a new commit on default branch (main), use the literal tag 'dspace-7_x' on Docker image. + # For a new commit on other branches, use the branch name as the tag for Docker image. + # For a new tag, copy that tag name as the tag for Docker image. IMAGE_TAGS: | - type=ref,event=branch,enable=${{ github.ref != 'refs/heads/main' }} - type=raw,value=dspace-7_x,enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=dspace-7_x,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }} type=ref,event=tag + # Define default tag "flavor" for docker/metadata-action per + # https://github.com/docker/metadata-action#flavor-input + # We turn off 'latest' tag by default. + TAGS_FLAVOR: | + latest=false steps: # https://github.com/actions/checkout @@ -45,6 +50,7 @@ jobs: with: images: dspace/dspace-angular tags: ${{ env.IMAGE_TAGS }} + flavor: ${{ env.TAGS_FLAVOR }} # https://github.com/docker/build-push-action - name: Build and push 'dspace-angular' image From 10622008c407fdb77d3df24507cc580e75862685 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 8 Dec 2021 09:38:52 -0600 Subject: [PATCH 047/151] 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 a6eeceeb67e3213eb590803b847da8169b4dc107 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 8 Dec 2021 15:08:44 -0600 Subject: [PATCH 048/151] Ensure Docker GitHub action only runs for maintenance branches, official tags. Never run for forked repos --- .github/workflows/docker.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 913a1f514a..00ec2fa8f7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,11 +1,21 @@ # DSpace Docker image build for hub.docker.com name: Docker images -# Run this Build for all pushes / PRs to current branch -on: [push, pull_request] +# Run this Build for all pushes to 'main' or maintenance branches, or tagged releases. +# Also run for PRs to ensure PR doesn't break Docker build process +on: + push: + branches: + - main + - 'dspace-**' + tags: + - 'dspace-**' + pull_request: jobs: docker: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular' + if: github.repository == 'dspace/dspace-angular' runs-on: ubuntu-latest env: # Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action) @@ -27,6 +37,7 @@ jobs: - name: Checkout codebase uses: actions/checkout@v2 + # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx uses: docker/setup-buildx-action@v1 From ed806dc3bfba0be104ef6329b8a9416c1e8ff4b8 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 8 Dec 2021 16:52:08 -0600 Subject: [PATCH 049/151] Minor license formatting cleanup to allow GitHub to recognize our BSD 3-Clause License. Copy over NOTICE file from DSpace/DSpace --- LICENSE | 20 ++++++-------------- NOTICE | 28 ++++++++++++++++++++++++++++ README.md | 6 +++++- 3 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 NOTICE diff --git a/LICENSE b/LICENSE index f55d21fe42..b381f6d968 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -DSpace source code BSD License: +BSD 3-Clause License Copyright (c) 2002-2021, LYRASIS. All rights reserved. @@ -13,13 +13,12 @@ notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name DuraSpace nor the name of the DSpace Foundation -nor the names of its contributors may be used to endorse or promote -products derived from this software without specific prior written -permission. +- Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, @@ -29,11 +28,4 @@ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - - -DSpace uses third-party libraries which may be distributed under -different licenses to the above. Information about these licenses -is detailed in the LICENSES_THIRD_PARTY file at the root of the source -tree. You must agree to the terms of these licenses, in addition to -the above DSpace source code license, in order to use this software. +DAMAGE. \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..44bbf95d2a --- /dev/null +++ b/NOTICE @@ -0,0 +1,28 @@ +Licenses of Third-Party Libraries +================================= + +DSpace uses third-party libraries which may be distributed under +different licenses than specified in our LICENSE file. Information +about these licenses is detailed in the LICENSES_THIRD_PARTY file at +the root of the source tree. You must agree to the terms of these +licenses, in addition to the DSpace source code license, in order to +use this software. + +Licensing Notices +================= + +[July 2019] DuraSpace joined with LYRASIS (another 501(c)3 organization) in July 2019. +LYRASIS holds the copyrights of DuraSpace. + +[July 2009] Fedora Commons joined with the DSpace Foundation and began operating under +the new name DuraSpace in July 2009. DuraSpace holds the copyrights of +the DSpace Foundation, Inc. + +[July 2007] The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007 +with a mission to promote and advance the dspace platform enabling management, +access and preservation of digital works. The Foundation was able to transfer +the legal copyright from Hewlett-Packard Company (HP) and Massachusetts +Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many +of the files in the source code may contain a copyright statement stating HP +and MIT possess the copyright, in these instances please note that the copy +right has transferred to the DSpace foundation, and subsequently to DuraSpace. \ No newline at end of file diff --git a/README.md b/README.md index 69b6132478..1143a79f31 100644 --- a/README.md +++ b/README.md @@ -449,4 +449,8 @@ DSpace uses GitHub to track issues: License ------- -This project's source code is made available under the DSpace BSD License: http://www.dspace.org/license +DSpace source code is freely available under a standard [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause). +The full license is available in the [LICENSE](LICENSE) file or online at http://www.dspace.org/license/ + +DSpace uses third-party libraries which may be distributed under different licenses. Those licenses are listed +in the [LICENSES_THIRD_PARTY](LICENSES_THIRD_PARTY) file. From 2bf880b21603879ba295ac48ca524968179b02b1 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 8 Dec 2021 19:31:05 -0600 Subject: [PATCH 050/151] 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 b64b7c2607630512d301e182afaf1f7d291148f9 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 9 Dec 2021 12:17:20 +0100 Subject: [PATCH 051/151] [CST-4882] Error fixed - Tests improved --- .../my-dspace-results.component.html | 2 +- .../my-dspace-results.component.spec.ts | 14 ++++++++++++-- .../my-dspace-results.component.ts | 8 ++++---- .../search-filter/search-filter.component.ts | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) 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 index ad06f97c88..67b13cc49c 100644 --- 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 @@ -10,5 +10,5 @@
- +

{{'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 index 29a2e593de..5b069f4ddb 100644 --- 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 @@ -40,9 +40,19 @@ describe('MyDSpaceResultsComponent', () => { expect(fixture.debugElement.query(By.css('a'))).toBeNull(); }); - it('should display error message if error is != 400', () => { - (comp as any).searchResults = { hasFailed: true, error: { statusCode: 500 } }; + 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(); }); 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 96395adea2..77f27e9d42 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 @@ -59,11 +59,11 @@ export class MyDSpaceResultsComponent { return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading; } - showError(searchResults: RemoteData>>): boolean { - return searchResults?.hasFailed && (!searchResults?.errorMessage || searchResults?.statusCode !== 400); + showError(): boolean { + return this.searchResults?.hasFailed && (!this.searchResults?.errorMessage || this.searchResults?.statusCode !== 400); } - errorMessageLabel(searchResults: RemoteData>>): string { - return (searchResults?.statusCode === 422) ? 'error.invalid-search-query' : 'error.search-results'; + errorMessageLabel(): string { + return (this.searchResults?.statusCode === 422) ? 'error.invalid-search-query' : 'error.search-results'; } } 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 a1b66eb332..8bc7502159 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 @@ -169,7 +169,7 @@ export class SearchFilterComponent implements OnInit { return this.searchService.getFacetValuesFor(this.filter, 1, options).pipe( filter((RD) => !RD.isLoading), map((valuesRD) => { - return valuesRD.payload.totalElements > 0; + return valuesRD.payload?.totalElements > 0; }),); } )); From d3b5e09e2ac39f1382ea28eb1eb5d848b7acf328 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 9 Dec 2021 17:48:59 +0100 Subject: [PATCH 052/151] [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 053/151] 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 64231683b3db55c70c23f59ee4b9fd36f19b440a Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 10 Dec 2021 18:28:30 +0100 Subject: [PATCH 054/151] [CST-4882] Error 422 is now handled in home page too --- src/app/search-page/search.component.ts | 10 +++++----- .../search-results/search-results.component.html | 6 +++--- .../search/search-results/search-results.component.ts | 8 ++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/app/search-page/search.component.ts b/src/app/search-page/search.component.ts index 8be21af552..2972175bdf 100644 --- a/src/app/search-page/search.component.ts +++ b/src/app/search-page/search.component.ts @@ -8,7 +8,7 @@ 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 { getFirstSucceededRemoteData } from '../core/shared/operators'; +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'; @@ -126,12 +126,12 @@ export class SearchComponent implements OnInit { this.searchOptions$ = this.getSearchOptions(); this.sub = this.searchOptions$.pipe( switchMap((options) => this.service.search( - options, undefined, true, true, followLink('thumbnail', { isOptional: true }) - ).pipe(getFirstSucceededRemoteData(), startWith(undefined)) + options, undefined, false, true, followLink('thumbnail', { isOptional: true }) + ).pipe(getFirstCompletedRemoteData(), startWith(undefined)) ) ).subscribe((results) => { - this.resultsRD$.next(results); - }); + this.resultsRD$.next(results); + }); if (isEmpty(this.configuration$)) { this.configuration$ = this.searchConfigService.getCurrentConfiguration('default'); diff --git a/src/app/shared/search/search-results/search-results.component.html b/src/app/shared/search/search-results/search-results.component.html index 4e6bca094e..23850f50dd 100644 --- a/src/app/shared/search/search-results/search-results.component.html +++ b/src/app/shared/search/search-results/search-results.component.html @@ -16,11 +16,11 @@ + *ngIf="showError()" + message="{{errorMessageLabel() | translate}}">
{{ 'search.results.no-results' | translate }} = new EventEmitter(); + 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'; + } + /** * Method to change the given string by surrounding it by quotes if not already present. */ From 4c27a117476bdc8c488187ee47ce6231d86c5c15 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Sat, 11 Dec 2021 01:59:38 +0100 Subject: [PATCH 055/151] [CST-4882] Tests improved --- .../search-results/search-results.component.spec.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/app/shared/search/search-results/search-results.component.spec.ts b/src/app/shared/search/search-results/search-results.component.spec.ts index f52cdfa1b0..4cc4f84f65 100644 --- a/src/app/shared/search/search-results/search-results.component.spec.ts +++ b/src/app/shared/search/search-results/search-results.component.spec.ts @@ -45,9 +45,19 @@ describe('SearchResultsComponent', () => { expect(fixture.debugElement.query(By.css('a'))).toBeNull(); }); - it('should display error message if error is != 400', () => { + it('should display error message if error is 500', () => { (comp as any).searchResults = createFailedRemoteDataObject('Error', 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 = createFailedRemoteDataObject('Error', 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(); }); From e2614b9dadcd17ce84b809518c783dda3a6a75dc Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Mon, 13 Dec 2021 13:48:09 -0500 Subject: [PATCH 056/151] 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 5acc29e49856608c3ffa0accdb8ed89ed2aca80b Mon Sep 17 00:00:00 2001 From: Michael Spalti Date: Mon, 13 Dec 2021 10:51:16 -0800 Subject: [PATCH 057/151] Updated Mirador dependencies. --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 236845da1a..28ffd3d970 100644 --- a/package.json +++ b/package.json @@ -110,9 +110,9 @@ "jwt-decode": "^3.1.2", "klaro": "^0.7.10", "lodash": "^4.17.21", - "mirador": "^3.0.0", + "mirador": "^3.3.0", "mirador-dl-plugin": "^0.13.0", - "mirador-share-plugin": "^0.10.0", + "mirador-share-plugin": "^0.11.0", "moment": "^2.29.1", "morgan": "^1.10.0", "ng-mocks": "11.11.2", @@ -125,8 +125,6 @@ "nouislider": "^14.6.3", "pem": "1.14.4", "postcss-cli": "^8.3.0", - "react": "^16.14.0", - "react-dom": "^16.14.0", "reflect-metadata": "^0.1.13", "rxjs": "^6.6.3", "sortablejs": "1.13.0", @@ -189,6 +187,8 @@ "protractor": "^7.0.0", "protractor-istanbul-plugin": "2.0.0", "raw-loader": "0.5.1", + "react": "^16.14.0", + "react-dom": "^16.14.0", "rimraf": "^3.0.2", "rxjs-spy": "^7.5.3", "sass-resources-loader": "^2.1.1", @@ -204,4 +204,4 @@ "webpack-cli": "^4.2.0", "webpack-dev-server": "^4.5.0" } -} \ No newline at end of file +} From b1c3967a5b70c3f6d0b4691557012ece55ee3a6a Mon Sep 17 00:00:00 2001 From: Michael Spalti Date: Mon, 13 Dec 2021 11:32:37 -0800 Subject: [PATCH 058/151] Added lockfile. --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index fdb758af01..f467fb9840 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9121,12 +9121,12 @@ mirador-dl-plugin@^0.13.0: resolved "https://registry.yarnpkg.com/mirador-dl-plugin/-/mirador-dl-plugin-0.13.0.tgz#9a6cb0fa3c566a2a1ebe1ad9caa1ff590ff22689" integrity sha512-I/6etIvpTtO1zgjxx2uEUFoyB9NxQ43JWg8CMkKmZqblW7AAeFqRn1/zUlQH7N8KFZft9Rah6D8qxtuNAo9jmA== -mirador-share-plugin@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/mirador-share-plugin/-/mirador-share-plugin-0.10.0.tgz#82cde27faedc440fab648db137e62849d6542420" - integrity sha512-hC9hG0H04WAR6JNfLDnQICtxwWV3K+cmqnArtOvAIGGnbgXWs5tmQyfdY55z05jzbeL40rd7z1K094hHV3R4WQ== +mirador-share-plugin@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/mirador-share-plugin/-/mirador-share-plugin-0.11.0.tgz#13e2f654e38839044382acad42d9329e91a8cd5e" + integrity sha512-fHcdDXyrtfy5pn1zdQNX9BvE5Tjup66eQwyNippE5PMaP8ImUcrFaSL+mStdn+v6agsHcsdRqLhseZ0XWgEuAw== -mirador@^3.0.0: +mirador@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/mirador/-/mirador-3.3.0.tgz#7a957a1db1a5388b2b8cafab00db4eb9f97557b9" integrity sha512-BmGfRnWJ45B+vtiAwcFT7n9nKialfejE9UvuUK0NorO37ShArpsKr3yVSD4jQASwSR4DRRpPEG21jOk4WN7H3w== From 13cfd7168612fd0e78841e9891a6fbecf3799be4 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 13 Dec 2021 16:41:34 -0600 Subject: [PATCH 059/151] Update Docker to use latest Solr 8 --- docker/docker-compose-ci.yml | 2 +- docker/docker-compose-rest.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml index d2d02f0a55..18fa152c9d 100644 --- a/docker/docker-compose-ci.yml +++ b/docker/docker-compose-ci.yml @@ -64,7 +64,7 @@ services: dspacesolr: container_name: dspacesolr # Uses official Solr image at https://hub.docker.com/_/solr/ - image: solr:8.8 + image: solr:8.11-slim # Needs main 'dspace' container to start first to guarantee access to solr_configs depends_on: - dspace diff --git a/docker/docker-compose-rest.yml b/docker/docker-compose-rest.yml index 370ccbbdf1..3534682afc 100644 --- a/docker/docker-compose-rest.yml +++ b/docker/docker-compose-rest.yml @@ -62,7 +62,7 @@ services: dspacesolr: container_name: dspacesolr # Uses official Solr image at https://hub.docker.com/_/solr/ - image: solr:8.8 + image: solr:8.11-slim # Needs main 'dspace' container to start first to guarantee access to solr_configs depends_on: - dspace From b6904a4df90e9bb4e86ffc3bcadb20f34c531ae5 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 12:40:51 +0100 Subject: [PATCH 060/151] [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 061/151] [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 062/151] [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 34b73cc4f7dc9f223f93e92a6bf4ed29cfd3dddb Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 11:55:33 +0100 Subject: [PATCH 063/151] [DSC-370] Fix SSR issue with submission after migrate to angular 11 --- src/app/submission/submission.module.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 13cf2016dd..2c04c47290 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -65,6 +65,14 @@ const DECLARATIONS = [ SubmissionImportExternalCollectionComponent ]; +const ENTRY_COMPONENTS = [ + SubmissionSectionUploadComponent, + SubmissionSectionformComponent, + SubmissionSectionLicenseComponent, + SubmissionSectionCcLicensesComponent, + SubmissionSectionDetectDuplicateComponent +]; + @NgModule({ imports: [ CommonModule, @@ -88,4 +96,14 @@ const DECLARATIONS = [ * This module handles all components that are necessary for the submission process */ export class SubmissionModule { + /** + * NOTE: this method allows to resolve issue with components that using a custom decorator + * which are not loaded during CSR otherwise + */ + static withEntryComponents() { + return { + ngModule: SubmissionModule, + providers: ENTRY_COMPONENTS.map((component) => ({provide: component})) + }; + } } From 662f846caad37a32bc91dc67e15a3d151b644635 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 15:37:44 +0100 Subject: [PATCH 064/151] [CST-5033] fix issue with cherry-pick --- src/app/submission/submission.module.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 2c04c47290..fccd41fd14 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -69,8 +69,7 @@ const ENTRY_COMPONENTS = [ SubmissionSectionUploadComponent, SubmissionSectionformComponent, SubmissionSectionLicenseComponent, - SubmissionSectionCcLicensesComponent, - SubmissionSectionDetectDuplicateComponent + SubmissionSectionCcLicensesComponent ]; @NgModule({ From a952438247018e495d356c5161dc7bbb6bf443dc Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 15:44:43 +0100 Subject: [PATCH 065/151] [CST-5033] fix comments --- src/app/admin/admin-search-page/admin-search.module.ts | 2 +- src/app/admin/admin-workflow-page/admin-workflow.module.ts | 2 +- src/app/admin/admin.module.ts | 2 +- src/app/browse-by/browse-by.module.ts | 2 +- .../entity-groups/journal-entities/journal-entities.module.ts | 2 +- .../research-entities/research-entities.module.ts | 2 +- src/app/item-page/item-page.module.ts | 4 ++-- src/app/my-dspace-page/my-dspace-search.module.ts | 2 +- src/app/navbar/navbar.module.ts | 2 +- src/app/shared/shared.module.ts | 2 +- src/app/submission/submission.module.ts | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) 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 0b3b7df9bb..ecf8d8a2d2 100644 --- a/src/app/admin/admin-search-page/admin-search.module.ts +++ b/src/app/admin/admin-search-page/admin-search.module.ts @@ -36,7 +36,7 @@ const ENTRY_COMPONENTS = [ export class AdminSearchModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { 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 4715ae16f4..02e5e01023 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow.module.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow.module.ts @@ -28,7 +28,7 @@ const ENTRY_COMPONENTS = [ export class AdminWorkflowModuleModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts index 25cdd67dcf..6c756dd851 100644 --- a/src/app/admin/admin.module.ts +++ b/src/app/admin/admin.module.ts @@ -34,7 +34,7 @@ const ENTRY_COMPONENTS = [ export class AdminModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { diff --git a/src/app/browse-by/browse-by.module.ts b/src/app/browse-by/browse-by.module.ts index 2d3618aae6..cd44e52b81 100644 --- a/src/app/browse-by/browse-by.module.ts +++ b/src/app/browse-by/browse-by.module.ts @@ -31,7 +31,7 @@ const ENTRY_COMPONENTS = [ export class BrowseByModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { 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 e23a729d6a..dc88490eac 100644 --- a/src/app/entity-groups/journal-entities/journal-entities.module.ts +++ b/src/app/entity-groups/journal-entities/journal-entities.module.ts @@ -54,7 +54,7 @@ const ENTRY_COMPONENTS = [ export class JournalEntitiesModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { 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 2e91a6f1e7..c9d6c2d5b6 100644 --- a/src/app/entity-groups/research-entities/research-entities.module.ts +++ b/src/app/entity-groups/research-entities/research-entities.module.ts @@ -74,7 +74,7 @@ const COMPONENTS = [ export class ResearchEntitiesModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts index 2e3e248692..80cb1f61a2 100644 --- a/src/app/item-page/item-page.module.ts +++ b/src/app/item-page/item-page.module.ts @@ -33,7 +33,7 @@ import { NgxGalleryModule } from '@kolkov/ngx-gallery'; import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component'; import { VersionPageComponent } from './version-page/version-page/version-page.component'; import { VersionedItemComponent } from './simple/item-types/versioned-item/versioned-item.component'; -import { ThemedFileSectionComponent} from './simple/field-components/file-section/themed-file-section.component'; +import { ThemedFileSectionComponent } from './simple/field-components/file-section/themed-file-section.component'; const ENTRY_COMPONENTS = [ @@ -91,7 +91,7 @@ const DECLARATIONS = [ export class ItemPageModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { diff --git a/src/app/my-dspace-page/my-dspace-search.module.ts b/src/app/my-dspace-page/my-dspace-search.module.ts index a97f2207e7..67a4f0818e 100644 --- a/src/app/my-dspace-page/my-dspace-search.module.ts +++ b/src/app/my-dspace-page/my-dspace-search.module.ts @@ -50,7 +50,7 @@ const ENTRY_COMPONENTS = [ export class MyDspaceSearchModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { diff --git a/src/app/navbar/navbar.module.ts b/src/app/navbar/navbar.module.ts index 6591cd3453..c84e732fd5 100644 --- a/src/app/navbar/navbar.module.ts +++ b/src/app/navbar/navbar.module.ts @@ -58,7 +58,7 @@ const ENTRY_COMPONENTS = [ export class NavbarModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index abc42cb08a..2b64260f46 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -623,7 +623,7 @@ const DIRECTIVES = [ export class SharedModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index fccd41fd14..e8c96284c0 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -97,7 +97,7 @@ const ENTRY_COMPONENTS = [ export class SubmissionModule { /** * NOTE: this method allows to resolve issue with components that using a custom decorator - * which are not loaded during CSR otherwise + * which are not loaded during SSR otherwise */ static withEntryComponents() { return { From d426f5f17985c579e08177a998434a503a455bfd Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 14 Dec 2021 15:47:30 +0100 Subject: [PATCH 066/151] [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 f672e12433dd3118331ab5ed197a3b281c61d8e0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 14 Dec 2021 09:40:00 -0600 Subject: [PATCH 067/151] Remove unnecessary relativeLinkResolution: 'legacy' --- src/app/app-routing.module.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 26d9a5dc0d..04d2c55bdd 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -203,7 +203,6 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu ]} ],{ onSameUrlNavigation: 'reload', - relativeLinkResolution: 'legacy' }) ], exports: [RouterModule], From 0afa7c5bab0249400aaee424338c32692880a574 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 14 Dec 2021 18:01:34 -0600 Subject: [PATCH 068/151] 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 069/151] 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 070/151] 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 071/151] 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 072/151] 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 073/151] 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 074/151] 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 075/151] 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 076/151] 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 077/151] 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 32ae686e36030f4ca9a6ced4a487ab39b24abadb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 15 Dec 2021 14:24:23 -0600 Subject: [PATCH 078/151] Syntax fix, add trailing quote --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 268d8792c8..bb719f202e 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -2182,7 +2182,7 @@ "item.edit.bitstreams.headers.name": "Name", // "item.edit.bitstreams.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button", - "item.edit.bitstreams.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um Ihre Änderungen wiederherzustellen, klicken Sie auf die Schaltfläche 'Rückgängig', + "item.edit.bitstreams.notifications.discarded.content": "Ihre Änderungen wurden verworfen. Um Ihre Änderungen wiederherzustellen, klicken Sie auf die Schaltfläche 'Rückgängig'", // "item.edit.bitstreams.notifications.discarded.title": "Changes discarded", "item.edit.bitstreams.notifications.discarded.title": "Änderungen verworfen", From a4d91c37a7fc9e9d734bbd62864023628b26661c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 15 Dec 2021 22:38:08 +0100 Subject: [PATCH 079/151] [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 080/151] 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 081/151] [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 9801ae341409fd0024ccf774f2167f03901db4f3 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Thu, 16 Dec 2021 10:56:29 +0100 Subject: [PATCH 082/151] update legacy relative links to angular 11 format --- .../shared/resource-policies/resource-policies.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/resource-policies/resource-policies.component.ts b/src/app/shared/resource-policies/resource-policies.component.ts index 30bd7ce45b..94c928438a 100644 --- a/src/app/shared/resource-policies/resource-policies.component.ts +++ b/src/app/shared/resource-policies/resource-policies.component.ts @@ -281,7 +281,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy { * Redirect to resource policy creation page */ redirectToResourcePolicyCreatePage(): void { - this.router.navigate([`../create`], { + this.router.navigate([`./create`], { relativeTo: this.route, queryParams: { policyTargetId: this.resourceUUID, @@ -296,7 +296,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy { * @param policy The resource policy */ redirectToResourcePolicyEditPage(policy: ResourcePolicy): void { - this.router.navigate([`../edit`], { + this.router.navigate([`./edit`], { relativeTo: this.route, queryParams: { policyId: policy.id From e7f0921e6ed52ec25d60f70c61d19cddcfd2a656 Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 14 Dec 2021 09:30:38 +0100 Subject: [PATCH 083/151] 85862: Handling very long strings for metadata/files on item page, edit metadata/bitstreams page --- .../item-edit-bitstream.component.html | 6 ++++-- .../edit-in-place-field.component.html | 2 +- .../metadata-uri-values.component.html | 2 +- .../metadata-values.component.html | 2 +- .../file-download-link.component.html | 2 +- src/styles/_global-styles.scss | 18 ++++++++++++++++++ 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.html b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.html index 62014f06bd..5794288018 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.html +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.html @@ -1,8 +1,10 @@
-
- {{ bitstreamName }} +
+ + {{ bitstreamName }} +
diff --git a/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html b/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html index fe46906f47..f5543af971 100644 --- a/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html +++ b/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html @@ -26,7 +26,7 @@
- {{metadata?.value}} + {{metadata?.value}}